)
}
================================================
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) => (
))
}
}
export default class Document extends NextDocument {
static async getInitialProps(ctx) {
const initialProps = await NextDocument.getInitialProps(ctx)
return { ...initialProps }
}
render() {
return (
)
}
}
================================================
FILE: src/pages/building-react-and-vue-support-for-tailwind-ui/index.mdx
================================================
import { adamwathan } from '@/authors'
import stackedListExample from './stacked-list-example.png'
export const meta = {
private: true,
title: 'Building React + Vue support for Tailwind UI',
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.`,
date: '2021-04-12T19:45:00.0Z',
authors: [adamwathan],
}
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.
Grab some popcorn...
## The Backstory
From 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.
We 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.
We 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?)_
So 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.
But 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.
## What should it even be?
As 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.
I 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:
1. **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.
2. **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.
3. **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.
The 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.
To 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.
So before we could add JavaScript support to Tailwind UI, we needed to build some tools.
## Building Headless UI
Years 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"_.
This 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.
We 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.
There were two big reasons we ended up starting our own project:
1. **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.
2. **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.
I 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.
### Getting the ball rolling
We 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.
The 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 [``](https://v3.vuejs.org/guide/transitions-enterleave.html#enter-leave-transitions) component in Vue:
```jsx
I will fade in and out
```
This 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!
We 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:
- Menu Button (or dropdown)
- Listbox (or custom select)
- Switch (or toggle)
We 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).
Here's what a custom dropdown looks like in React:
```jsx
import { Menu } from '@headlessui/react'
function MyDropdown() {
return (
)
}
```
You'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):
```jsx
{({ active }) => (
Documentation
)}
```
Render 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.
### Designing the right components
After 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.
When 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.
This 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.
Some are obvious:
- A modal dialog should be a [dialog](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/dialog_role)
- A toggle should be a [switch](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/Switch_role)
- A dropdown should be a [menu](https://www.w3.org/TR/wai-aria-practices/examples/menu-button/menu-button-links.html) _(well, sometimes...)_
But some are a lot trickier. For example, what about mobile menus, the kind of thing you open with a hamburger button?
_If it opens kinda like a popup, is that a menu like a dropdown?_
_What if it slides in from the side of the screen?_
_What if it just opens in place and pushes the rest of the page further down?_
We 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.
Here's what we decided we needed in order to support the patterns that already existed in Tailwind UI:
- **Menu Button**. Used for dropdown menus that only contain links or buttons, like a little actions menu at the end of a table row.
- **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.
- **Switch**. For custom toggle switches that behave like checkboxes.
- **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.
- **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.
- **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.
- **Radio Group**. For custom radio selection UIs, like where you want a set of clickable cards instead of a boring little radio circle.
We ran into tons of challenges building this stuff, especially around complex stuff like focus management, and _especially_ around nested focus management.
Imaging 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.
I 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!
We _(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.
We 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.
We think you're gonna love it. _</TimCook>_
## Just enough abstraction
With 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.
The 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.
This 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.
The biggest question was how hard should we try to remove any duplication, and what are the right approaches to doing so?
Both 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.
The challenge is that creating components like that is always _very_ project specific. Take this list component for example:
Fully componentized in a real app, the final code might look something like this:
```jsx
{projectMembers.map(member => (
))}
```
It looks super clean sure, but it's forcing a lot of opinions on you.
For 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!
It 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.
The 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.
To 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.
I wrestled with it for a long time, but ultimately decided that right now we were trying to solve two main problems:
1. **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`.
2. **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.
I 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.
So this is what's different when you look at a React or Vue example compared to the vanilla HTML version:
1. **Each framework example uses the right syntax** — React examples use JSX, and Vue examples are provided in the single-file component syntax.
2. **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.
3. **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.
4. **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.
5. **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.
Here's an example of what it actually looks like:
```jsx
import { Menu, Transition } from '@headlessui/react'
import { DotsVerticalIcon } from '@heroicons/react/solid'
import { Fragment } from 'react'
const people = [
{
name: 'Calvin Hawkins',
email: 'calvin.hawkins@example.com',
image:
'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',
},
{
name: 'Kristen Ramos',
email: 'kristen.ramos@example.com',
image:
'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',
},
{
name: 'Ted Fox',
email: 'ted.fox@example.com',
image:
'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',
},
]
export default function Example() {
return (
{people.map((person) => (
{person.name}
{person.email}
))}
)
}
```
It'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.
## The machine that makes it work
So 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.
Tailwind 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.
If 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.
So 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.
For 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.
Here'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).
Here's what one of our dropdown examples looks like in our internal authoring format:
```html
```
You 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.
We crawl this document as an AST, and actually transform it into _four_ formats:
1. The vanilla HTML you get when you copy the snippet.
2. 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.
3. The React snippet for you to copy.
4. The Vue snippet for you to copy.
The 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.
There'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.
## When's it coming out?
React 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.
We'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.
Thanks 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.
Hope you enjoy the stuff!
– Adam
================================================
FILE: src/pages/building-the-tailwind-blog/index.mdx
================================================
import { adamwathan } from '@/authors'
import image from './card.jpg'
export const meta = {
title: 'Building the Tailwind Blog with Next.js',
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!`,
date: '2020-06-30T18:05:31Z',
authors: [adamwathan],
image,
discussion: 'https://github.com/tailwindcss/tailwindcss/discussions/1987',
}
One 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.
The problem was that up until today, we didn't actually have anywhere to publish those posts!
## Choosing a platform
We'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).
There 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.
```md
# My first MDX post
MDX is a really cool authoring format because it lets
you embed React components right in your markdown:
How cool is that?
```
MDX 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.
We'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.
## Organizing our content
We 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.
Having 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.
```
public/
src/
├── components/
├── css/
├── img/
└── pages/
├── building-the-tailwindcss-blog/
│ ├── index.mdx
│ └── card.jpeg
├── introducing-linting-for-tailwindcss-intellisense/
│ ├── index.mdx
│ ├── css.png
│ ├── html.png
│ └── card.jpeg
├── _app.js
├── _document.js
└── index.js
next.config.js
package.json
postcss.config.js
README.md
tailwind.config.js
```
This 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.
## Metadata
We store metadata about each post in a `meta` object that we export at the top of each MDX file:
```js
import { bradlc } from '@/authors'
import openGraphImage from './card.jpeg'
export const meta = {
title: 'Introducing linting for Tailwind CSS IntelliSense',
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.`,
date: '2020-06-23T18:52:03Z',
authors: [bradlc],
image: openGraphImage,
discussion: 'https://github.com/tailwindcss/tailwindcss/discussions/1956',
}
// Post content goes here
```
This 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.
We store all of our authors data in a separate file that just contains each team member's name, Twitter handle, and avatar.
```js
import adamwathanAvatar from './img/adamwathan.jpg'
import bradlcAvatar from './img/bradlc.jpg'
import steveschogerAvatar from './img/steveschoger.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,
}
```
The 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:
```js
export const meta = {
title: 'An example of a guest post by someone not on the team',
authors: [
{
name: 'Simon Vrachliotis',
twitter: '@simonswiss',
avatar: 'https://pbs.twimg.com/profile_images/1160929863/n510426211_274341_6220_400x400.jpg',
},
],
// ...
}
```
This 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.
## Displaying post previews
We wanted to display previews for each post on the homepage, and this turned out to be a surprisingly challenging problem.
Essentially 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.
The 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.
We 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:
1. The entire post, used for post pages.
2. The post preview, where we load the minimum data needed for the homepage.
The 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.
Here's a snippet of what that custom loader looks like:
```js
{
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('', ''))
}),
],
},
```
It lets us define the excerpt for each post either by sticking `` after the intro paragraph, or by wrapping the excerpt in a pair of `` and `` tags, allowing us to write an excerpt that's completely independent from the post content.
```
export const meta = {
// ...
}
This is the beginning of the post, and what we'd like to
show on the homepage.
Anything after that is not included in the bundle unless
you are actually viewing that post.
```
Solving 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.
## Generating next/previous post links
The 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.
At 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.
This 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.
We 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:
```js
{
use: [
...mdx,
createLoader(function (src) {
const content = [
'import Post from "@/components/Post"',
'export { getStaticProps } from "@/getStaticProps"',
src,
'export default (props) => ',
].join('\n')
if (content.includes('')) {
return this.callback(null, content.split('').join('\n'))
}
return this.callback(null, content.replace(/.*/s, ''))
}),
],
}
```
We 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.
This 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.
We 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:
```js
import getAllPostPreviews from '@/getAllPostPreviews'
export async function getStaticProps() {
return {
props: {
posts: getAllPostPreviews().map((post) => ({
title: post.module.meta.title,
link: post.link.substr(1),
})),
},
}
}
```
Then in our actual `Post` layout component, we use the current route to determine the next and previous posts:
```js
export default function Post({ meta, children, posts }) {
const router = useRouter()
const postIndex = posts.findIndex((post) => post.link === router.pathname)
const previous = posts[postIndex + 1]
const next = posts[postIndex - 1]
// ...
}
```
This 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.
There'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.
## Wrapping up
Overall, 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.
If 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).
================================================
FILE: src/pages/designing-tailwind-ui-ecommerce/index.mdx
================================================
import { adamwathan } from '@/authors'
import card from './card.jpg'
export const meta = {
private: true,
title: 'Designing Tailwind UI Ecommerce',
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.`,
date: '2021-08-07T12:45:00.0Z',
authors: [adamwathan],
image: card,
}
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.
Designing 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._
We'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.
---
**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:
- Product overviews
- Product lists
- Product feature details
- Category previews
- Checkout forms
- Shopping carts
- Category filters
- Customer reviews
- Order history
- Order details
- Category mega menus
- Product quickviews
- Promo sections
We 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.
Some 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.
For example, this section isn't really that different from a hero section you might see on a marketing site:

So should the ecommerce kit have "Hero Sections" too, like the marketing kit? And if so what makes them different — why have two duplicate categories?
It 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".
We 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.
There 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.
---
**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.
For 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.
Sometimes 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.
So 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.

We'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.
---
**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.
What 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.
Here's an example of a design that changed a _lot_ during the building process — Figma on the left, final HTML version on the right:

Definitely 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.
If 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.
Here's what it looks like:

You 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!
---
**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.

The 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.
Generally 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.
The way I like to think about it is we're trying to give you pre-built LEGO creations, not just the bricks.

It'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.
For example, this single "Product Overview" section could be cut up into a bunch of smaller components:

There'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.
We 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.
---
**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.
For 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.
We 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.

We 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:

We'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.)_
---
**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.
As 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.
Thanks as always for following along — look for another update early next week, and fingers crossed we'll have this ready for release shortly after!
– Adam
================================================
FILE: src/pages/from-900-to-1-how-we-hired-robin-malfait/index.mdx
================================================
import { adamwathan } from '@/authors'
import cardImage from './card.jpg'
import robinImage from './robin.jpg'
export const meta = {
title: 'From Nine Hundred to One: How We Hired Robin Malfait',
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.`,
date: '2020-08-10T13:30:00.000Z',
authors: [adamwathan],
image: cardImage,
discussion: 'https://github.com/tailwindlabs/tailwindcss/discussions/2153',
}
Back 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.
After 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!
Robin 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!
Robin 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.
We'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!
What 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.
---
## The Job Posting
Before 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.
I 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:
[**Read the job posting →**](https://jobs.tailwindui.com/full-stack-developer)
Here are the important things I focused on when writing it:
- Be specific about the projects the applicant would be working on after they started
- Be clear that we are a small team, so everyone has to do a bit of everything, including customer support
- Give concrete examples of projects we just finished that the applicant would have worked on if they were already at the company
- 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
- 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?
We 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.
## The Application Process
One 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.
I decided to ask for applications this way for a few reasons:
- I just don't think resumes are that important
- 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
- 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
- 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
- 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
Even 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:
[**Read Robin's application →**](https://robin-malfait-tailwind-job-application.now.sh/)
Some 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:
- The visual design was great. We're a very design-focused company, so having good taste in design is really important to us.
- 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.
- 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.
- 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.
- 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.
- He capitalized the "H" in "GitHub" _(holy shit I hate when people don't do that)_.
Robin's was one of maybe 40-50 that _really_ stood out from a content perspective.
## Filtering the Applications
Dealing 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.
I'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.
I 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.
## The Interview Process
We started by scheduling video interviews with the top ~20 applicants, which took about 3 weeks to get through.
These were 30-45 minute calls where we had a pretty casual conversation about a few topics:
- What the person had been working on recently, and where they think their strengths are
- Why they applied for the job, and what about the role was interesting to them
- What we as a company are going to be doing over the next year or so, and digging into a few projects in detail
- Answering any questions the person had about the job or our company
This 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.
## Take-Home Project
The 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.
We 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.
[**See the take-home project on GitHub →**](https://github.com/adamwathan/tailwind-take-home-project)
We 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.
We 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.
All 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.
One 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.
## Pairing Session
The final step in the application process was a two-hour pair programming session with me.
When 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:
- I made sure whatever we were pairing on was something completely new, that I had no prior experience with
- I let the candidate suggest a few things for us to pair on, and picked something from their list
I 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.
Instead, 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.
Some of the things I paired on included:
- Building a date picker from scratch
- Learning XState
- Building a modal dialog with the Vue 3 composition API
I 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.
For 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.
## Making the offer
This 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.
================================================
FILE: src/pages/headless-ui-unstyled-accessible-ui-components/index.mdx
================================================
import { adamwathan } from '@/authors'
import image from './card.jpg'
import headlessUiCard from './headless-ui-card.svg'
export const meta = {
title: `Headless UI: Unstyled, Accessible UI Components`,
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.`,
date: '2020-10-06T18:30:00.000Z',
authors: [adamwathan],
image,
discussion: 'https://github.com/tailwindlabs/tailwindcss/discussions/2508',
}
One 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_.
You 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.
The 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.
We think there's a better option, so we're building it.
**[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.
Here'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:
```jsx
import { Menu } from '@headlessui/react'
function MyDropdown() {
return (
)
}
```
Here's what you're getting for free in that example, without having to write a single line of code related to it yourself:
- The dropdown panel opens on click, spacebar, enter, or when using the arrow keys
- The dropdown closes when you press escape, or click outside of it
- You can navigate the items using the up and down arrow keys
- You can jump the first item using the `Home` key, and the last item using the `End` key
- Disabled items are automatically skipped when navigating with the keyboard
- Hovering over an item with your mouse after navigating with the keyboard will switch to mouse position based focusing
- Items are announced properly to screen readers while navigating them with the keyboard
- The dropdown button is properly announced to screenreaders as controlling a menu
- ...and probably tons more that I'm forgetting.
All 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!
There 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?
Here's a fully-styled live demo _(taken from [Tailwind UI](https://tailwindui.com))_ so you can see it in action:
Make sure to try it with the keyboard or a screen reader to really appreciate it!
We just tagged v0.2.0, which currently includes the following components:
- [Menu Button](https://codesandbox.io/s/headlessuivue-menu-example-70br3?file=/src/App.vue) (or dropdown)
- [Listbox](https://codesandbox.io/s/headlessuivue-listbox-example-mi67g?file=/src/App.vue) (or custom select)
- [Switch](https://codesandbox.io/s/headlessuivue-switch-example-8ycp6?file=/src/App.vue) (or toggle)
- ...with many more on the way.
To learn more and dive in, [**head over to the Headless UI website**](https://headlessui.dev) and read the documentation.
---
If 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.
Earlier 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.
The 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.
We 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.
## Why reinvent the wheel?
We'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.
We decided to try our own take on the problem for a few reasons:
- 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.
- 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.
- 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.
- We want to make sure it is always super easy to style these components with Tailwind, rather than having to write custom CSS.
We 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.
## What's next
We've got quite a few more components to develop for Headless UI, including:
- Modal
- Radio group
- Tabs
- Accordion
- Combobox
- Datepicker
...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.
After 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.
If you'd like to keep up with what we're doing, be sure to [follow the project on GitHub](https://github.com/tailwindlabs/headlessui).
================================================
FILE: src/pages/headless-ui-v1/.prettierrc
================================================
{
"printWidth": 80,
"singleQuote": true,
"semi": false
}
================================================
FILE: src/pages/headless-ui-v1/index.mdx
================================================
import { adamwathan } from '@/authors'
import card from './card.jpg'
export const meta = {
title: 'Headless UI v1.0',
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.`,
date: '2021-04-14T16:00:00.000Z',
authors: [adamwathan],
image: card,
footer: `
`,
}
Last 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.
Today 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.
## What’s new
We’ve added four new components to the React library, and five new components for Vue.
### Dialog (modal)
Headless 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.
```jsx
import { useState } from 'react'
import { Dialog } from '@headlessui/react'
function MyDialog() {
let [isOpen, setIsOpen] = useState(true)
return (
)
}
```
### Disclosure
We’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.
```html
Is team pricing available?
Yes! You can purchase a license that you can share with your entire team.
```
### Radio Group
There’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.
```jsx
import { useState } from 'react'
import { RadioGroup } from '@headlessui/react'
function MyRadioGroup() {
let [plan, setPlan] = useState('startup')
return (
Plan
{({ checked }) => (
Startup
)}
{({ checked }) => (
Business
)}
{({ checked }) => (
Enterprise
)}
)
}
```
### Popover
The 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.
```html
Solutions
```
### TransitionRoot and TransitionChild (for Vue)
Headless UI already had a `Transition` component for React, but we’ve always recommended the native `` 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.
Headless UI v1.0 brings our React `Transition` component to Vue as well, which makes it a lot easier to transition things like modal dialogs.
```html
```
## Try it out
Head 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).
================================================
FILE: src/pages/headless-ui-v1-4/.prettierrc
================================================
{
"printWidth": 80,
"singleQuote": true,
"semi": false
}
================================================
FILE: src/pages/headless-ui-v1-4/index.mdx
================================================
import { adamwathan, robinmalfait } from '@/authors'
import card from './card.jpg'
import banner from './banner.jpg'
import ecommerce from './ecommerce-screenie.jpg'
import { Tab } from '@headlessui/react'
import ReactExample1 from './snippets/react-1.mdx'
import ReactExample2 from './snippets/react-2.mdx'
import ReactExample3 from './snippets/react-3.mdx'
import ReactExample4 from './snippets/react-4.mdx'
import VueExample1 from './snippets/vue-1.mdx'
import VueExample2 from './snippets/vue-2.mdx'
import VueExample3 from './snippets/vue-3.mdx'
import VueExample4 from './snippets/vue-4.mdx'
export const meta = {
title: 'Headless UI v1.4: The One With Tabs',
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.`,
date: '2021-07-29T12:00:00.000Z',
authors: [adamwathan, robinmalfait],
image: card,
footer: `
`,
}
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.
## Tabs
Earlier 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.
Here's what we ended up with:
`py-2 px-4 inline-block rounded-md ${
selected ? 'bg-gray-700' : 'bg-transparent hover:bg-gray-700'
} 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`
}
>
React
`py-2 px-4 inline-block rounded-md ${
selected ? 'bg-gray-700' : 'bg-transparent hover:bg-gray-700'
} 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`
}
>
Vue
And yep, those are tabs!
Like 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.
[Check out the documentation](https://headlessui.dev/react/tabs) to learn more.
### Closing Disclosures and Popovers
Up 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.
Now 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:
`py-2 px-4 inline-block rounded-md ${
selected ? 'bg-gray-700' : 'bg-transparent hover:bg-gray-700'
} 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`
}
>
React
`py-2 px-4 inline-block rounded-md ${
selected ? 'bg-gray-700' : 'bg-transparent hover:bg-gray-700'
} 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`
}
>
Vue
The same thing works with `Popover` components, too:
`py-2 px-4 inline-block rounded-md ${
selected ? 'bg-gray-700' : 'bg-transparent hover:bg-gray-700'
} 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`
}
>
React
`py-2 px-4 inline-block rounded-md ${
selected ? 'bg-gray-700' : 'bg-transparent hover:bg-gray-700'
} 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`
}
>
Vue
If 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:
`py-2 px-4 inline-block rounded-md ${
selected ? 'bg-gray-700' : 'bg-transparent hover:bg-gray-700'
} 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`
}
>
React
`py-2 px-4 inline-block rounded-md ${
selected ? 'bg-gray-700' : 'bg-transparent hover:bg-gray-700'
} 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`
}
>
Vue
For 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.
## Try it out
Headless UI v1.4 is a minor update so there are no breaking changes. To upgrade, just install the latest version via npm:
```shell
# For React
npm install @headlessui/react
# For Vue
npm install @headlessui/vue
```
Check 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.
================================================
FILE: src/pages/headless-ui-v1-4/snippets/react-1.mdx
================================================
```jsx
import { Tab } from '@headlessui/react'
function MyTabs() {
return (
Tab 1Tab 2Tab 3Content 1Content 2Content 3
)
}
```
================================================
FILE: src/pages/headless-ui-v1-4/snippets/react-2.mdx
================================================
```jsx
import { Disclosure } from '@headlessui/react'
import MyLink from './MyLink'
function MyDisclosure() {
return (
Open mobile menu
Home
{/* ... */}
)
}
```
================================================
FILE: src/pages/headless-ui-v1-4/snippets/react-3.mdx
================================================
```jsx
import { Popover } from '@headlessui/react'
import MyLink from './MyLink'
function MyPopover() {
return (
Solutions
Insights
{/* ... */}
)
}
```
================================================
FILE: src/pages/headless-ui-v1-4/snippets/react-4.mdx
================================================
```jsx
import { Popover } from '@headlessui/react'
function MyPopover() {
return (
Terms
{({ close }) => (
)}
)
}
```
================================================
FILE: src/pages/headless-ui-v1-4/snippets/vue-1.mdx
================================================
```html
Tab 1Tab 2Tab 3Content 1Content 2Content 3
```
================================================
FILE: src/pages/headless-ui-v1-4/snippets/vue-2.mdx
================================================
```html
Open mobile menuHome
```
================================================
FILE: src/pages/headless-ui-v1-4/snippets/vue-3.mdx
================================================
```html
SolutionsInsights
```
================================================
FILE: src/pages/headless-ui-v1-4/snippets/vue-4.mdx
================================================
```html
Solutions
```
================================================
FILE: src/pages/heroicons-v1/.prettierrc
================================================
{
"printWidth": 80,
"singleQuote": true,
"semi": false
}
================================================
FILE: src/pages/heroicons-v1/index.mdx
================================================
import { adamwathan } from '@/authors'
import card from './card.jpg'
export const meta = {
title: 'Heroicons v1.0',
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.`,
date: '2021-03-29T19:00:00.000Z',
authors: [adamwathan],
image: card,
footer: `
`,
}
Just 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.
Today 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.
## React + Vue Libraries
In 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.
Here's what it looks like with React for example:
```js
import { BeakerIcon } from '@heroicons/react/solid'
function MyComponent() {
return (
...
)
}
```
[Check out the documentation](https://github.com/tailwindlabs/heroicons) on GitHub to learn more.
## Figma Assets
We've also published an official [Heroicons Figma file](https://www.figma.com/community/file/958423903283802665/heroicons) on our new Figma Community page!
It 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.
================================================
FILE: src/pages/index.js
================================================
import tinytime from 'tinytime'
import Link from 'next/link'
import Head from 'next/head'
import getAllPostPreviews from '@/getAllPostPreviews'
import twitterCard from '@/img/twitter-card.jpg'
import Header from '@/components/Header'
import SectionContainer from '@/components/SectionContainer'
const posts = getAllPostPreviews()
const postDateTemplate = tinytime('{MMMM} {DD}, {YYYY}')
export default function Home() {
return (
<>
Blog – Tailwind CSS
Latest
All the latest Tailwind CSS news, straight from the team.
>
)
}
================================================
FILE: src/pages/introducing-heroicons/index.mdx
================================================
import { steveschoger } from '@/authors'
import card from './card.jpg'
import banner from './banner.svg'
import iconStyles from './icon-styles.svg'
import heroPatternsPreview from './heropatterns-preview.jpg'
export const meta = {
title: 'Introducing Heroicons.com',
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.`,
date: '2020-08-25T13:00:00.000Z',
authors: [steveschoger],
image: card,
discussion: 'https://github.com/tailwindlabs/tailwindcss/discussions/2238',
}
A 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.
There are currently over 220 icons available in both medium and small sizes, with size designed to serve a different use-case:
- Medium icons are designed to be rendered at 24x24, and work well for things like primary navigation and marketing sections.
- Small icons are designed to be rendered at 20x20, and work well for buttons, form elements and to support text.
All of the icons are Tailwind-ready, and are easy to style with Tailwind’s built-in size and color utilities.
```html
```
For best results, use h-6 w-6 for medium icons, and h-5 w-5 for small icons.
## Just the beginning
We’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.
Designing 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:
We’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.
## Got a suggestion?
If 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.
================================================
FILE: src/pages/introducing-linting-for-tailwindcss-intellisense/index.mdx
================================================
import { bradlc } from '@/authors'
import imgCss from './css.png'
import imgCss2x from './css@2x.png'
import imgHtml from './html.png'
import imgHtml2x from './html@2x.png'
import applyQuickFixVideo from './apply-quick-fix.mp4'
import image from './card.jpg'
export const meta = {
title: 'Introducing linting for Tailwind CSS IntelliSense',
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.',
date: '2020-06-23T18:52:03Z',
authors: [bradlc],
image,
discussion: 'https://github.com/tailwindcss/tailwindcss/discussions/1956',
}
Today 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.
## Detecting errors in your CSS
Tailwind 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:
## Catching conflicts in your HTML
There 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!
## Quick fixes included
To 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!
As 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:
## Configuration
We 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.
Check out the [extension readme](https://github.com/tailwindcss/intellisense#tailwindcssvalidate) for more details about configuring the lint rules to suit your workflow.
## Conclusion
Linting 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).
This 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!
================================================
FILE: src/pages/introducing-tailwind-play/index.mdx
================================================
import { adamwathan } from '@/authors'
import image from './card.jpg'
export const meta = {
title: `Introducing Tailwind Play`,
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.`,
date: '2020-10-07T13:00:00.000Z',
authors: [adamwathan],
image,
discussion: 'https://github.com/tailwindlabs/tailwindcss/discussions/2511',
}
To 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).
This 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.
So 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.
It 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:
- Customizing your Tailwind theme
- Enabling special variants, like `group-hover` or `focus-within`
- Using custom directives in your CSS like `@apply`, `@variants`, and `@responsive`
- Adding plugins like `@tailwindcss/typography`
- Intelligent code completion and linting
- Responsive design mode
- One-click sharing
The 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!
Our 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.
One-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.
**Check it out at [play.tailwindcss.com](https://play.tailwindcss.com)** and let us know what you think!
================================================
FILE: src/pages/just-in-time-the-next-generation-of-tailwind-css/index.mdx
================================================
import { adamwathan } from '@/authors'
import image from './card.jpg'
export const meta = {
title: `Just-In-Time: The Next Generation of Tailwind CSS`,
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.`,
date: '2021-03-15T16:30:00.000Z',
authors: [adamwathan],
image,
footer: `
`,
}
_**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)._
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. 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.
For 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`.
Today 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).
[**@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.
This comes with a lot of advantages:
- **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.
- **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.
- **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]`.
- **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.
- **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.
Try it today by installing `@tailwindcss/jit` and swapping it into your PostCSS configuration:
```shell
npm install -D @tailwindcss/jit tailwindcss postcss autoprefixer
```
```js
// postcss.config.js
module.exports = {
plugins: {
'@tailwindcss/jit': {},
autoprefixer: {},
},
}
```
We'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.
[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!
================================================
FILE: src/pages/multi-line-truncation-with-tailwindcss-line-clamp/index.mdx
================================================
import { adamwathan } from '@/authors'
import image from './card.jpg'
export const meta = {
title: 'Multi-line truncation with @tailwindcss/line-clamp',
description: `A few weeks back we released @tailwindcss/line-clamp, an official Tailwind CSS plugin for truncating text to a specific number of lines.`,
date: '2021-01-24T20:00:00Z',
authors: [adamwathan],
image,
}
Imagine 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.
It 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.
Sound familiar? If so, the line-clamp plugin is here to save your bacon.
A few weeks back we released{' '}
@tailwindcss/line-clamp
, an official Tailwind CSS plugin for truncating text to a specific number of lines.
Imagine 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.
It 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.
Sound familiar? If so, the line-clamp plugin is here to save your bacon.
First, install the plugin and add it to your `tailwind.config.js` file:
```shell
npm install @tailwindcss/line-clamp
```
```js
// tailwind.config.js
module.exports = {
// ...
plugins: [
// ...
require('@tailwindcss/line-clamp'),
],
}
```
Then 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:
```html
Here's a block of text from a blog post that isn't conveniently three lines long like you designed
for originally. It's probably like 6 lines on mobile or even on desktop depending on how you have
things laid out. Truly a big pain in the derriere, and not the sort of thing you expected to be
wasting your time trying to deal with at 4:45pm on a Friday am I right? You've got tickets to
SmackDown and you heard there's gonna be a dark match with that local guy from two towns over that
your cousin went to high school with before the show starts, and you're gonna miss it if you're
not there early.
```
For more details, [check out the documentation](https://github.com/tailwindlabs/tailwindcss-line-clamp/) over on the GitHub repository.
================================================
FILE: src/pages/simon-vrachliotis-joins-tailwind-labs/index.mdx
================================================
import { adamwathan } from '@/authors'
import image from './card.jpg'
export const meta = {
title: 'Simon Vrachliotis Joins Tailwind Labs',
description: `Today we are super excited to share that Simon Vrachliotis has joined the development team at Tailwind Labs.`,
date: '2020-07-19T19:00:00.000Z',
authors: [adamwathan],
image,
discussion: 'https://github.com/tailwindcss/tailwindcss/discussions/2043',
}
Today 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?)
Simon 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.
He 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.
Steve 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:
We know this video isn't from Laracon, but this recording turned out better :)
He 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.
Simon 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.
He’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.
We couldn’t be more excited to be welcoming him to the team!
================================================
FILE: src/pages/tailwind-ui-ecommerce/.prettierrc
================================================
{
"printWidth": 80,
"singleQuote": true,
"semi": false
}
================================================
FILE: src/pages/tailwind-ui-ecommerce/index.mdx
================================================
import { adamwathan } from '@/authors'
import card from './card.jpg'
import productPagePreview from './product-page-preview.jpg'
export const meta = {
title: 'Introducing Tailwind UI Ecommerce',
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.`,
date: '2021-08-11T19:30:00.000Z',
authors: [adamwathan],
image: card,
footer: `
`,
}
Almost 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.
Almost 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!
[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:
- Product Overviews
- Product Lists
- Category Previews
- Shopping Carts
- Category Filters
- Product Quickviews
- Store Navigation
- Promo Sections
- Checkout Forms
- Customer Reviews
- Order Summaries
- Storefront Pages
- Product Pages
- Order History Pages
...and more.
For a quick preview, check out this product page example we shared via our newsletter last week:
It'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.
You 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).
If 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.
================================================
FILE: src/pages/tailwind-ui-now-with-react-and-vue-support/.prettierrc
================================================
{
"printWidth": 80,
"singleQuote": true,
"semi": false
}
================================================
FILE: src/pages/tailwind-ui-now-with-react-and-vue-support/index.mdx
================================================
import { adamwathan } from '@/authors'
import card from './card.jpg'
export const meta = {
title: 'Tailwind UI: Now with React + Vue support',
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.`,
date: '2021-04-14T16:01:00.000Z',
authors: [adamwathan],
image: card,
footer: `
`,
}
Last 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.
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.
It’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.
## Functional and accessible
All 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.
Headless 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.
## Fully customizable
With 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.
```jsx
import { useState } from 'react'
import { Switch } from '@headlessui/react'
function classNames(...classes) {
return classes.filter(Boolean).join(' ')
}
export default function Example() {
const [enabled, setEnabled] = useState(false)
return (
Use setting
)
}
```
You 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.
## Get started
If 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.
If 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.
================================================
FILE: src/pages/tailwindcss-1-5/index.mdx
================================================
import { adamwathan } from '@/authors'
import image from './card.jpg'
export const meta = {
title: 'Tailwind CSS v1.5.0',
description: `Tailwind CSS v1.5.0 is here, now with component variants, responsive container variants, focus-visible support, and more.`,
date: '2020-07-15T18:55:18.391Z',
authors: [adamwathan],
image,
discussion: 'https://github.com/tailwindcss/tailwindcss/discussions/2033',
}
I 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.
No breaking changes, this is a minor release and we're professionals you silly goose.
I 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.
No breaking changes, this is a minor release and we're professionals you silly goose.
## New Features
### Component `variants` support
Until Tailwind CSS v1.5.0, only "utility" classes were really intended to be used with `variants` (like "responsive", "hover", "focus", etc.)
While 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:
```html
```
You 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:
```js
plugin(function ({ addComponents })) {
addComponents({
'.card': {
// ...
}
}, {
variants: ['responsive']
})
})
```
...or using the array shorthand you might be familiar with from the `addUtilities` API:
```js
plugin(function ({ addComponents })) {
addComponents({
'.card': {
// ...
}
}, ['responsive'])
})
```
To 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:
```css
@layer components {
@responsive {
.card {
/* ... */
}
}
}
```
This helps Tailwind purge your unused CSS correctly, ensuring it doesn't remove any responsive component variants when using the default "conservative" purge mode.
### Responsive `container` variants
Piggy-backing off of the new component `variants` support, the `container` class now supports variants!
```html
```
We'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:
```js
// tailwind.config.js
module.exports = {
// ...
variants: {
container: ['responsive', 'focus', 'group-hover'],
},
}
```
### New `focus-visible` variant
We'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.
This is super useful for adding focus styles that _only_ appear to keyboard users, and are ignored for mouse users:
```html
```
It's not enabled for anything by default, but you can enable it in the `variants` section of your config file:
```js
// tailwind.config.js
module.exports = {
// ...
variants: {
backgroundColor: ['responsive', 'hover', 'focus', 'focus-visible'],
},
}
```
Browser 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.
### New `checked` variant
We've added a new `checked` variant you can use to conditionally style things like checkboxes and radio buttons:
```html
```
It's not enabled for anything by default, but you can enable it in the `variants` section of your config file:
```js
// tailwind.config.js
module.exports = {
// ...
variants: {
backgroundColor: ['responsive', 'hover', 'focus', 'checked'],
},
}
```
================================================
FILE: src/pages/tailwindcss-1-6/index.mdx
================================================
import { adamwathan } from '@/authors'
import image from './card.jpg'
export const meta = {
title: 'Tailwind CSS v1.6.0',
description: `Tailwind CSS v1.6.0 is now available, with animations and more!`,
date: '2020-07-28T16:58:33.714Z',
authors: [adamwathan],
image,
discussion: 'https://github.com/tailwindlabs/tailwindcss/discussions/2088',
}
It's like Tailwind CSS v1.5 except now there's animation support, overscroll utilities, and more!
There 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.
## New Features
### Animation support
Tailwind CSS v1.6 adds a brand new `animation` core plugin, with 4 general purpose animations included out of the box:
- `animate-spin`
- `animate-ping`
- `animate-pulse`
- `animate-bounce`
```html
```
These are completely customizable as always, using the `animation` and `keyframes` sections of your `tailwind.config.js` theme:
```js
// tailwind.config.js
module.exports = {
theme: {
extend: {
animation: {
wiggle: 'wiggle 1s ease-in-out infinite',
},
keyframes: {
wiggle: {
'0%, 100%': { transform: 'rotate(-3deg)' },
'50%': { transform: 'rotate(3deg)' },
},
},
},
},
}
```
For 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).
### New `prefers-reduced-motion` variants
To 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).
These can be useful in conjunction with transition and animation utilities to disable problematic motion for users who are sensitive to it:
```html
```
...or to explicitly opt-in to motion to make sure it's only being shown to users who haven't opted out:
```html
```
These can be combined with responsive variants and pseudo-class variants as well:
```html
```
These 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:
```js
// tailwind.config.js
module.exports = {
// ...
variants: {
translate: ['responsive', 'hover', 'focus', 'motion-safe', 'motion-reduce'],
},
}
```
For more details, check out [the updated variants documentation](https://tailwindcss.com/docs/pseudo-class-variants).
### New `overscroll-behavior` utilities
We've also added new utilities for the [`overscroll-behavior`](https://developer.mozilla.org/en-US/docs/Web/CSS/overscroll-behavior) property.
You 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.
```html
```
Note 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.
This plugin can be configured in your `tailwind.config.js` file as `overscrollBehavior`:
```js
// tailwind.config.js
module.exports = {
// ...
// Disabling the plugin
corePlugins: {
overscrollBehavior: false,
},
// Customizing the enabled variants
variants: {
overscrollBehavior: ['responsive', 'hover'],
},
}
```
### Generate your CSS without an input file
If you never write any custom CSS and you're sick of creating this file all the time...
```css
@tailwind base;
@tailwind components;
@tailwind utilities;
```
...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.
The 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:
```bash
npx tailwindcss build -o compiled.css
```
Your kids are going to be so grateful for the extra time you get to spend together.
================================================
FILE: src/pages/tailwindcss-1-7/index.mdx
================================================
import { adamwathan } from '@/authors'
import image from './card.jpg'
export const meta = {
title: 'Tailwind CSS v1.7.0',
description: `Tailwind CSS v1.7.0 is now available, with gradients and more!`,
date: '2020-08-18T19:15:00.000Z',
authors: [adamwathan],
image,
discussion: 'https://github.com/tailwindlabs/tailwindcss/discussions/2183',
}
Another 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!
## New features
### Gradients
The big one for this release — Tailwind now ships with built-in support for background gradients!
Gradients are designed with a highly composable API that lets you specify up to three color stops in one of 8 directions by default:
```html
```

This 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.
The default configuration for these plugins looks like this:
```js
// tailwind.config.js
module.exports = {
theme: {
backgroundImage: {
'gradient-to-t': 'linear-gradient(to top, var(--gradient-color-stops))',
'gradient-to-tr': 'linear-gradient(to top right, var(--gradient-color-stops))',
'gradient-to-r': 'linear-gradient(to right, var(--gradient-color-stops))',
'gradient-to-br': 'linear-gradient(to bottom right, var(--gradient-color-stops))',
'gradient-to-b': 'linear-gradient(to bottom, var(--gradient-color-stops))',
'gradient-to-bl': 'linear-gradient(to bottom left, var(--gradient-color-stops))',
'gradient-to-l': 'linear-gradient(to left, var(--gradient-color-stops))',
'gradient-to-tl': 'linear-gradient(to top left, var(--gradient-color-stops))',
},
gradientColorStops: (theme) => theme('colors'),
},
variants: {
backgroundImage: ['responsive'],
gradientColorStops: ['responsive', 'hover', 'focus'],
},
}
```
Learn more [the original pull request](https://github.com/tailwindlabs/tailwindcss/pull/2176).
### New background-clip utilities
We've also added a new `backgroundClip` core plugin that you can use to control how background are rendered within an element.
It includes 4 new utilities:
| Class | CSS |
| ----------------- | ------------------------------ |
| `bg-clip-border` | `background-clip: border-box` |
| `bg-clip-padding` | `background-clip: padding-box` |
| `bg-clip-content` | `background-clip: content-box` |
| `bg-clip-text` | `background-clip: text` |
Combined with the new gradient features, you can use this to do cool gradient text stuff like this:
```html
Greetings from Tailwind v1.7.
```

Only responsive variants are enabled for the `backgroundClip` plugin by default:
```js
// tailwind.config.js
module.exports = {
variants: {
backgroundClip: ['responsive'],
},
}
```
### New gap utility aliases
For 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.
I was finding myself getting them wrong all the time — is `row-gap` the gaps in a row, or the gap between rows?
Tailwind 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".
These utilities will replace the old ones in v2.0, but for now they both exist together.
**We recommend migrating to the new names now, and disabling the old names using this feature flag:**
```js
// tailwind.config.js
module.exports = {
future: {
removeDeprecatedGapUtilities: true,
},
// ...
}
```
Tailwind will issue a warning in the console to remind you that you are including deprecated classes in your build until you enable this flag.
### New `contents` display utility
We've added a new `contents` class for the recent `display: contents` CSS feature.
```html
```
Learn more about it in [this great article by Rachel Andrew](https://rachelandrew.co.uk/archives/2016/01/29/vanishing-boxes-with-display-contents/).
### Default letter-spacing per font-size
You can now configure a default letter-spacing value for each font-size in your `tailwind.config.js` theme, using a tuple syntax:
```js
// tailwind.config.js
module.exports = {
theme: {
fontSize: {
2xl: ['24px', {
letterSpacing: '-0.01em',
}],
// Or with a default line-height as well
3xl: ['32px', {
letterSpacing: '-0.02em',
lineHeight: '40px',
}],
}
}
}
```
This new syntax is supported in addition to the simpler `[{fontSize}, {lineHeight}]` syntax that was recently introduced.
### Divide border styles
We've added utilities for setting the border style on the `divide` utilities:
```html
```
These utilities include `responsive` variants by default:
```js
// tailwind.config.js
module.exports = {
variants: {
divideStyle: ['responsive'],
},
}
```
### Access entire config object from plugins
The `config` function passed to the plugin API now returns the entire config option when invoked with no arguments:
```js
tailwind.plugin(function ({ config, addUtilities, /* ... */ })) {
// Returns entire config object
config()
})
```
### Define colors as closures
You can now define your colors as callbacks, which receive a bag of parameters you can use to generate your color value.
This is particularly useful when trying to make your custom colors work with the `backgroundOpacity`, `textOpacity`, etc. utilities
```
// tailwind.config.js
module.exports = {
theme: {
colors: {
primary: ({ opacityVariable }) => `rgba(var(--color-primary), var(${variable}, 1))`,
},
},
}
```
Currently 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.
## Deprecations
Tailwind v1.7 introduces a new feature flagging and deprecation system designed to make upgrades as painless as possible.
Any 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.
Whenever 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:
```
risk - There are upcoming breaking changes: removeDeprecatedGapUtilities
risk - We highly recommend opting-in to these changes now to simplify upgrading Tailwind in the future.
risk - https://tailwindcss.com/docs/upcoming-changes
```
You can opt-in to a breaking change by setting that flag to `true` in your `tailwind.config.js` file:
```js
// tailwind.config.js
module.exports = {
future: {
removeDeprecatedGapUtilities: true,
},
}
```
If you'd prefer not to opt-in but would like to silence the warning, explicitly set the flag to `false`:
```js
// tailwind.config.js
module.exports = {
future: {
removeDeprecatedGapUtilities: false,
},
}
```
**We do not recommend this**, as it will make upgrading to Tailwind v2.0 more difficult.
### Deprecated gap utilities
As 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.
By default both classes will exist, but the old utilities will be removed in Tailwind v2.0.
To migrate to the new class names, simply replace any existing usage of the old names with the new names:
```diff
-
+
```
To opt-in to the new names now, enable the `removeDeprecatedGapUtilities` flag in your `tailwind.config.js` file:
```js
// tailwind.config.js
module.exports = {
future: {
removeDeprecatedGapUtilities: true,
},
}
```
## Experimental features
Tailwind 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.
It's important to note that **experimental features may introduce breaking changes, do not follow semver, and can change at any time**.
If you like to live on the wild side though, you can enable all of them like so:
```js
// tailwind.config.js
module.exports = {
experimental: 'all',
}
```
With 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...
### Use `@apply` with variants and other complex classes
This is a huge one — you can finally use `@apply` with responsive variants, pseudo-class variants, and other complex classes!
```css
.btn {
@apply bg-indigo hover:bg-indigo-700 sm:text-lg;
}
```
There 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.
This introduces breaking changes to how `@apply` worked before, so be sure to read all of the details before just flipping the switch.
To enable this feature, use the `applyComplexClasses` flag:
```js
// tailwind.config.js
module.exports = {
experimental: {
applyComplexClasses: true,
},
}
```
### New color palette
We've added a teaser of the new Tailwind 2.0 color palette that you can start playing with today using the `uniformColorPalette` flag:
```js
// tailwind.config.js
module.exports = {
experimental: {
uniformColorPalette: true,
},
}
```
The 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.
We do expect these colors to continue to change a lot as we iterate on them, so use these at your own risk.
### Extended spacing scale
We'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.)
You can enable the extended spacing scale using the `extendedSpacingScale` flag:
```js
// tailwind.config.js
module.exports = {
experimental: {
extendedSpacingScale: true,
},
}
```
This is pretty stable, I would be surprised if we change this.
### Default line-heights per font-size by default
We've added recommended default line-heights to every built-in font-size, which can be enabled using the `defaultLineHeights` flag:
```js
// tailwind.config.js
module.exports = {
experimental: {
defaultLineHeights: true,
},
}
```
This is a breaking change and will impact your designs, as previously all font sizes had a default line-height of `1.5`.
### Extended font size scale
We'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.
You can enable them under the `extendedFontSizeScale` flag:
```js
// tailwind.config.js
module.exports = {
experimental: {
extendedFontSizeScale: true,
},
}
```
================================================
FILE: src/pages/tailwindcss-1-8/index.mdx
================================================
import { adamwathan } from '@/authors'
import image from './card.jpg'
export const meta = {
title: 'Tailwind CSS v1.8.0',
description: `Tailwind CSS v1.8.0 is now available, with font-variant-numeric support, experimental dark mode, and more!`,
date: '2020-09-04T20:15:00.000Z',
authors: [adamwathan],
image,
discussion: 'https://github.com/tailwindlabs/tailwindcss/discussions/2315',
}
Tailwind CSS v1.8 is now available with a handful of new utilities, a couple new features, and an exciting new experiment!
### New features
- New `font-variant-numeric` utilities ([#2305](https://github.com/tailwindlabs/tailwindcss/pull/2305))
- New `place-items`, `place-content`, `place-self`, `justify-items`, and `justify-self` utilities ([#2306](https://github.com/tailwindlabs/tailwindcss/pull/2306))
- New `preserveHtmlElements` option for `purge` ([#2283](https://github.com/tailwindlabs/tailwindcss/pull/2283))
- New `layers` mode for `purge` ([#2288](https://github.com/tailwindlabs/tailwindcss/pull/2288))
- Support configuring variants as functions ([#2309](https://github.com/tailwindlabs/tailwindcss/pull/2309))
- Dark mode variant (experimental) ([#2279](https://github.com/tailwindlabs/tailwindcss/pull/2279))
### Changes
- CSS within `@layer` at-rules are now grouped with the corresponding `@tailwind` at-rule ([#2312](https://github.com/tailwindlabs/tailwindcss/pull/2312))
### Deprecations
- The `conservative` purge mode has been deprecated in favor of the new `layers` mode ([#2288](https://github.com/tailwindlabs/tailwindcss/pull/2288))
Check out the [full release notes on GitHub](https://github.com/tailwindlabs/tailwindcss/releases/tag/v1.8.0) for more details.
================================================
FILE: src/pages/tailwindcss-1-9/.prettierrc
================================================
{
"printWidth": 80,
"singleQuote": true,
"semi": false
}
================================================
FILE: src/pages/tailwindcss-1-9/index.mdx
================================================
import { adamwathan } from '@/authors'
import image from './card.jpg'
export const meta = {
title: 'Tailwind CSS v1.9.0',
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!`,
date: '2020-10-13T18:30:00.000Z',
authors: [adamwathan],
image,
discussion: 'https://github.com/tailwindlabs/tailwindcss/discussions/2552',
}
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!
Let's dig in to the highlights...
- [Configuration presets](#configuration-presets)
- [Utilities for `grid-auto-columns` and `grid-auto-rows`](#utilities-for-grid-auto-columns-and-grid-auto-rows)
- [Focus indicator improvements and configurable outlines](#focus-indicator-improvements-and-configurable-outlines)
- [Extended border radius, rotate, and skew scales](#extended-border-radius-rotate-and-skew-scales)
- [Upgrading to v1.9](#upgrading)
For the complete summary of changes [check out the release notes on GitHub](https://github.com/tailwindlabs/tailwindcss/releases/tag/v1.9.0).
---
Configuration presets
Tailwind 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.
```js
// tailwind.config.js
module.exports = {
presets: [require('@my-company/tailwind-base')],
theme: {
extend: {
// Project specific overrides...
},
},
}
```
Whatever 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.
You can even list multiple presets, which are merged together from top to bottom:
```js
// tailwind.config.js
module.exports = {
presets: [
require('@my-company/tailwind-base'),
require('@my-company/tailwind-marketing'),
],
theme: {
extend: {
// Project specific overrides...
},
},
}
```
The 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.
---
Utilities for grid-auto-columns and grid-auto-rows
We've added new `gridAutoColumns` and `gridAutoRows` core plugins that add new utilities for the `grid-auto-columns` and `grid-auto-rows` CSS properties respectively.
These 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.
```html
1
2
3
```
Here's a list of the new utilities that are included out of the box:
| Class | CSS |
| ---------------- | ------------------------------------ |
| `auto-cols-auto` | `grid-auto-columns: auto;` |
| `auto-cols-min` | `grid-auto-columns: min-content;` |
| `auto-cols-max` | `grid-auto-columns: max-content;` |
| `auto-cols-fr` | `grid-auto-columns: minmax(0, 1fr);` |
| `auto-rows-auto` | `grid-auto-rows: auto;` |
| `auto-rows-min` | `grid-auto-rows: min-content;` |
| `auto-rows-max` | `grid-auto-rows: max-content;` |
| `auto-rows-fr` | `grid-auto-rows: minmax(0, 1fr);` |
We 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.
---
Focus indicator improvements and configurable outlines
We'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.
Now you can create custom focus styles using box shadows _safely_, without making your sites difficult to use for people with low vision.
```html
```
We've also added two new outline styles: `outline-white` and `outline-black`.
These 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.
We'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.
```html
```
Of 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.
We've made the `outline` property configurable as well, so you can now define custom outlines in your `tailwind.config.js` file:
```js
// tailwind.config.js
module.exports = {
theme: {
extend: {
outline: {
blue: '2px solid #0000ff',
},
},
},
}
```
You can also provide an `outline-offset` value for any custom outline utilities using a tuple of the form `[outline, outlineOffset]`:
```js
// tailwind.config.js
module.exports = {
theme: {
extend: {
outline: {
blue: ['2px solid #0000ff', '1px'],
},
},
},
}
```
---
Extended border radius, rotate, and skew scales
We've added three new border radius utilities by default:
| Class | Value |
| ------------- | ------------------ |
| `rounded-xl` | `0.75rem` _(12px)_ |
| `rounded-2xl` | `1rem` _(16px)_ |
| `rounded-3xl` | `1.5rem`_(24px)_ |
...and an extended set of smaller values for both the `rotate` and `skew` utilities:
| Class | Value |
| ----------- | ------- |
| `rotate-1` | `1deg` |
| `rotate-2` | `2deg` |
| `rotate-3` | `3deg` |
| `rotate-6` | `6deg` |
| `rotate-12` | `12deg` |
| `skew-1` | `1deg` |
| `skew-2` | `2deg` |
Negative versions are included for all of these as well. Super handy for more subtle rotate and skew effects!
---
Upgrading
Tailwind CSS v1.9 is a non-breaking minor release, so to upgrade all you need to do is run:
```bash
# npm
npm install tailwindcss@^1.9
# yarn
yarn add tailwindcss@^1.9
```
We 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.
================================================
FILE: src/pages/tailwindcss-2-1/.prettierrc
================================================
{
"printWidth": 80,
"singleQuote": true,
"semi": false
}
================================================
FILE: src/pages/tailwindcss-2-1/index.mdx
================================================
import { adamwathan } from '@/authors'
import image from './card.jpg'
export const meta = {
title: 'Tailwind CSS v2.1',
description: `We just released Tailwind CSS v2.1 which brings the new JIT engine to core, adds first-class CSS filter support, and more!`,
date: '2021-04-05T19:00:00.000Z',
authors: [adamwathan],
image,
footer: `
`,
}
The 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.
Here are some of the highlights:
- [JIT engine in core](#jit-engine-in-core)
- [Composable CSS filters API](#new-filter-and-backdrop-filter-utilities)
- [New blending mode utilities](#new-blending-mode-utilities)
- [New isolation utilities](#new-isolation-utilities)
For the full details, [check out the release notes](https://github.com/tailwindlabs/tailwindcss/releases/tag/v2.1.0) on GitHub.
---
## JIT engine in core
The [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:
```js
// tailwind.config.js
module.exports = {
mode: 'jit',
purge: [
// ...
],
// ...
}
```
**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.
If 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.
Read the [Just-in-Time Mode documentation](https://tailwindcss.com/docs/just-in-time-mode) to learn more.
## Composable CSS filters API
This is a huge one — we've finally added first-class support for CSS filters!
They 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.
Here's what `filter` looks like:
```html
```
...and here's what `backdrop-filter` looks like:
```html
```
Check 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!
## New blending mode utilities
We've added brand new utilities for `mix-blend-mode` and `background-blend-mode`:
```html
```
Check out [the documentation](https://tailwindcss.com/docs/mix-blend-mode) to learn more.
## New isolation utilities
We've added new `isolate` and `isolation-auto` utilities for working with the `isolation` property:
```html
```
This 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.
I 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.
---
## Upgrading
Tailwind CSS v2.1 is an incremental upgrade with no breaking changes, so to upgrade you just need to run:
```bash
npm install tailwindcss@latest
```
If you were previously using `@tailwindcss/jit`, you can now switch back to `tailwindcss` and update your PostCSS configuration accordingly.
================================================
FILE: src/pages/tailwindcss-2-2/.prettierrc
================================================
{
"printWidth": 80,
"singleQuote": true,
"semi": false
}
================================================
FILE: src/pages/tailwindcss-2-2/index.mdx
================================================
import { adamwathan } from '@/authors'
import image from './card.jpg'
export const meta = {
title: 'Tailwind CSS v2.2',
description: `An all-new high-performance CLI tool, ::before and ::after support, sibling selectors, selected text variants, and tons more.`,
date: '2021-06-17T14:00:00.000Z',
authors: [adamwathan],
image,
footer: `
`,
}
Well 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!
We'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.
Well 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!
This 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.
Here are the highlights:
- [All-new high-performance Tailwind CLI](#all-new-high-performance-tailwind-cli)
- [Before and after variants](#before-and-after-variants)
- [First-letter/line variants](#first-letter-line-variants)
- [Selected text variants](#selected-text-variants)
- [List marker variants](#list-marker-variants)
- [Sibling selector variants](#sibling-selector-variants)
- [Exhaustive pseudo-class support](#exhaustive-pseudo-class-support)
- [Shorthand color opacity syntax](#shorthand-color-opacity-syntax)
- [Extended arbitrary value support](#extended-arbitrary-value-support)
- [Improved nesting support](#improved-nesting-support)
- [Caret color utilities](#caret-color-utilities)
- [Background origin utilities](#background-origin-utilities)
- [Simplified transform and filter composition](#simplified-transform-and-filter-composition)
- [Per-side border color utilities](#per-side-border-color-utilities)
- [Built-in safelist, transform, and extract support](#built-in-safelist-transform-and-extract-support)
For the full details, [check out the release notes](https://github.com/tailwindlabs/tailwindcss/releases/tag/v2.2.0) on GitHub.
It'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.
When you're ready to upgrade, just install the latest version from npm and you're off to the races:
```shell
npm install -D tailwindcss@latest
```
---
## All-new high-performance Tailwind CLI
We'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.
```shell
npx tailwindcss -o dist/tailwind.css --watch --jit --purge="./src/**/*.html"
```
Here are some of the highlights:
- **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.
- **Watch mode** — so you can automatically rebuild your CSS whenever you make any changes.
- **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.
- **Minification support** — now you can minify your CSS with [cssnano](https://cssnano.co/) just by adding the `--minify` flag.
- **PostCSS plugin support** — the new CLI will read and respect any extra plugins you configure using a `postcss.config.js` file.
It'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.
Check out our [updated Tailwind CLI documentation](https://tailwindcss.com/docs/installation#using-tailwind-cli) to learn more.
_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._
## Before and after pseudo-element variants
_This feature is only available in [Just-in-Time mode](https://tailwindcss.com/docs/just-in-time-mode)_.
People 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`:
```html
```
We 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:
```html
```
You can even grab the content from an attribute using the CSS `attr()` function:
```html
```
This can be super helpful when your content has spaces in it, since spaces can't be used in CSS class names.
## First-letter/line variants
_This feature is only available in [Just-in-Time mode](https://tailwindcss.com/docs/just-in-time-mode)_.
We've added variants for the `first-letter` and `first-line` pseudo-elements, so you can do stuff like drop caps:
```html
The night was March 31, 1996, and it was finally time for Bret Hart to face
off against Shawn Michaels in the long anticipated Iron Man match — a 60
minute war of endurance where the man who scored the most number of falls
would walk away as the WWF World Heavyweight Champion.
```
## Selected text variants
_This feature is only available in [Just-in-Time mode](https://tailwindcss.com/docs/just-in-time-mode)_.
We've added a new `selection` variant that makes it super easy to style highlighted to match your design:
```html
After nearly a grueling hour of warfare with neither man scoring a fall, Hart
locked in the Sharpshooter, his signature submission hold. As Michaels
screamed in pain, the crowd were certain that Hart was about to walk away from
WrestleMania XII as the still-World Heavyweight Champion.
```
We'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:
```html
But Michaels didn't give up — he held on until the bell rang and the
designated 60 minutes was up. Hart walked away content, thinking that
without a clear winner, the title was his to hold. He was not prepared for
what would happen next, when Gorilla Monsoon declared the match would
continue under sudden death rules.
```
## List marker variants
_This feature is only available in [Just-in-Time mode](https://tailwindcss.com/docs/just-in-time-mode)_.
You can use the new `marker` variant to style the bullets or numbers at the beginning of a list:
```html
WrestleMania XII Results
The British Bulldog, Owen Hart, and Vader defeated Ahmed Johnson, Jake
Roberts, and Yokozuna
Roddy Piper defeated Goldust
Stone Cold Steve Austin defeated Savio Vega
The Ultimate Warrior defeated Hunter Hearst Helmsley
The Undertaker defeated Diesel
Shawn Michaels defeated Bret Hart
```
Like 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.
## Sibling selector variants
_This feature is only available in [Just-in-Time mode](https://tailwindcss.com/docs/just-in-time-mode)_.
Tailwind CSS v2.2 adds new `peer-*` variants that behave much like the `group-*` variants, but for targeting sibling elements instead of parent elements.
This is useful for things like styling an element when a preceding checkbox is checked, doing things like floating labels, and lots more:
```html
```
Just 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.
The generated CSS uses the [general sibling combinator](https://developer.mozilla.org/en-US/docs/Web/CSS/General_sibling_combinator) and looks like this:
```css
.peer:checked ~ .peer-checked\:bg-blue-500 {
background-color: #3b82f6;
}
```
So just like in vanilla CSS, it will only work for targeting _previous_ siblings, not siblings that appear later in the DOM.
## Exhaustive pseudo-class support
_This feature is only available in [Just-in-Time mode](https://tailwindcss.com/docs/just-in-time-mode)_.
We've added variants for basically every single missing pseudo-class we could think of in this release:
- `only` _(only-child)_
- `first-of-type`
- `last-of-type`
- `only-of-type`
- `target`
- `default`
- `indeterminate`
- `placeholder-shown`
- `autofill`
- `required`
- `valid`
- `invalid`
- `in-range`
- `out-of-range`
Personal 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:
```html
```
## Shorthand color opacity syntax
_This feature is only available in [Just-in-Time mode](https://tailwindcss.com/docs/just-in-time-mode)_.
Instead 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:
```diff-html
-
+
```
This 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:
```html
```
The opacity values are taken from your `opacity` scale, but you can also use arbitrary opacity values using square bracket notation:
```html
```
If 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.
## Extended arbitrary value support
_This feature is only available in [Just-in-Time mode](https://tailwindcss.com/docs/just-in-time-mode)_.
We'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.
You should be able to whatever arbitrary values you want, just about wherever you want:
```html
```
If you find one we missed, open an issue and we'll sort it out.
In 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:
```html
```
Now you can provide a hint to the engine by prefixing the arbitrary value with the type name:
```html
```
Currently, the supported types are:
- `length`
- `color`
- `angle`
- `list`
We'll probably flesh this out even more over time as people discover new edge cases but this should get you very far.
## Improved nesting support
Since 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`.
To 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.
So if you need nesting support in your project, use our plugin, and stick it before Tailwind in your PostCSS plugin list:
```js
// postcss.config.js
module.exports = {
plugins: [
// ...
require('tailwindcss/nesting'),
require('tailwindcss'),
// ...
],
}
```
By 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:
```js
// postcss.config.js
module.exports = {
plugins: [
// ...
require('tailwindcss/nesting')(require('postcss-nesting')),
require('tailwindcss'),
// ...
],
}
```
Under 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:
```css
/* Input */
@media screen(sm) {
/* ... */
}
/* Output */
@media (min-width: 640px) {
/* ... */
}
```
You 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.
```diff-css
- @screen sm { /* ... */ }
+ @media screen(sm) { /* ... */ }
```
## Caret color utilities
_This feature is only available in [Just-in-Time mode](https://tailwindcss.com/docs/just-in-time-mode)_.
You can now set the color of the cursor in form fields using the new `caret-{color}` utilities:
```html
```
These are customizable using the `caretColor` key in the `theme` section of your `tailwind.config.js` file.
## Background origin utilities
We'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:
```html
Background is rendered under the border
Background is rendered within the border but on top of any padding
Background is rendered within any padding and under the content
```
Learn more in the [background origin documentation](https://tailwindcss.com/docs/background-origin).
## Simplified transform and filter composition
_This feature is only available in [Just-in-Time mode](https://tailwindcss.com/docs/just-in-time-mode)_.
The `transform`, `filter`, and `backdrop-filter` classes are no longer necessary to "enable" their respective set of composable utilities.
```diff-html
-
+
```
Now those features are automatically enabled any time you use any of the relevant sub-utilities.
It'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:
```diff-html
-
+
```
I 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.
## Per-side border color utilities
_This feature is only available in [Just-in-Time mode](https://tailwindcss.com/docs/just-in-time-mode)_.
Requested 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.
```html
```
Go forth and build ugly websites! _(Kidding, kidding, I know they are useful settle the hell down.)_
## Built-in safelist, transform, and extract support
We'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.
First 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:
```js
// tailwind.config.js
module.exports = {
purge: {
content: ['./src/**/*.html'],
safelist: [
'bg-blue-500',
'text-center',
'hover:opacity-100',
// ...
'lg:text-right',
],
},
// ...
}
```
**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.
Next is `transform`, which lets you transform content for different file extensions before scanning it for potential class names:
```js
// tailwind.config.js
let remark = require('remark')
module.exports = {
purge: {
content: ['./src/**/*.{html,md}'],
transform: {
md: (content) => {
return remark().process(content)
},
},
},
// ...
}
```
This is really useful if you have templates that are written in a language that compiles to HTML, like Markdown.
Finally we have `extract`, which lets you customize the logic that Tailwind uses to detect class names in specific file types:
```js
// tailwind.config.js
module.exports = {
purge: {
content: ['./src/**/*.{html,md}'],
extract: {
pug: (content) => {
return /[^<>"'`\s]*/.match(content)
},
},
},
// ...
}
```
This is an advanced feature and most users won’t need it. The default extraction logic in Tailwind works extremely well for almost all projects.
For more information on these features, check out our [optimizing for production documentation](https://tailwindcss.com/docs/optimizing-for-production).
---
## Upgrading
To upgrade to Tailwind CSS v2.2, install the latest release from npm:
```bash
npm install -D tailwindcss@latest
```
If 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.
================================================
FILE: src/pages/tailwindcss-from-zero-to-production/index.mdx
================================================
import { simonswiss } from '@/authors'
import image from './card.jpg'
export const meta = {
title: `"Tailwind CSS: From Zero to Production" on YouTube`,
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.`,
date: '2021-02-16T16:05:00.000Z',
authors: [simonswiss],
image,
footer: `
`,
}
Today 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.
It'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.
1. [Setting Up Tailwind CSS](https://www.youtube.com/watch?v=qYgogv4R8zg)
2. [The Utility-First Workflow](https://www.youtube.com/watch?v=UvF56fPGVt4)
3. [Responsive Design](https://www.youtube.com/watch?v=hX1zUdj4Dw4)
4. [Hover, Focus and Other States](https://www.youtube.com/watch?v=5_BPDve5-3M)
5. [Composing Utilities with @apply](https://www.youtube.com/watch?v=TrftauE2Vyk)
6. [Extracting Reusable Components](https://www.youtube.com/watch?v=v-mkUxhaFVA)
7. [Customizing Your Design System](https://www.youtube.com/watch?v=0l0Gx8gWPHk)
8. [Optimizing for Production](https://www.youtube.com/watch?v=HZn2LtBT59w)
Watch 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.
================================================
FILE: src/pages/tailwindcss-typography/index.mdx
================================================
import { adamwathan } from '@/authors'
import image from './card.jpg'
export const meta = {
title: 'Introducing Tailwind CSS Typography',
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.`,
date: '2020-07-13T16:35:02.037Z',
authors: [adamwathan],
image,
discussion: 'https://github.com/tailwindcss/tailwindcss/discussions/2021',
}
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`](https://github.com/tailwindcss/typography) — a plugin that lets you easily style vanilla HTML content with beautiful typographic defaults.
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.
By 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.
We get lots of complaints about it actually, with people regularly asking us things like:
> 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?
We 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.
That'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.
It 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:
```html
Garlic bread with cheese: What the science tells us
For years parents have espoused the health benefits of eating garlic bread with cheese to their
children, with the food earning such an iconic status in our culture that kids will often dress
up as warm, cheesy loaf for Halloween.
But a recent study shows that the celebrated appetizer may be linked to a series of rabies cases
springing up around the country.
```
So how does it actually look? Well you're looking at it right now — we use it to style the content on this very blog!
[**Check out the documentation**](https://github.com/tailwindcss/typography) to learn more and try it out today.
================================================
FILE: src/pages/tailwindcss-v2/.prettierrc
================================================
{
"printWidth": 80,
"singleQuote": true,
"semi": false
}
================================================
FILE: src/pages/tailwindcss-v2/index.mdx
================================================
import { adamwathan } from '@/authors'
import image from './card.jpg'
import colorsImage from './announcement-tailwind-colors.jpg'
import graysImage from './announcement-tailwind-grays.jpg'
export const meta = {
title: 'Tailwind CSS v2.0',
description: `Today we're finally releasing Tailwind CSS v2.0, including an all-new color palette, dark mode support, and tons more!`,
date: '2020-11-18T17:45:00.000Z',
authors: [adamwathan],
image,
}
Almost 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.
Over 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.
Today we're finally releasing Tailwind CSS v2.0.
Tailwind CSS v2.0 is the first major update ever, including:
- [**All-new color palette**](#all-new-color-palette), featuring 220 total colors and a new workflow for building your own color schemes
- [**Dark mode support**](#dark-mode), making it easier than ever to dynamically change your design when dark mode is enabled
- [**Extra wide 2XL breakpoint**](#extra-wide-2-xl-breakpoint), for designing at 1536px and above
- [**New outline ring utilities**](#new-outline-ring-utilities), which are almost as good as if they would just make `outline-radius` a real thing
- [**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
- [**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
- [**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
- [**Use @apply with anything**](#use-apply-with-anything), including responsive, hover, focus, and other variants
- [**New text overflow utilities**](#new-text-overflow-utilities), for controlling things more precisely than you can with `truncate` alone
- [**Extend variants**](#extend-variants), so you can enable extra variants like `focus-visible` without redeclaring the entire list or thinking about order
- [**Group-hover and focus-within by default**](#group-hover-and-focus-within-by-default), because you were enabling them on every project anyways
- [**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
- [**Incompatibility with IE11**](#incompatibility-with-ie11), so you can tell the person in charge _"sorry boss it's out of my hands, blame Tailwind"_
...and [a bunch of other little things](https://github.com/tailwindlabs/tailwindcss/blob/master/CHANGELOG.md) too.
Even 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.
[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.
If 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.
Also how about that [brand new website](https://tailwindcss.com) eh? Hot damn.
---
All-new color palette
We'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.
The new color palette includes 22 colors _(compared to 10 previously)_ with 10 shades each _(instead of 9_) for a total of 220 values.
We've added an extra light `50` shade for every color, so they go from 50–900 now:
```html
I can't believe it's not white.
```
The 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.
We 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:
```js
// tailwind.config.js
const colors = require('tailwindcss/colors')
module.exports = {
theme: {
colors: {
gray: colors.trueGray,
indigo: colors.indigo,
red: colors.rose,
yellow: colors.amber,
},
},
}
```
Learn more in the new [customizing colors documentation](https://tailwindcss.com/docs/customizing-colors).
---
Dark mode
Ever 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.
Open up your `tailwind.config.js` file and flip `darkMode` to `media`:
```js
// tailwind.config.js
module.exports = {
darkMode: 'media',
// ...
}
```
Boom — now just add `dark:` to the beginning of a class like `bg-black` and it'll only take effect when dark mode is enabled:
```html
Dark mode
The feature you've all been waiting for.
```
Works with hover and stuff too:
```html
```
And responsive stuff:
```html
```
And responsive hover stuff:
```html
```
Check out the [dark mode docs](https://tailwindcss.com/docs/dark-mode) for all the gory details.
---
Extra wide 2XL breakpoint
I'm pretty sure they make an iPhone that is 1280px wide now, so it's time to step it up.
We've added a new `2xl` breakpoint out-of-the-box that lets you target things at 1536px and above:
```html
Godzilla
```
Exciting 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.
---
New outline ring utilities
You 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.
They 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:
```html
```
You can even offset them to create a sort of halo effect with `ring-offset-{width}` utilities:
```html
```
Using a bunch of CSS custom property voodoo we've even made them automatically combine with regular box-shadows, too:
```html
```
The [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.
---
Utility-friendly form styles
One 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`.
I 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.
Alongside 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:
```html
```
It's not included out of the box but you can add it to your `tailwind.config.js` file with a single line:
```js
// tailwind.config.js
module.exports = {
// ...
plugins: [require('@tailwindcss/forms')],
}
```
Check out [the @tailwindcss/forms documentation](https://github.com/tailwindlabs/tailwindcss-forms) for more information.
---
Default line-heights per font-size
Every font-size utility in Tailwind now comes paired with a sensible default line-height:
```js
// Tailwind's default theme
module.exports = {
theme: {
// ...
fontSize: {
xs: ['0.75rem', { lineHeight: '1rem' }],
sm: ['0.875rem', { lineHeight: '1.25rem' }],
base: ['1rem', { lineHeight: '1.5rem' }],
lg: ['1.125rem', { lineHeight: '1.75rem' }],
xl: ['1.25rem', { lineHeight: '1.75rem' }],
'2xl': ['1.5rem', { lineHeight: '2rem' }],
'3xl': ['1.875rem', { lineHeight: '2.25rem' }],
'4xl': ['2.25rem', { lineHeight: '2.5rem' }],
'5xl': ['3rem', { lineHeight: '1' }],
'6xl': ['3.75rem', { lineHeight: '1' }],
'7xl': ['4.5rem', { lineHeight: '1' }],
'8xl': ['6rem', { lineHeight: '1' }],
'9xl': ['8rem', { lineHeight: '1' }],
},
},
}
```
So now when you add a utility like `text-xl`, the corresponding default line-height (`1.75rem` in this case) is added automatically:
```html
This will have a line-height of 1.75rem automatically.
```
If you want to override this, you can still do it by layering on a `leading` utility:
```html
Come on don't do this to me.
```
Check out the [font size documentation](https://tailwindcss.com/docs/font-size#providing-a-default-line-height) for some additional details.
---
Extended spacing, typography, and opacity scales
We've extended the default spacing scale to include a bunch of micro values like `0.5`, `1.5`, `2.5`, and `3.5`:
```html
Just a little nudge.
```
...as well as a bunch of new values at the top end as well like `72`, `80`, and `96`:
```html
This is too much padding.
```
We'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:
```html
```
We've extended the default typography scale with new `7xl`, `8xl`, and `9xl` values:
```html
What is this, an Apple website?
```
And we've also extended the default opacity scale with steps of 10, as well as 5 and 95 values:
```html
You can't see me.
John Cena
```
Peruse the entire [default config file](https://github.com/tailwindlabs/tailwindcss/blob/master/stubs/defaultConfig.stub.js) to see exactly what's available.
---
Use @apply with anything
By far the most common question I've gotten over the years is "why doesn't `@apply hover:bg-black` work?"
And it's a fair question, it's stupid that it ~~doesn't~~ didn't work.
It took some serious engineering muscle but we figured it out — now you can `@apply` literally anything:
```css
.btn {
@apply bg-indigo-500 hover:bg-indigo-600 focus:ring-2 focus:ring-indigo-200 focus:ring-opacity-50;
}
```
Check out [the updated @apply documentation](https://tailwindcss.com/docs/functions-and-directives#apply) to learn more.
---
New text overflow utilities
Up until v2.0, if you wanted to control overflowing text all we really gave you was the fairly heavy-handed `truncate` utility.
Now 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.
```html
Look ma no whitespace-nowrap ipsum...
```
Check out the new [text overflow documentation](https://tailwindcss.com/docs/text-overflow) to see it in action.
---
Extend variants
You know what sucks? Wanting to enable `focus-visible` for `backgroundColor` but having to list _every single default variant_ just to add one extra one:
```js
// tailwind.config.js
module.exports = {
// ...
variants: {
backgroundColor: ['responsive', 'hover', 'focus', 'focus-visible'],
},
}
```
You know what's better? Just adding the one you want to enable:
```js
// tailwind.config.js
module.exports = {
// ...
variants: {
extend: {
backgroundColor: ['focus-visible'],
},
},
}
```
[Giddy up](https://tailwindcss.com/docs/configuring-variants#enabling-extra-variants).
---
Group-hover and focus-within by default
One 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.
Anywhere where we enabled hover or focus by default previously now has `group-hover` and `focus-within` enabled by default too:
```html
Da ba dee da ba daa
```
Check 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.
---
Default transition duration and easing curve
Until now, any time you wanted to add a transition in Tailwind you typically needed to add three classes:
```html
```
In 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:
```js
// tailwind.config.js
module.exports = {
// ...
theme: {
// ...
transitionDuration: {
DEFAULT: '150ms',
// ...
},
transitionTimingFunction: {
DEFAULT: 'cubic-bezier(0.4, 0, 0.2, 1)',
// ...
},
},
}
```
So now you only need to write a single class if you have a common duration and timing function that you use really frequently:
```html
```
Of course you can override this by layering on separate duration or timing function utilities:
```html
```
Learn more about transitions in the [transition property documentation](https://tailwindcss.com/docs/transition-property).
---
Incompatibility with IE11
We'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.
Dropping 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.
Cheers 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.
The 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.
---
So there you have it folks, that's Tailwind CSS v2.0 in a _(pretty big)_ nutshell!
What are you waiting for? [Go build something awesome](https://tailwindcss.com).
================================================
FILE: src/pages/utility-friendly-transitions-with-tailwindui-react/index.mdx
================================================
import { robinmalfait } from '@/authors'
import image from './card.jpg'
export const meta = {
title: 'Utility-Friendly Transitions with @tailwindui/react',
description: `Announcing the first release of @tailwindui/react, which introduces a component for utility-first enter/leave transitions.`,
date: '2020-08-27T18:30:00.000Z',
authors: [robinmalfait],
image,
discussion: 'https://github.com/tailwindlabs/tailwindcss/discussions/2262',
}
Back 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.
We 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.
One example of this is enter/leave transitions, like when you toggle a dropdown menu and see it fade in and out.
Vue.js has a really neat `` component for enter/leave transitions with a very utility-friendly API:
```html
```
But 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.
**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.**
We'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 `` 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.
Here's what it looks like to use:
```jsx
import { Transition } from '@tailwindui/react'
import { useState } from 'react'
function MyComponent() {
const [isOpen, setIsOpen] = useState(false)
return (
{/* Will fade in and out */}
)
}
```
[Read the documentation](https://github.com/tailwindlabs/tailwindui-react) to learn more about advanced functionality, like:
- Rendering without an extra DOM element
- Co-ordinating related transitions
- Transitioning on initial mount.
Check it out in action in this CodeSandbox demo:
Try it out on your projects, and if you run into any problems, [report an issue on GitHub](https://github.com/tailwindlabs/tailwindui-react).
================================================
FILE: src/pages/welcoming-brad-cornes-to-the-tailwind-team/index.mdx
================================================
import { adamwathan } from '@/authors'
import card from './card.jpg'
import intellisenseImage from '../introducing-linting-for-tailwindcss-intellisense/card.jpg'
export const meta = {
title: 'Welcoming Brad Cornes to the Team',
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?`,
date: '2020-07-18T15:43:02.988Z',
authors: [adamwathan],
image: card,
discussion: 'https://github.com/tailwindcss/tailwindcss/discussions/2040',
}
Back 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?
You 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!
Brad 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.
When 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.
Brad 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.
He'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.
Follow 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!
================================================
FILE: src/pages/welcoming-david-luhr-to-tailwind-labs/index.mdx
================================================
import { adamwathan } from '@/authors'
import card from './card.jpg'
export const meta = {
title: 'Welcoming David Luhr to Tailwind Labs',
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!`,
date: '2021-02-01T13:35:00.0Z',
authors: [adamwathan],
image: card,
footer: `
`,
}
We 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.
Today we're excited to share that David has joined the team full-time!
Watch David build some Tailwind UI components in Figma live on his YouTube channel!
David 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.
Say 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.
As 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.
One 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.
Trying 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.
Coming 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.
David 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.
================================================
FILE: src/pages/welcoming-james-mcdonald-to-tailwind-labs/index.mdx
================================================
import { adamwathan } from '@/authors'
import card from './card.jpg'
import jamesWorkExamples from './james-work.jpg'
import tailwindUiExamples from './tailwindui-work.jpg'
export const meta = {
title: 'Welcoming James McDonald to Tailwind Labs',
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.`,
date: '2021-03-08T19:00:00.0Z',
authors: [adamwathan],
image: card,
footer: `
`,
}
Many years ago I got a message from [Steve](https://twitter.com/steveschoger) 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](https://dribbble.com/james), and today we're totally over the moon to share that James is joining our team full-time.
Some of James' awesome work from his Dribbble profile.
James 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-)!
James 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 Tailwind UI when we wanted to add some fresh perspective last year.
Examples James designed for us for Tailwind UI.
We'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.
Can't wait to share some of the work we'll be creating together in the months to come!
================================================
FILE: src/pages/whats-new-in-tailwindcss-on-youtube/index.mdx
================================================
import { simonswiss } from '@/authors'
import image from './card.jpg'
export const meta = {
title: `"What's new in Tailwind CSS?" on YouTube`,
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.`,
date: '2020-09-23T18:30:00.000Z',
authors: [simonswiss],
image,
discussion: 'https://github.com/tailwindlabs/tailwindcss/discussions/2441',
}
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.
["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.
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.
["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:
- [CSS Grid](https://youtu.be/b-hrxkgkG-s)
- [Transitions](https://youtu.be/AYoQfPX31Mg)
- [Transforms](https://youtu.be/fZl6ufxmAqI)
- [Space-between utilities](https://youtu.be/wTRTC4JNSGs)
- [Divide utilities](https://youtu.be/XVmbVtO3tUU)
- [Color opacity](https://youtu.be/Zo1JtXnLDdA)
- [Built-in PurgeCSS support](https://youtu.be/ZNLeQDpE_8M)
- [Animations](https://youtu.be/75_a2lTJSjA)
- [Reduced motion variants](https://youtu.be/vnK7UrZ-IFs)
- [Gradients](https://youtu.be/1Rs5Kml8qMM)
- [Using `@apply` with complex classes](https://youtu.be/naJ_rIK6ppQ)
...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/).
And don't forget to like and subscribe.
================================================
FILE: tailwind.config.js
================================================
const defaultTheme = require('tailwindcss/defaultTheme')
const colors = require('tailwindcss/colors')
const mdx = require('@mdx-js/mdx')
module.exports = {
mode: 'jit',
purge: {
content: ['./src/**/*.{js,mdx}', './next.config.js'],
transform: {
mdx: mdx.sync,
},
},
theme: {
extend: {
fontFamily: {
sans: ['Inter var', ...defaultTheme.fontFamily.sans],
},
colors: {
teal: colors.cyan,
// for syntax highlighting
fuchsia: colors.fuchsia,
lime: colors.lime,
sky: colors.sky,
rose: colors.rose,
emerald: colors.emerald,
},
typography: (theme) => ({
DEFAULT: {
css: {
color: theme('colors.gray.700'),
h2: {
fontWeight: '700',
letterSpacing: theme('letterSpacing.tight'),
color: theme('colors.gray.900'),
},
h3: {
fontWeight: '600',
color: theme('colors.gray.900'),
},
'ol li:before': {
fontWeight: '600',
color: theme('colors.gray.500'),
},
'ul li:before': {
backgroundColor: theme('colors.gray.400'),
},
code: {
color: theme('colors.gray.900'),
},
a: {
color: theme('colors.gray.900'),
fontWeight: 400,
},
pre: {
color: theme('colors.white'),
backgroundColor: theme('colors.gray.800'),
},
blockquote: {
color: theme('colors.gray.900'),
borderLeftColor: theme('colors.gray.200'),
},
},
},
}),
},
},
plugins: [
require('@tailwindcss/aspect-ratio'),
require('@tailwindcss/typography'),
function ({ addBase }) {
addBase([
{
'@font-face': {
fontFamily: 'Inter var',
fontWeight: '100 900',
fontStyle: 'normal',
fontNamedInstance: 'Regular',
fontDisplay: 'swap',
src: 'url("/fonts/Inter-roman.var-latin.woff2?3.13") format("woff2")',
},
},
{
'@font-face': {
fontFamily: 'Inter var',
fontWeight: '100 900',
fontStyle: 'italic',
fontNamedInstance: 'Italic',
fontDisplay: 'swap',
src: 'url("/fonts/Inter-italic.var-latin.woff2?3.13") format("woff2")',
},
},
])
},
],
}