author

Tailwind CSS

All the latest Tailwind CSS news, straight from the team.

September 12, 2024  10:30:00

Over the past couple of months we’ve been working away at a new SaaS marketing site template for Tailwind UI. It’s called Radiant, and you can start using it today.

We just wrapped up work on a beautiful new SaaS marketing site template called Radiant, and it’s available now as part of Tailwind UI.

Learn about the Radiant template

It’s built with Next.js, Framer Motion, and Tailwind CSS, with a blog powered by Sanity.

It’s been a while since we built a SaaS marketing template like this, and in that time we’ve learned a lot about what makes a template like this useful and easy to work with. We’ve tried to incorporate all of those learnings into Radiant.

Check out the live preview as always for the full experience — there are tons of cool details in this one that you have to see in the browser to really appreciate.


Tastefully interactive

It’s super easy to overdo animation on a site like this. We’ve all seen sites where you can’t even scroll a few pixels without a bunch of different elements animating in to place. Even worse is how slow things feel when you have to wait for the content to appear before you can read it.

Radiant is loaded with delightful animations, but they are all layered on to existing content and triggered by user interaction so the site still feels fast. In most cases, we went for animations that loop to make elements feel “alive” while you’re interacting with them.

We used Framer Motion for almost all of the animations. It’s declarative, making it easy to create our own APIs for complex animations that other people can customize without much effort.

It does have some drawbacks to work around though. For example, when you have multiple elements animating independently it’s annoying to pass a hover state down to each child. We ended up leveraging Framer’s variant propagation to make this work — a hover event triggers a variant change in the parent that propagates down to the children because they share the same variant keys.

bento-card.tsx
export function BentoCard() {
  return (
    <motion.div
      initial="idle"
      whileHover="active"
      variants={{ idle: {}, active: {} }}
      data-dark={dark ? 'true' : undefined}
    >
      /* ... */
    </motion.div>
  )
}

There is no difference between the variants in the parent so it doesn’t actually change but the children still get a signal to change variants on hover, even if they are deeply nested.

map.tsx
function Marker({
  src,
  top,
  offset,
  delay,
}: {
  src: string
  top: number
  offset: number
  delay: number
}) {
  return (
    <motion.div
      variants={{
        idle: { scale: 0, opacity: 0, rotateX: 0, rotate: 0, y: 0 },
        active: { y: [-20, 0, 4, 0], scale: [0.75, 1], opacity: [0, 1] },
      }}
      transition={{ duration: 0.25, delay, ease: 'easeOut' }}
      style={{ '--offset': `${offset}px`, top } as React.CSSProperties}
      className="absolute left-[calc(50%+var(--offset))] size-[38px] drop-shadow-[0_3px_1px_rgba(0,0,0,.15)]"
    >
      /* ... */
    </motion.div>
  )
}

/* ... */

The logo timeline animation is a bit different, because we wanted the logos to pause in their current position when you stop hovering, rather than return to their original position. This doesn’t play very well with Framer’s approach of specifying start and end states, so it was actually easier to build this in CSS.

It exploits the fact that you can set a negative animation-delay value to offset the start position of the element. That way all the logos share the same animation keyframes but they can start at different positions and have different durations.

logo-timeline.tsx
function Logo({
  label,
  src,
  className,
}: {
  label: string
  src: string
  className: string
}) {
  return (
    <div
      className={clsx(
        className,
        'absolute top-2 grid grid-cols-[1rem,1fr] items-center gap-2 whitespace-nowrap px-3 py-1',
        'rounded-full bg-gradient-to-t from-gray-800 from-50% to-gray-700 ring-1 ring-inset ring-white/10',
        '[--move-x-from:-100%] [--move-x-to:calc(100%+100cqw)] [animation-iteration-count:infinite] [animation-name:move-x] [animation-play-state:paused] [animation-timing-function:linear] group-hover:[animation-play-state:running]',
      )}
    >
      <img alt="" src={src} className="size-4" />
      <span className="text-sm/6 font-medium text-white">{label}</span>
    </div>
  )
}

export function LogoTimeline() {
  return (
    /* ... */
    <Row>
      <Logo
        label="Loom"
        src="./logo-timeline/loom.svg"
        className="[animation-delay:-26s] [animation-duration:30s]"
      />
      <Logo
        label="Gmail"
        src="./logo-timeline/gmail.svg"
        className="[animation-delay:-8s] [animation-duration:30s]"
      />
    </Row>
    /* ... */

This approach means we don’t need to track the play state in JavaScript, we can just use a group-hover:[animation-play-state:running] class to start the animation when the parent is hovered.

As you’ve maybe noticed, we’re using a bunch of arbitrary properties for individual animation properties in this component, since these utilities don’t exist in Tailwind today. This is what’s great about building these templates — it helps us find blind spots in Tailwind CSS. Who knows, maybe we’ll see these utilities added for v4.0!


Deliberately reusable

The trickiest part of designing a SaaS template like this, is coming up with interactive elements that people can apply to their own product without too much effort. There’s nothing worse than buying a template and realizing that it’s so specific to the example content that you can’t actually use it for your own project.

We came up with some core graphical elements that most SaaS products might have. A map with pins, a logo cluster, a keyboard — things that could be applied to a bunch of different features. Because we wanted them to be easy to repurpose for your own product, we built a lot of them in code and designed nice APIs for them.

The logo cluster, for example, has a simple API that lets you pass in your own logos, tweak their position and hover animation to match.

<Logo
  src="./logo-cluster/dribbble.svg"
  left={285}
  top={20}
  hover={{ x: 4, y: -5, rotate: 6, delay: 0.3 }}
/>

The keyboard shortcuts section is another good example. Adding your own shortcuts is as simple as passing an array of key names to the Keyboard component and because each key is a component, you can easily add custom keys or change the layout.

<Keyboard highlighted={['F', 'M', 'L']} />

It turns out it’s actually quite a lot of work to build a keyboard in code, but at least now you’ll never have to find that out for yourself.

Of course, we also left spots for you to drop in screenshots of your own product. Here’s what this section looks like customized to suit our friends at SavvyCal, using the same interactive components.

Radiant as SavvyCal

Powered by a CMS

Usually we just use MDX when adding a blog to a template, but this time we thought it would be fun to play with a headless CMS for a chance instead. We decided to give Sanity a go for this one after polling our audience and hearing a lot of good things.

Instead of creating files, making commits, and managing images and stuff by hand, a CMS lets you handle everything from their UI, so even non-developers can easily contribute.

Sanity Studio

One cool thing about headless CMSes like Sanity is you get your content back in a structured format, so similar to MDX you can map elements to your own custom components to handle all of your typography styles.

<PortableText
  value={post.body}
  components={{
    block: {
      normal: ({ children }) => (
        <p className="my-10 text-base/8 first:mt-0 last:mb-0">
          {children}
        </p>
      ),
      h2: ({ children }) => (
        <h2 className="mb-10 mt-12 text-2xl/8 font-medium tracking-tight text-gray-950 first:mt-0 last:mb-0">
          {children}
        </h2>
      ),
      h3: ({ children }) => (
        <h3 className="mb-10 mt-12 text-xl/8 font-medium tracking-tight text-gray-950 first:mt-0 last:mb-0">
          {children}
        </h3>
      ),
      blockquote: ({ children }) => (
        <blockquote className="my-10 border-l-2 border-l-gray-300 pl-6 text-base/8 text-gray-950 first:mt-0 last:mb-0">
          {children}
        </blockquote>
      ),
    },
    types: {
      image: ({ value }) => (
        <img
          className="w-full rounded-2xl"
          src={image(value).width(2000).url()}
          alt={value.alt || ''}
        />
      ),
    },
    /* ... */
  }}
/>

Working with a CMS also means all of your assets like images are hosted for you, and you can control the size, quality, and format of the image on the fly.

<div className="text-sm/5 max-sm:text-gray-700 sm:font-medium">
  {dayjs(post.publishedAt).format('dddd, MMMM D, YYYY')}
</div>
{post.author && (
  <div className="mt-2.5 flex items-center gap-3">
    {post.author.image && (
      <img
       className="aspect-square size-6 rounded-full object-cover"
       src={image(post.author.image).width(64).height(64).url()}
       alt=""
      />
    )}
    <div className="text-sm/5 text-gray-700">
      {post.author.name}
    </div>
  </div>
)}

Like you might do with front matter in Markdown, you can also enrich content with custom fields. For example, we added a featured boolean field to the blog post schema so you can highlight some posts in a special section on the blog.

Radiant Blog

Sanity in particular is a paid product, but they have a pretty generous free tier which is more than enough to play around. And if you wanted to try out a different headless CMS, I think the Sanity integration we’ve put together here will still serve as a great informative example of how you might approach wiring things up with another tool.


And that’s Radiant! Have a look under the hood, kick the tires, and let us know what you think.

Like all of our templates, it’s included with a one-time purchase Tailwind UI all-access license, which is the best way to support our work on Tailwind CSS and make it possible for us to keep building awesome stuff for you for years to come.

(The post Radiant: A beautiful new marketing site template appeared first on Tailwind CSS Blog.)

June 21, 2024  15:00:00

We just released Headless UI v2.1 for React, which dramatically simplifies our transition APIs and adds support for rendering multiple dialogs as siblings.

Headless UI v2.1

We just released Headless UI v2.1 for React, which dramatically simplifies our transition APIs and adds support for rendering multiple dialogs as siblings.


Simplified transition API

We’ve made transitions way easier in v2.1 by adding a new transition prop to all of the built-in components you might want to transition, and adding data attributes for each transition stage so you can add transition styles by just throwing some classes on the target element:

import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'

function Example() {
  return (
    <Menu>
      <MenuButton>My account</MenuButton>
      <MenuItems
        transition
        className={`
          transition ease-out
          data-[closed]:scale-95 data-[closed]:opacity-0
          data-[enter]:duration-200 data-[leave]:duration-300
        `}
      >
        {/* Menu items… */}
      </MenuItems>
    </Menu>
  )
}

There are four data attributes you can use to target the different stages of your transitions:

  • data-closed: The styles the element should transition from when entering and to when leaving.
  • data-enter: Styles to apply while the element is entering, like a duration or easing curve.
  • data-leave: Styles to apply while the element is leaving, like a duration or easing curve.
  • data-transition: Styles to apply while the element is entering or leaving, useful for sharing values between both stages.

You can even stack these attributes to use different closed styles for entering and leaving. For example this dialog slides in from the left, but slides out to the right:

import { Dialog } from '@headlessui/react'
import { useState } from 'react'

function Example() {
  let [isOpen, setIsOpen] = useState(false)

  return (
    <>
      <button onClick={() => setIsOpen(true)}>Open dialog</button>
      <Dialog
        open={isOpen}
        onClose={() => setIsOpen(false)}
        transition
        className={`
          transition ease-out duration-300
          data-[closed]:opacity-0
          data-[closed]:data-[enter]:-translate-x-8
          data-[closed]:data-[leave]:translate-x-8
        `}
      >
        {/* Dialog content… */}
      </Dialog>
    </>
  )
}

And for transitioning regular HTML elements or other components, you can still use the <Transition> component but with the new data attribute APIs:

import { Transition } from '@headlessui/react'
import { useState } from 'react'

function Example() {
  const [isShowing, setIsShowing] = useState(false)

  return (
    <>
      <button onClick={() => setIsShowing((isShowing) => !isShowing)}>Toggle</button>
      <Transition show={isShowing}>
        <div className="transition duration-300 data-[closed]:opacity-0">
          I will fade in and out
        </div>
      </Transition>
    </>
  )
}

We’ve already updated all of Tailwind UI to use this new transition API and the code is a lot simpler and lighter. Take a look at the Modal Dialog, Dropdown, Slide-over, Flyout Menu, or Select Menu components for more examples.

All of the existing APIs continue to work for backwards compatibility, but this new approach is what we’re going to recommend going forward.

Check out the updated Transition component documentation to learn more.


Rendering multiple dialogs as siblings

In Headless UI v2.1 you can finally render multiple dialogs at the same time without nesting one inside the other.

This can be really helpful when two unrelated parts of your application need to show a dialog at the same time — for example maybe you already have some sort of confirmation dialog open but another part of your app detects that you’ve lost network connectivity or your session has timed-out and needs to throw up a new dialog on top.

Here’s what something like that might look like with Catalyst, the application UI kit we’ve been working on recently:

We keep track of the order in which each dialog is opened, and whichever one was opened last is the one that will close when you press escape or click outside the dialog.


To start using this stuff today, just install the latest version of Headless UI:

$ npm i @headlessui/react@latest

If you run into any issues, let us know on GitHub!

(The post Headless UI v2.1: Simplified transition API and improved multi-dialog support appeared first on Tailwind CSS Blog.)

May 31, 2024  16:30:00

We just released a new version of prettier-plugin-tailwindcss which adds support for removing unnecessary whitespace and duplicate classes when sorting.

Catalyst application layout preview

We just released a new version of prettier-plugin-tailwindcss which adds support for removing unnecessary whitespace and duplicate classes when sorting.


Cleaning up unnecessary whitespace

When you’re copying around class names or deleting a class name from the middle of a list, it’s easy to end up with some extra whitespace that needs to be cleaned up.

Now our Prettier plugin will handle this for you automatically, so you don’t need to clean it up yourself.

function MyComponent({ children }) {
  return (
    <div className=" mx-auto  max-w-7xl px-6 lg:px-8 ">
      {children}
    </div>
  )
}

Removing duplicate classes

Our VS Code extension has warned you about duplicate class names for a long time, but now our Prettier plugin can remove those duplicate classes for you automatically.

function MyComponent({ children }) {
  return (
    <div className="flex bg-zinc-100 bg-zinc-100 px-4">
      {children}
    </div>
  )
}

To start playing with these improvements in your own projects, just install the latest version:

$ npm i prettier-plugin-tailwindcss@latest

If you run into any issues, let us know on GitHub!

(The post Automatically clean up whitespace and duplicate class names appeared first on Tailwind CSS Blog.)

May 24, 2024  20:00:00

We just published the first major update to Catalyst since releasing the development preview, with two new application layouts, navbar and sidebar components, description lists, and more.

Catalyst application layout preview

We just published the first major update to Catalyst since releasing the development preview, with two new application layouts, navbar and sidebar components, description lists, and more.

We’re also pumped to share that with the release of Headless UI v2.0 for React, Catalyst is no longer in development preview — it’s officially stable and you can start using it in production today without worrying about breaking changes in the underlying dependencies.

Check out our brand new live demo site to see what a full Catalyst project looks and feels like after these updates for yourself.


New application layout components

One of the hardest things about trying to get started on a new project idea is getting past the blank canvas so you can actually start building something.

In this update we’ve added two new application layout components to make it easy to give your project a shape and structure so you have something you can start building with.

The first layout is a classic sidebar layout, that moves the sidebar into a collapsible mobile menu on smaller screens:

Sidebar layout example
import { SidebarLayout } from '@/components/sidebar-layout'
import { Navbar } from '@/components/navbar'
import { Sidebar } from '@/components/sidebar'

function Example({ children }) {
  return (
    <SidebarLayout
      sidebar={<Sidebar>{/* Sidebar menu */}</Sidebar>}
      navbar={<Navbar>{/* Navbar for mobile screens */}</Navbar>}
    >
      {/* Your page content */}
    </SidebarLayout>
  )
}

The second is a simpler stacked layout with a horizontal navigation menu, which is often a great fit for apps with fewer pages:

Stacked layout example
import { StackedLayout } from '@/components/stacked-layout'
import { Navbar } from '@/components/navbar'
import { Sidebar } from '@/components/sidebar'

function Example({ children }) {
  return (
    <StackedLayout
      navbar={<Navbar>{/* Top navigation menu */}</Navbar>}
      sidebar={<Sidebar>{/* Sidebar content for mobile menu */}</Sidebar>}
    >
      {/* Your page content */}
    </StackedLayout>
  )
}

And they both support dark mode too, of course:

Sidebar layout in dark mode

We worked really hard to get the APIs for all of these components right, making it easy to position things where you need them to be, optionally include icons, incorporate dropdown menus, and more.

The final result turned out feeling really simple which is exactly what we were going for, and I think you’ll find they are a real delight to build with.

Check out the Sidebar layout documentation and Stacked layout documentation to get started, then dig into the Navbar and Sidebar components to learn how to structure all of the navigation items.


Description lists

When we were working on the application layouts we realized we didn’t have any great content to demo them with, so we cooked up a DescriptionList component to fill in that big empty space.

Customer

Michael Foster

Event

Bear Hug: Live in Concert

Amount

$150.00 USD

Amount after exchange rate

US$150.00 → CA$199.79

Fee

$4.79 USD

Net

$1,955.00

import { DescriptionDetails, DescriptionList, DescriptionTerm } from '@/components/description-list'

function Example() {
  return (
    <DescriptionList>
      <DescriptionTerm>Customer</DescriptionTerm>
      <DescriptionDetails>Michael Foster</DescriptionDetails>

      <DescriptionTerm>Event</DescriptionTerm>
      <DescriptionDetails>Bear Hug: Live in Concert</DescriptionDetails>

       {/* ... */}
    </DescriptionList>
  )
}

It’s a really simple API that works just like the HTML <dl> element, but is nicely styled, responsive, and with dark mode support of course.

Check out the Description list documentation for more details.


Page headings

More components we needed to make the demo look good! We’ve added Heading and Subheading components you can use to quickly and consistently title things in your UI.

Heading

Subheading

import { Heading, Subheading } from '@/components/heading'

function Example() {
  return (
    <div>
      <Heading>Heading</Heading>
      <Subheading>Subheading</Subheading>
    </div>
  )
}

You can control which HTML heading element is rendered using the level prop, and like everything else, they’re responsive with built-in dark mode support.

See the Heading documentation for more examples.


Dividers

Saved the best for last — Catalyst now includes a gray line you can put in between things.


import { Divider } from '@/components/divider'

function Example() {
  return <Divider />
}

We worked tirelessly on this one, and are so proud to make this part of your application development process easier.

Check out the Divider documentation — it does have one prop at least.


Catalyst is included with your Tailwind UI all-access license at no additional cost, so if you’ve got a license, log in and download the latest version to start building.

Looking forward to seeing what you do with it!

(The post Catalyst: Application layouts, navigation menus, description lists, and more appeared first on Tailwind CSS Blog.)

May 7, 2024  16:00:00

Nothing beats actually building something real with your own tools when it comes to finding ways to make things better. As we’ve been working on Catalyst these last several months, we’ve been making dozens of improvements to Headless UI that let you write even less code, and make the developer experience even better.

Headless UI v2.0

Nothing beats actually building something real with your own tools when it comes to finding ways to make things better.

As we’ve been working on Catalyst these last several months, we’ve been making dozens of improvements to Headless UI that let you write even less code, and make the developer experience even better.

We just released Headless UI v2.0 for React, which is the culmination of all this work.

Here’s all the best new stuff:

Add it to your project by installing the latest version of @headlessui/react from npm:

npm install @headlessui/react@latest

If you’re upgrading from v1.x, check out the upgrade guide to learn more about what’s changed.


Built-in anchor positioning

We’ve integrated Floating UI directly into Headless UI, so you never have to worry about dropdowns going out of view or being obscured by other elements on the screen.

Use the new anchor prop on the Menu, Popover, Combobox, and Listbox components to specify the anchor positioning, then fine-tune the placement with CSS variables like --anchor-gap and --anchor-padding:

Scroll up and down to see the dropdown position change

import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'

function Example() {
  return (
      <Menu>
        <MenuButton>Options</MenuButton>
        <MenuItems
          anchor="bottom start"
          className="[--anchor-gap:8px] [--anchor-padding:8px]"
        >
          <MenuItem>
            <button>Edit</button>
          </MenuItem>
          <MenuItem>
            <button>Duplicate</button>
          </MenuItem>
          <hr />
          <MenuItem>
            <button>Archive</button>
          </MenuItem>
          <MenuItem>
            <button>Delete</button>
          </MenuItem>
        </MenuItems>
      </Menu>
  )
}

What makes this API really nice is that you can tweak the styles at different breakpoints by changing the CSS variables using utility classes like sm:[--anchor-gap:4px].

Check out the anchor positioning documentation for each component for all of the details.


New checkbox component

We’ve added a new headless Checkbox component to complement our existing RadioGroup component, making it easy to build totally custom checkbox controls:

This will give you early access to any awesome new features we're developing.

import { Checkbox, Description, Field, Label } from '@headlessui/react'
import { CheckmarkIcon } from './icons/checkmark'
import clsx from 'clsx'

function Example() {
  return (
    <Field>
      <Checkbox
        defaultChecked
        className={clsx(
          'size-4 rounded border bg-white dark:bg-white/5',
          'data-[checked]:border-transparent data-[checked]:bg-blue-500',
          'focus:outline-none data-[focus]:outline-2 data-[focus]:outline-offset-2 data-[focus]:outline-blue-500',
        )}
      >
        <CheckmarkIcon className="stroke-white opacity-0 group-data-[checked]:opacity-100" />
      </Checkbox>
      <div>
        <Label>Enable beta features</Label>
        <Description>
          This will give you early access to any awesome new features we're developing.
        </Description>
      </div>
    </Field>
  )
}

Checkboxes can be controlled or uncontrolled, and can automatically sync their state with a hidden input to play nicely with HTML forms.

Take a look at the Checkbox documentation to learn more.


HTML form components

We’ve added a whole new set of components that just wrap native form controls, but do all of the tedious work of wiring up IDs and aria-* attributes for you automatically.

Here’s what it looked like to build a simple <input> field with a properly associated <label> and description before:

<div>
  <label id="name-label" for="name-input">Name</label>
  <input
    id="name-input"
    aria-labelledby="name-label"
    aria-describedby="name-description"
  />
  <p id="name-description">Use your real name so people will recognize you.</p>
</div>

And here’s what it looks like with these new components in Headless UI v2.0:

import { Description, Field, Input, Label } from '@headlessui/react'

function Example() {
  return (
    <Field>
      <Label>Name</Label>
      <Input name="your_name" />
      <Description>Use your real name so people will recognize you.</Description>
    </Field>
  )
}

The new Field and Fieldset components also cascade disabled states like the native <fieldset> element, so you can easily disable an entire group of controls at once:

Select a country to see the region field become enabled

Shipping details

We currently only ship to North America.

import { Button, Description, Field, Fieldset, Input, Label, Legend, Select } from '@headlessui/react'
import { regions } from './countries'

export function Example() {
  const [country, setCountry] = useState(null)

  return (
    <form action="/shipping">
      <Fieldset>
        <Legend>Shipping details</Legend>
        <Field>
          <Label>Street address</Label>
          <Input name="address" />
        </Field>
        <Field>
          <Label>Country</Label>
          <Description>We currently only ship to North America.</Description>
          <Select
            name="country"
            value={country}
            onChange={(event) => setCountry(event.target.value)}
          >
            <option></option>
            <option>Canada</option>
            <option>Mexico</option>
            <option>United States</option>
          </Select>
        </Field>
        <Field disabled={!country}>
          <Label className="data-[disabled]:opacity-40">State/province</Label>
          <Select name="region" className="data-[disabled]:opacity-50">
            <option></option>
            {country && regions[country].map((region) => <option>{region}</option>)}
          </Select>
        </Field>
        <Button>Submit</Button>
      </Fieldset>
    </form>
  )
}

We expose the disabled state using a data-disabled attribute in the rendered HTML. This lets us expose it even on elements that don’t support the native disabled attribute like the associated <label> element, making it really easy to fine-tune the disabled styles for each element.

All in all we’ve added 8 new components here — Fieldset, Legend, Field, Label, Description, Input, Select, and Textarea.

For more details, start with the Fieldset documentation and work your way through the rest.


Improved hover, focus and active state detection

Using hooks from the awesome React Aria library under the hood, Headless UI now adds smarter data-* state attributes to your controls that behave more consistently across different devices than the native CSS pseudo-classes:

  • data-active — like :active, but is removed when dragging off of the element.
  • data-hover — like :hover, but is ignored on touch devices to avoid sticky hover states.
  • data-focus — like :focus-visible, without false positives from imperative focusing.

Click, hover, focus, and drag the button to see the data attributes applied

<!-- Rendered `Button` -->
<button class="bg-indigo-600 data-[active]:bg-indigo-700 data-[hover]:bg-indigo-500 data-[focus]:outline ...">
  Submit
</button>

To learn more about why applying these styles using JavaScript is important, I highly recommend reading through Devon Govett’s excellent blog series on this topic:

The web never ceases to surprise me with the amount of effort it takes to actually make nice things.


Combobox list virtualization

We’ve integrated TanStack Virtual into Headless UI to support list virtualization when you need to put a hundred thousand items in your combobox because, hey, that’s what the boss told you to do.

Use the new virtual prop to pass in all of your items, and use the ComboboxOptions render prop to provide the template for an individual option:

Open the combobox and scroll through the 1,000 options

import { Combobox, ComboboxButton, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
import { ChevronDownIcon } from '@heroicons/react/20/solid'
import { useState } from 'react'

const people = [
  { id: 1, name: 'Rossie Abernathy' },
  { id: 2, name: 'Juana Abshire' },
  { id: 3, name: 'Leonel Abshire' },
  { id: 4, name: 'Llewellyn Abshire' },
  { id: 5, name: 'Ramon Abshire' },
  // ...up to 1000 people
]

function Example() {
  const [query, setQuery] = useState('')
  const [selected, setSelected] = useState(people[0])

  const filteredPeople =
    query === ''
      ? people
      : people.filter((person) => {
          return person.name.toLowerCase().includes(query.toLowerCase())
        })

  return (
    <Combobox
      value={selected}
      virtual={{ options: filteredPeople }}
      onChange={(value) => setSelected(value)}
      onClose={() => setQuery('')}
    >
      <div>
        <ComboboxInput
          displayValue={(person) => person?.name}
          onChange={(event) => setQuery(event.target.value)}
        />
        <ComboboxButton>
          <ChevronDownIcon />
        </ComboboxButton>
      </div>
      <ComboboxOptions>
        {({ option: person }) => (
          <ComboboxOption key={person.id} value={person}>
            {person.name}
          </ComboboxOption>
        )}
      </ComboboxOptions>
    </Combobox>
  )
}

Check out the new virtual scrolling documentation to learn more.


New website and improved docs

To go along with this major release, we’ve also significantly revamped the documentation and given the website a fresh coat of paint:

New Headless UI v2.0 website

Head over to the new headlessui.com to check it out!

(The post Headless UI v2.0 for React appeared first on Tailwind CSS Blog.)

March 19, 2024  18:00:00

We’re hiring a Design Engineer and Staff Software Engineer to work on some ambitious new projects with us. Both roles are fully remote, with a salary of $275,000 USD.

We’re hiring a Design Engineer and Staff Software Engineer to work on some ambitious new projects with us.

We’re small on purpose, and we take a lot of pride in punching above our weight. We’re six people, but Tailwind CSS is installed over 30 million times per month, and is used by the world’s biggest companies to build the world’s best websites.

We’re independent and profitable, and we do this because it’s fun. We’re in this to enjoy the actual work we do every day, not to grind it out in hopes of a big exit in the future.

If this sounds like the sort of place you’d like to work, we’d love to hear from you.


Design Engineer

We’re hiring a Design Engineer to build ambitious interfaces, prototype new ideas, and push the boundaries of what’s possible with Tailwind CSS.

Design engineer

You’d be responsible for things like:

  • Design and build ambitious marketing websites for our open-source projects, commercial products, and events like Tailwind Connect.
  • Design and prototype new features for Tailwind CSS to make sure we’re always using the full potential of the platform.
  • Create new components and templates for Tailwind UI, taking them all the way from initial concept to shipped.
  • Enhance our documentation with visual demos to make it easy for people to understand and apply complex CSS features in their work.
  • Teach and inspire our audience by breaking down interesting things you design and build as articles and social media posts.

Here are some real examples of projects you would have worked on in the last few months:

  • Design and build the Tailwind Connect microsite — including coming up with a unique Markdown-driven badge design experience.
  • Craft the first components for Catalyst — our first fully-componentized React UI kit.
  • Build a tool for capturing videos for our Showcase site — to automate creating preview videos that feel like a real person scrolling through a site.
  • Build interactive demos to explain dynamic viewport units — to include in the documentation along with the release of Tailwind CSS v3.4.

After you start, you’d work on upcoming projects like:

  • Design and build an interactive microsite for the Tailwind CSS v4.0 release with thoughtfully crafted demos that communicate the most important improvements in a visual way.
  • Prototype APIs for scroll-driven animations in Tailwind CSS, finding the right balance between simplicity and flexibility, and making our users feel like they have superpowers.
  • Explore new color palettes with automatic dark mode support, carefully figuring out the right abstractions for different levels of hierarchy that apply universally to different projects.
  • Research and design text-shadow support for Tailwind CSS, finally.

This is a fully remote position with a salary of $275,000 USD, open to candidates in the Eastern (UTC-5) to Central European (UTC+1) timezones.

Learn more about this role and apply →


Staff Software Engineer

We’re hiring a Staff Software Engineer to work on Tailwind CSS, Headless UI, and our supporting ecosystem of tools like the Tailwind CSS IntelliSense extension for VS Code.

Staff engineer

You’d have a wide range of responsibilities, including:

  • Build and document new features for projects like Tailwind CSS and Headless UI.
  • Contribute improvements to upstream projects like Lightning CSS, Next.js, and Vite.
  • Maintain CI workflows and automations for running tests and building releases.
  • Manage issues and contributions from the community to our open-source repositories.
  • Define and uphold engineering standards with a focus on code quality, performance, and documentation, to make sure we’re always shipping work we can be proud of.
  • Manage project priorities and adjust scope to meet project deadlines without sacrifing on top-level goals.

Here are some real examples of projects you would have worked on in the last few months:

  • Create a Rust library for extracting utility class names — a tool that can scan an entire directory of template files as fast as possible and extract potential class names in parallel.
  • Write a fast CSS parser in TypeScript — to parse only the exact amount of detail needed by Tailwind’s internals, looking for domain-specific shortcuts to make it significantly faster than existing libraries for our needs.
  • Fix an issue with scroll-locking in Headless UI — to make sure scrollable elements in dialogs can still be scrolled on iOS.
  • Campaign for new APIs we need in Vite — prepare a compelling argument for improvements we need to support Tailwind CSS and collaborate with the Vite core team to prototype a solution.

After you start, you’d work on upcoming projects like:

  • Develop a Tailwind CSS bundler plugin for Next.js — to generate your CSS using the module graph instead of scanning the file system, working with our contacts at Vercel to get it right.
  • Design a set of IDE integration APIs for Tailwind CSS — first-class, stable, documented APIs that editor teams like JetBrains can rely on to add things like completions, lint warnings, class sorting, and more to their tools.
  • Design, build, and document a Tooltip component for Headless UI — making sure it’s fully accessible, with a flexible but delightful API.
  • Build a backwards compatibility layer for Tailwind CSS v4.0 — re-introduce support for JavaScript configuration files, explore codemod tooling, and make sure existing community plugins are as compatible as possible with the new engine.

This is a fully remote position with a salary of $275,000 USD, open to candidates in the Eastern (UTC-5) to Central European (UTC+1) timezones.

Learn more about this role and apply →

(The post We're hiring a Design Engineer + Staff Engineer appeared first on Tailwind CSS Blog.)

March 6, 2024  16:30:00

Last summer at Tailwind Connect I shared a preview of Oxide — a new high-performance engine for Tailwind CSS that we’ve been working on, designed to simplify the developer experience and take advantage of how the web platform has evolved since Tailwind was first released.

Tailwind CSS v4.0-alpha

Last summer at Tailwind Connect I shared a preview of Oxide — a new high-performance engine for Tailwind CSS that we’ve been working on, designed to simplify the developer experience and take advantage of how the web platform has evolved in recent years.

The new engine was originally going to ship as a v3.x release, but even though we’re committed to backwards compatibility, this feels so clearly like a new generation of the framework that it deserves to be v4.0.

It’s still early and we’ve got a lot of work to do, but today we’re open-sourcing our progress and tagging the first public v4.0.0-alpha so you can start experimenting with it and help us get to a stable release later this year.

I’ll try keep it brief to save some of the excitement for the stable release, but if you like to play with very early and experimental stuff, there should be plenty of information here to get you going.


A new engine, built for speed

The new engine is a ground-up rewrite, using everything we know about the framework now to better model the problem space, making things faster with a lot less code.

  • Up to 10x faster — we can do a full build of the Tailwind CSS website in 105ms instead of 960ms, or our Catalyst UI kit in 55ms instead of 341ms.
  • Smaller footprint — the new engine is over 35% smaller installed, even with the heavier native packages we ship like the parts we’ve rewritten in Rust and Lightning CSS.
  • Rust where it counts — we’ve migrated some of the most expensive and parallelizable parts of the framework to Rust, while keeping the core of the framework in TypeScript for extensibility.
  • One dependency — the only thing the new engine depends on is Lightning CSS.
  • Custom parser — we wrote our own CSS parser and designed our own data structures tailored to our needs, making parsing over 2x as fast for us as it was with PostCSS.

Unified toolchain

Tailwind CSS v4 isn’t just a plugin anymore — it’s an all-in-one tool for processing your CSS. We’ve integrated Lightning CSS directly into the framework so you don’t have to configure anything about your CSS pipeline.

  • Built-in @import handling — no need to setup and configure a tool like postcss-import.
  • Built-in vendor prefixing — you don’t have to add autoprefixer to your projects anymore.
  • Built-in nesting support — no plugins needed to flatten nested CSS, it works out of the box.
  • Syntax transforms — modern CSS features like oklch() colors and media query ranges are transpiled to syntax with better browser support.

We’re still shipping a PostCSS plugin, but we’re also exploring first-party bundler plugins, and we’re shipping an official Vite plugin with this first alpha release that you can try out today.


Designed for the modern web

We’re looking into the future with Tailwind CSS v4 and trying to build a framework that’s going to feel cutting edge for years to come.

  • Native cascade layers — we’re using real @layer rules now, which solves a ton of specificity problems we’ve wrestled with in the past.
  • Explicitly defined custom properties — we use @property to define our internal custom properties with proper types and constraints, making it possible to do things like transition background gradients.
  • Using color-mix for opacity modifiers — making it easier than ever to use our opacity modifier syntax when using CSS variables for colors, or even adjusting the opacity of currentColor.
  • Container queries in core — we’ve added support for container queries directly to core, with new @min-* and @max-* variants to support container query ranges.

We’re also working on refreshing our color palette with wide gamut colors, and introducing support for other modern CSS features like @starting-style, anchor positioning, and more.


Composable variants

The new architecture makes it possible to compose together variants that act on other selectors, like group-*, peer-*, has-*, and a new not-* variant we’re introducing for v4.

In earlier releases, variants like group-has-* were explicitly defined in the framework, but now group-* can compose with the existing has-* variant, which can compose with other variants like focus:

index.html
<div class="group">
  <div class="group-has-[&:focus]:opacity-100">
  <div class="group-has-focus:opacity-100">
    <!-- ... -->
  </div>
</div>

There’s no limits to this composability, and you can even write stuff like group-not-has-peer-not-data-active:underline if for some horrible reason that’s what you need to do.


Zero-configuration content detection

You’ll notice that at least in these early alpha releases, it’s not even possible to configure your content paths. For most projects, you’re never going to need to do this ever again — Tailwind just finds your template files for you.

We do this using one of two ways depending on how you’ve integrated Tailwind into your project:

  • Using the PostCSS plugin or the CLI, Tailwind will crawl your entire project looking for template files, using a bunch of heuristics we’ve built in to keep things fast, like not crawling directories that are in your .gitignore file, and ignoring binary file formats.

  • Using the Vite plugin, we rely on the module graph. This is amazing because we know exactly what files you’re actually using, so it’s maximally performant, and with no false positives or negatives. We’re hoping to expand this approach outside of the Vite ecosystem with other bundler plugins in the future.

We’ll introduce a way to configure content paths explicitly in the future for sure, but we’re curious to see how well this automatic approach works for everyone — it’s working awesome in our own projects.


CSS-first configuration

A major goal of Tailwind CSS v4.0 is making the framework feel CSS-native, and less like a JavaScript library.

Once you’ve installed it, you add it to your project with a regular CSS @import statement:

main.css
@import "tailwindcss";

And instead of setting up all of your customizations in a JavaScript configuration file, you just use CSS variables:

main.css
@import "tailwindcss";

@theme {
  --font-family-display: "Satoshi", "sans-serif";

  --breakpoint-3xl: 1920px;

  --color-neon-pink: oklch(71.7% 0.25 360);
  --color-neon-lime: oklch(91.5% 0.258 129);
  --color-neon-cyan: oklch(91.3% 0.139 195.8);
}

The special @theme directive tells Tailwind to make new utilities and variants available based on those variables, letting you use classes like 3xl:text-neon-lime in your markup:

index.html
<div class="max-w-lg 3xl:max-w-xl">
  <h1 class="font-display text-4xl">
    Data to <span class="text-neon-cyan">enrich</span> your online business
  </h1>
</div>

Adding new CSS variables behaves like extend did in earlier versions of the framework, but you can override a whole set of variables by clearing the namespace with syntax like --color-*: initial before defining all of your custom values:

main.css
@import "tailwindcss";

@theme {
  --color-*: initial;

  --color-gray-50: #f8fafc;
  --color-gray-100: #f1f5f9;
  --color-gray-200: #e2e8f0;
  /* ... */
  --color-green-800: #3f6212;
  --color-green-900: #365314;
  --color-green-950: #1a2e05;
}

We’re still fine-tuning some of the naming conventions, but you can explore the default theme on GitHub to see what’s available to customize.

If you don’t want to explicitly clear the default theme and would rather start from scratch, you can import "tailwindcss/preflight" and "tailwindcss/utilities" directly to skip importing the default theme:

main.css
@import "tailwindcss";
@import "tailwindcss/preflight" layer(base);
@import "tailwindcss/utilities" layer(utilities);

@theme {
  --color-*: initial;
  --color-gray-50: #f8fafc;
  --color-gray-100: #f1f5f9;
  --color-gray-200: #e2e8f0;
  /* ... */
  --color-green-800: #3f6212;
  --color-green-900: #365314;
  --color-green-950: #1a2e05;
}

We also make all of your theme values available as native CSS variables in your custom CSS:

dist/main.css
:root {
  --color-gray-50: #f8fafc;
  --color-gray-100: #f1f5f9;
  --color-gray-200: #e2e8f0;
  /* ... */
  --color-green-800: #3f6212;
  --color-green-900: #365314;
  --color-green-950: #1a2e05;
}

This makes it easy to reference any of your theme values in arbitrary values without needing the theme() function:

index.html
<div class="p-[calc(var(--spacing-6)-1px)]">
  <!-- ... -->
</div>

It also makes it possible to use your theme values when working with UI libraries like Framer Motion, without having to use the resolveConfig() function:

JSX
import { motion } from "framer-motion"

export const MyComponent = () => (
  <motion.div
    initial={{ y: 'var(--spacing-8)' }}
    animate={{ y: 0 }}
    exit={{ y: 'var(--spacing-8)' }}
  >
    {children}
  </motion.div>
)

What’s changed

We don’t take breaking changes lightly, but there are a few things we’re doing differently in v4 so far that are worth sharing:

  • Removed deprecated utilities — we’ve removed utilities we stopped documenting a long time ago like text-opacity-*, flex-grow-*, and decoration-slice in favor of their modern replacements like text-{color}/*, grow-*, and box-decoration-slice.
  • PostCSS plugin and CLI are separate packages — the main tailwindcss package doesn’t include these anymore since not everyone needs them, instead they should be installed separately using @tailwindcss/postcss and @tailwindcss/cli.
  • No default border color — the border utility used to default to gray-200, but now it defaults to currentColor like the browser does. We made this change to make it harder to accidentally introduce a wrong gray into your project if you’re using zinc or slate or something else as your main gray.
  • Rings are 1px by default — the ring utility used to be a 3px blue ring by default, now it’s a 1px ring using currentColor. We find ourselves using the ring-* utilities as an alternative to borders in our projects, and using outline-* for focus rings, so we think making things consistent here is a helpful change.

There are a handful of other really low-level implementation detail changes that might surface in some way in your projects, but nothing deliberate like these changes. If you bump into anything surprising, let us know.


Roadmap to v4.0

This new engine is a ground-up rewrite, and up until now we’ve been focused entirely on this reimagined developer experience using the new configuration approach.

We put an enormous amount of value in backwards compatibility, and that’s where the bulk of the work lies before we can tag a stable v4.0 release later this year.

  • Support for JavaScript configuration files — reintroducing compatibility with the classic tailwind.config.js file to make migrating to v4 easy.
  • Explicit content path configuration — making it possible to tell Tailwind exactly where your templates are when automatic content detection isn’t good enough for your setup.
  • Support for other dark modes — right now we only support dark mode using media queries, and still need to reimplement the selector and variant strategies.
  • Plugins and custom utilities — we don’t have support for plugins, or for writing custom utilities that automatically work with variants yet. Obviously we will make this work before a stable release.
  • Prefix support — there’s no way to configure a prefix for your classes yet, but we’ll bring it back for sure.
  • Safelists and blocklists — can’t force Tailwind to generate certain classes or prevent it from generating other classes yet.
  • Support for important configuration — there’s no way to make utilities all generate with !important right now, but we plan to implement it.
  • Support for the theme() function — this isn’t needed for new projects because you can use var() now, but we’ll implement it for backwards compatibility.
  • Standalone CLI — we haven’t worked on a standalone CLI for the new engine yet, but will absolutely have it before the v4.0 release.

Aside from that, I’m sure we’ll find a lot of bugs to fix, some exciting new CSS features to sneak in, and refine some of these new APIs that need some more polish before a proper release.

I don’t want to make promises on a specific release timeline, but I’d personally love to mark v4.0 as stable before the summer holiday season kicks in.


Try out the alpha

We’ve tagged a couple alpha releases already, and you can start playing with it in your projects today.

If you’re using the Tailwind CSS IntelliSense extension for VS Code, make sure you switch to the prerelease version from the extension page, and if you’re using our Prettier plugin, make sure you install the latest version.

If you find an issue, please let us know on GitHub. We really want this thing to be bullet-proof before we tag a stable release and reporting any problems you find will help us a lot.

Using Vite

Install the Tailwind CSS v4 alpha and our new Vite plugin:

$ npm install tailwindcss@next @tailwindcss/vite@next

Then add our plugin to your vite.config.ts file:

vite.config.ts
import tailwindcss from '@tailwindcss/vite'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [tailwindcss()],
})

Finally, import Tailwind in your main CSS file:

app.css
@import "tailwindcss";

Using PostCSS

Install the Tailwind CSS v4 alpha and the separate PostCSS plugin package:

$ npm install tailwindcss@next @tailwindcss/postcss@next

Then add our plugin to your postcss.config.js file:

postcss.config.js
module.exports = {
  plugins: {
    '@tailwindcss/postcss': {}
  }
}

Finally, import Tailwind in your main CSS file:

app.css
@import "tailwindcss";

Using the CLI

Install the Tailwind CSS v4 alpha and the separate CLI package:

$ npm install tailwindcss@next @tailwindcss/cli@next

Next, import Tailwind in your main CSS file:

app.css
@import "tailwindcss";

Finally, compile your CSS using the CLI tool:

$ npx @tailwindcss/cli@next -i app.css -o dist/app.css

(The post Open-sourcing our progress on Tailwind CSS v4.0 appeared first on Tailwind CSS Blog.)

December 20, 2023  20:00:00

Today’s the day — we just published the first development preview of Catalyst, our first fully-componentized, batteries-included application UI kit for React, just in time for your holiday hacking sessions.

Today’s the day — we just published the first development preview of Catalyst, just in time for your holiday hacking sessions.

Overview of components included in Catalyst

Catalyst is our first fully-componentized, batteries-included application UI kit — real React components with thoughtfully designed APIs that build on each other to create a real component architecture, the same way we’d do it in a real application.

Check out the live demo, read the documentation, and if you’re a Tailwind UI All-Access customer, download it and try it out in a new project today.

Catalyst is currently in development preview and there’s a lot more to come, but we’re releasing it today so you can play with it right away as we continue to build new components and find ways to make it an even better experience.


Your components, not ours

With Catalyst, we set out to build a UI kit that tomorrow’s Stripe or Linear would feel good about using to build their products — design-obsessed teams who want to own their UI components, and would never choose an off-the-shelf library.

So it’s not a dependency you install, instead you download the source and copy the components into your own project where they become the starting point for your own component system:

Want to change the border radius on your buttons? Just open button.tsx and change some classes. You don’t need to open a GitHub issue and try to convince us to expose a new configuration option.

Catalyst is a “disappearing UI kit” — six months after you’ve installed it, you should almost forget it wasn’t you who built the original components.


Design is in the details

Getting the visual style right on a project like this is hard. We went into it with a few goals:

  • Be competitive — we wanted to design something that could hold its own next to some of the nicest interfaces on the web today.
  • Be timeless — we didn’t want to design something that would look dated in 6 months because it leaned too hard into specific trends.
  • Be productive — whatever we designed needed to feel fast and efficient to real users, not just look great in a Dribbble shot.

It took a lot of work and there were a lot of trade-offs to balance, but I’m really in love with where we ended up:

To be competitive, we invested in lots of details like subtle backdrop blurs on dropdown menus, perfecting the way shadows and borders blend with each other on form controls, and thoughtful use of animation in things like dialogs and toggle switches.

To be timeless, we tried to strike the right balance between flat and skeuomorphic design, with just enough depth cues that our components will look great even if the trends change a bit in either direction.

We also took inspiration from the browser, and used unopinionated blue focus rings to avoid picking a treatment that might soon look out of fashion.

To be productive, we worked carefully to make sure there was still plenty of whitespace, but that the UI was still dense enough to fit plenty of information on the screen.

We also limited our use of transitions and animations only to places where it felt important, and even then tried to keep them fast so it never feels like you’re waiting on the UI.

Catalyst also ships with full dark mode support, and anything you build with Catalyst components automatically adapts between light and dark modes.

It’s not obvious, but there are a ton of little details we had to change to make things look their best in dark mode, like adjusting shadows, changing outer rings to inner rings to mimic the change in lighting, and more.


Modeled after HTML

We spent a lot of time working on the component APIs, trying very hard to make things really easy to drop in and use right away, without compromising on flexibility.

It’s common for UI libraries to use APIs like this:

JSX
function Example() {
  return (
    <TextField
      name="product_name"
      label="Product name"
      description="Use the name you'd like people to see in their cart."
    />
  )
}

But with all the props living on the same component, it starts to get difficult to do things like add a class just to the <input> element itself.

Ultimately that led us to APIs that closely mirrored HTML, where it’s rare that a single component renders more than one element.

Creating a text field with Catalyst looks like this for example:

JSX
import { Description, Field, Label } from '@/components/fieldset'
import { Input } from '@/components/input'

function Example() {
  return (
    <Field>
      <Label>Product name</Label>
      <Description>Use the name you'd like people to see in their cart.</Description>
      <Input name="product_name" />
    </Field>
  )
}

By keeping things composable like this, it makes it really easy to do things like constrain the width of the input, without constraining the width of any of the other elements:

JSX
import { Description, Field, Label } from '@/components/fieldset'
import { Input } from '@/components/input'

function Example() {
  return (
    <Field>
      <Label>Product name</Label>
      <Description>Use the name you'd like people to see in their cart.</Description>
      <Input name="product_name" />
      <Input name="product_name" className="max-w-sm" />
    </Field>
  )
}

It also makes it easy to move the description below the input, instead of above:

JSX
import { Description, Field, Label } from '@/components/fieldset'
import { Input } from '@/components/input'

function Example() {
  return (
    <Field>
      <Label>Product name</Label>
      <Description>Use the name you'd like people to see in their cart.</Description>
      <Input name="product_name" className="max-w-sm" />
      <Description>Use the name you'd like people to see in their cart.</Description>
    </Field>
  )
}

It took a lot of experimenting to figure out the right way to make these APIs work, especially around details like adding layout styles to the right children, but the payoff was worth it, and these components are really a delight to use.


Powered by the next generation of Headless UI

We released the first version of Headless UI back in the summer of 2020, but it’s been just over a year now since the last significant feature release because of all the work we’ve been focused on with Tailwind CSS itself.

Catalyst was the perfect excuse to get our hands dirty with Headless UI again, and we quickly found lots of ways to improve the project to simplify the code in Catalyst itself.

We just published Headless UI v2.0.0-alpha.1, which includes a ton of new stuff:

  • Built-in anchor positioning — using Floating UI, components like Menu, Listbox, and more can now automatically position their popovers to be anchored to their trigger, adapting as needed to changes in the viewport.
  • Headless checkbox component — we’ve added a headless Checkbox component to complement our existing RadioGroup component, making it easy to build totally custom checkbox controls.
  • HTML form components — we’ve added Input, Select, Textarea, Label, Description, Fieldset, and Legend components that handle all of the ID generation and aria-* attribute mapping you need to do to connect form fields together.
  • Improved hover and focus-visible detection — using hooks from the awesome React Aria library under the hood, Headless UI now adds smarter data-hover and data-focus attributes to your controls that behave more consistently across different devices than the native pseudo-classes.
  • Combobox list virtualization — the next version of Headless UI can now handle giant lists of combobox options with no performance issues.

…with plenty of other improvements to come, including a date picker, tooltips, and more.

These improvements are React-only for now during this early alpha period, but we plan to bring all of these improvements to Vue as well before tagging v2.0.

We’ll have documentation published for this stuff really soon, but couldn’t resist getting Catalyst out before the holidays, even if it meant publishing the Headless UI docs a few days later.


Try it out

Catalyst is a free update for all Tailwind UI All-Access customers, and you can download it and start playing with this first release today.

More work went into getting everything we’re releasing today just right than you could ever imagine, but we’re eager for feedback and ways it could be improved, so build some stuff with it and let us know what you think.

We’re going to take a couple weeks to recharge over the holidays, but we’ll be right back into Catalyst in the new year, working on new components like application layouts, comboboxes, command palettes, tooltips, and more.

(The post Introducing Catalyst: A modern UI kit for React appeared first on Tailwind CSS Blog.)

December 19, 2023  17:45:00

There’s nothing like building a major new product for finding all the features you wish you had in your own tools, so we capitalized on some of that inspiration and turned it into this — Tailwind CSS v3.4.

Tailwind CSS v3.3

There’s nothing like building a major new product for finding all the features you wish you had in your own tools, so we capitalized on some of that inspiration and turned it into this — Tailwind CSS v3.4.

As always the improvements range from things you’ve been angry about for years, to supporting CSS features you’ve never even heard of and probably can’t even use at work.

All the good stuff is in that list, but check out the release notes for a couple more details that weren’t exciting enough to earn a spot in this post.

Upgrade your projects by installing the latest version of tailwindcss from npm:

$ npm install tailwindcss@latest

Or try out all of the new features on Tailwind Play, right in your browser.


Dynamic viewport units

When the vh unit was added to browsers we all got so excited — finally a way to build full-height application layouts and stuff without drilling height: 100% through 17 layers of DOM! But mobile devices and their damn disappearing menu bars spoiled all the fun, effectively making the vh unit just a cruel reminder of a future that could’ve been so great.

Well we’ve got a new future now — dvh, lvh, and svh are designed to accommodate that disappearing browser chrome and Tailwind CSS v3.4 supports them out of the box:

Scroll up and down in the viewport to hide/show the browser UI

tailwindcss.com

h-dvh

<div class="h-dvh">
  <!-- ... -->
</div>

We’ve added the following new classes by default:

ClassCSS
h-svhheight: 100svh
h-lvhheight: 100lvh
h-dvhheight: 100dvh
min-h-svhmin-height: 100svh
min-h-lvhmin-height: 100lvh
min-h-dvhmin-height: 100dvh
max-h-svhmax-height: 100svh
max-h-lvhmax-height: 100lvh
max-h-dvhmax-height: 100dvh

If you need other values, you can always use arbitrary values too like min-h-[75dvh].

Browser support is pretty great for these nowadays, so unless you need to support Safari 14 you can start using these right away.


New :has() variant

The :has() pseudo-class is the most powerful thing that’s been added to CSS since flexbox. For the first time ever, you can style an element based on its children, not just based on its parents. It even makes it possible to style based on subsequent siblings.

Here’s an example where the parent gets a colored ring if the radio button inside of it is checked:

Payment method
<label class="has-[:checked]:ring-indigo-500 has-[:checked]:text-indigo-900 has-[:checked]:bg-indigo-50 ..">
  <svg fill="currentColor">
    <!-- ... -->
  </svg>
  Google Pay
  <input type="radio" class="accent-indigo-500 ..." />
</label>

I feel like I’ve found a new use-case for :has() every week while working on this new UI kit we’ve been building for the last few months, and it’s replaced a crazy amount of JavaScript in our code.

For example, our text inputs are pretty complicated design-wise and require a little wrapper element to build. Without :has(), we had no way of styling the wrapper based on things like the :disabled state of the input, but now we can:

input.jsx
export function Input({ ... }) {
  return (
    <span className="has-[:disabled]:opacity-50 ...">
      <input ... />
    </span>
  )
}

This one is pretty bleeding edge but as of literally today it’s now supported in the latest version of all major browsers. Give it a few weeks for any Firefox users to install today’s update and we should be able to go wild with it.


Style children with the * variant

Here’s one people have wanted for literally ever — a way to style children from the parent using utility classes.

We’ve added a new * variant that targets direct children, letting you do stuff like this:

Categories

Sales
Marketing
SEO
Analytics
Design
Strategy
Security
Growth
Mobile
UX/UI
<div>
  <h2>Categories:<h2>
  <ul class="*:rounded-full *:border *:border-sky-100 *:bg-sky-50 *:px-2 *:py-0.5 dark:text-sky-300 dark:*:border-sky-500/15 dark:*:bg-sky-500/10 ...">
    <li>Sales</li>
    <li>Marketing</li>
    <li>SEO</li>
    <!-- ... -->
  </ul>
</div>

Generally I’d recommend just styling the children directly, but this can be useful when you don’t control those elements or need to make a conditional tweak because of the context the element is used in.

It can be composed with other variants too, for instance hover:*:underline will style any child when the child is hovered.

Here’s a cool way we’re using that to conditionally add layout styles to different child elements in the new UI kit we’re working on:

JSX
function Field({ children }) {
  return (
    <div className="data-[slot=description]:*:mt-4 ...">
      {children}
    </div>
  )
}

function Description({ children }) {
  return (
    <p data-slot="description" ...>{children}</p>
  )
}

function Example() {
  return (
    <Field>
      <Label>First name</Label>
      <Input />
      <Description>Please tell me you know your own name.</Description>
    </Field>
  )
}

See that crazy data-[slot=description]:*:mt-4 class? It first targets all direct children (that’s the *: part), then filters them down to just items with a data-slot="description" attribute using data-[slot=description].

This makes it easy to target only specific children, without having to drop all the way down to a raw arbitrary variant.

Looking forward to seeing all the horrible stuff everyone does to make me regret adding this feature.


New size-* utilities

You’re sick of typing h-5 w-5 every time you need to size an avatar, you know it and I know it.

In Tailwind CSS v3.4 we’ve finally added a new size-* utility that sets width and height at the same time:

HTML
<div>
  <img class="h-10 w-10" ...>
  <img class="h-12 w-12" ...>
  <img class="h-14 w-14" ...>
  <img class="size-10" ...>
  <img class="size-12" ...>
  <img class="size-14" ...>
</div>

We’ve wanted to add this forever but have always been hung up on the exact name — size-* felt like so much to type compared to w-* or h-* and s-* felt way too cryptic.

After using it for a few weeks though I can say decisively that even with the longer name, it’s way better than separate width and height utilities. Super convenient, especially if you’re combining it with variants or using a complex arbitrary value.


Balanced headlines with text-wrap utilities

How much time have you spent fiddling with max-width or inserting responsive line breaks to try and make those little section headings wrap nicely on your landing pages? Well now you can spend zero time on it, because the browser can do it for you with text-wrap: balance:

Beloved Manhattan soup stand closes

New Yorkers are facing the winter chill with less warmth this year as the city's most revered soup stand unexpectedly shutters, following a series of events that have left the community puzzled.

<article>
  <h3 class="text-balance ...">Beloved Manhattan soup stand closes<h3>
  <p>New Yorkers are facing the winter chill...</p>
</article>

We’ve also added text-pretty which tries to avoid orphaned words at the end of paragraphs using text-wrap: pretty:

Beloved Manhattan soup stand closes

New Yorkers are facing the winter chill with less warmth this year as the city's most revered soup stand unexpectedly shutters, following a series of events that have left the community puzzled.

<article class="text-pretty ...">
  <h3>Beloved Manhattan soup stand closes<h3>
  <p>New Yorkers are facing the winter chill...</p>
</article>

The nice thing about these features is that even if someone visits your site with an older browser, they’ll just fallback to the regular wrapping behavior so it’s totally safe to start using these today.


Subgrid support

Subgrid is a fairly recent CSS feature that lets an element sort of inherit the grid columns or rows from its parent, make it possible to place its child elements in the parent grid.

HTML
<div class="grid grid-cols-4 gap-4">
  <!-- ... -->
  <div class="grid grid-cols-subgrid gap-4 col-span-3">
      <div class="col-start-2">06</div>
  </div>
  <!-- ... -->
</div>

We’re using subgrid in the new UI kit we’re working on for example in dropdown menus, so that if any item has an icon, all of the other items are indented to keep the text aligned:

HTML
<div role="menu" class="grid grid-cols-[auto_1fr]">
  <a href="#" class="grid-cols-subgrid col-span-2">
    <svg class="mr-2">...</svg>
    <span class="col-start-2">Account</span>
  </a>
  <a href="#" class="grid-cols-subgrid col-span-2">
    <svg class="mr-2">...</svg>
    <span class="col-start-2">Settings</span>
  </a>
  <a href="#" class="grid-cols-subgrid col-span-2">
    <span class="col-start-2">Sign out</span>
  </a>
</div>

When none of the items have an icon, the first column shrinks to 0px and the text is aligned all the way to left.

Check out the MDN documentation on subgrid for a full primer — it’s a bit of a tricky feature to wrap your head around at first, but once it clicks it’s a game-changer.


Extended min-width, max-width, and min-height scales

We’ve finally extended the min-width, max-width, and min-height scales to include the full spacing scale, so classes like min-w-12 are actually a real thing now:

HTML
<div class="min-w-12">
  <!-- ... -->
</div>

We should’ve just done this for v3.0 but never really got around to it — I’m sorry and you’re welcome.


Extended opacity scale

We’ve also extended the opacity scale to include every step of 5 out of the box:

HTML
<div class="opacity-35">
  <!-- ... -->
</div>

Hopefully that means a few less arbitrary values in your markup. I’m coming for you next 2.5%.


Extended grid-rows-* scale

We’ve also bumped the baked-in number of grid rows from 6 to 12 because why not:

HTML
<div class="grid grid-rows-9">
  <!-- ... -->
</div>

Maybe we’ll get even crazier and bump it to 16 in the next release.


New forced-colors variant

Ever heard of forced colors mode? Your site probably looks pretty bad in it.

Well now you can’t blame us at least, because Tailwind CSS v3.4 includes a forced-colors variant for adjusting styles for forced colors mode:

HTML
<form>
  <input type="checkbox" class="appearance-none forced-colors:appearance-auto ...">
</form>

It’s really useful for fine-tuning totally custom controls, especially combined with arbitrary values and a working knowledge of CSS system colors.


New forced-color-adjust utilities

We’ve also added new forced-color-adjust-auto and forces-color-adjust-none utilities to control how forced colors mode affects your design:

HTML
<fieldset>
  <legend>Choose a color</legend>
  <div class="forced-color-adjust-none ...">
    <label>
      <input class="sr-only" type="radio" name="color-choice" value="white" />
      <span class="sr-only">White</span>
      <span class="size-6 rounded-full bg-white"></span>
    </label>
    <label>
      <input class="sr-only" type="radio" name="color-choice" value="gray" />
      <span class="sr-only">Gray</span>
      <span class="size-6 rounded-full bg-gray-300"></span>
    </label>
    <!-- ... -->
  </div>
</fieldset>

These should be used pretty sparingly, but they can be useful when it’s critical that something is rendered in a specific color no matter what, like choosing the color of something someone is buying in an online store.

To learn more about all this forced colors stuff, I recommend reading “Forced colors explained: A practical guide” on the Polypane blog — by far the most useful post I’ve found on this topic.


If you’ve been paying close attention, you might be wondering about Oxide, the engine improvements we previewed at Tailwind Connect this summer.

We’d originally slated those improvements for v3.4, but we have a few things still to iron out and so many of these other improvements had been piling up that we felt it made sense to get it all out the door instead of holding it back. The Oxide stuff is still coming, and will be the headlining improvement for the next Tailwind CSS release in the new year.

In the mean time, dig in to Tailwind CSS v3.4 by updating to the latest version with npm:

$ npm install tailwindcss@latest

With :has() and the new * variant, your HTML is about to get more out of control than ever.

(The post Tailwind CSS v3.4: Dynamic viewport units, :has() support, balanced headlines, subgrid, and more appeared first on Tailwind CSS Blog.)

December 18, 2023  16:30:00

We just released Heroicons v2.1 which includes a brand new micro style — a full set of almost three hundred 16×16 icons designed for tighter, higher density UIs.

Heroicons Micro

We’ve wanted to do a set this size for a long time, but it wasn’t until we started working on a new React UI kit project earlier this year that we finally needed them badly enough to bite the bullet and design them.

In Catalyst (our new UI kit), we’ve tried to really thread the needle in terms of giving things enough room to breathe but also keeping things dense enough that the applications you build with it actually feel productive to use.

We’re using lots of 14px text which looks great in the UI, but when we tried to incorporate our existing 20×20 icons, they felt just a tiny bit too big and unbalanced.

Heroicons Mini compared to Heroicons Micro used in a dropdown

Most people would have probably just scaled the icons down and moved on, but we’re not most people, for better or for worse.

Icons always turn out a lot sharper when you design them for the exact size they’re going to be used, so we began the process of redrawing every icon from scratch, carefully trimming down the amount of detail on an icon-by-icon basis to make sure they render nice and crisp at their intended size.

Examples of details adjusted for Heroicons Micro

About a month later we had a brand new set of 288 icons, meticulously crafted for higher density interfaces like what we’ve been working on with Catalyst.

You can check out all of the new icons on the Heroicons website, start designing with them using our official Heroicons Figma library, and add them to your projects with our React and Vue libraries.

(The post Heroicons Micro: What are these, icons for ants? appeared first on Tailwind CSS Blog.)

August 7, 2023  10:30:00

We just released Studio — a beautiful new agency website template we’ve been working on for the last couple of months for Tailwind UI.

We just released Studio — a beautiful new agency website template we’ve been working on for the last couple of months for Tailwind UI.

Learn about the Studio template

We built it with Next.js, MDX, and of course Tailwind CSS, and it’s the first template we’ve published using the new Next.js App Router.

Designing an agency template is an interesting project, because creative agencies commonly use their own website to show off some really flashy, bespoke ideas, and using a template just kind of feels strange when the goal is to show what your own company is capable of.

So we tried to approach this one with two goals in mind to actually make it useful to people:

  1. Teach people how to do some of the cool stuff you see on flash agency sites — I’ve always believed our templates are just as (if not more) valuable as an educational resource than as simply templates, so we wanted to use this template as an opportunity to show off how we’d build a lot of the cool interactive and animated details you see on these sorts of sites.
  2. Design it for agencies that don’t sell design — there are a lot of agencies out there who just focus on engineering work, and a lot of the time those companies struggle to stand out design-wise. We tried to design this template in a way that it didn’t depend on tons of screenshots of design work and stuff to look good, so that an agency that focuses on code could use it as a starting point for their own site.

I think what we came up with nailed these two goals and I’m really proud of how it all turned out.

Check out the live preview as always for the full experience — there are tons of cool details in this one that you have to see in the browser to really appreciate.


Delightfully animated

One of the unspoken rules of agency websites is that they’ve gotta be flashy. We didn’t go full replace-the-mouse-cursor or render-the-entire-site-with-WebGL but we did look for opportunities to tastefully introduce animations and interactivity wherever we could.

For instance, we built a light declarative component-based API around some features of Framer Motion to make it easy to do scroll-triggered entrance animations:

The authoring experience for these types of animations turned out really nice — just wrap the stuff you want to fade in with a FadeIn or FadeInStagger component and you’re in business:

function Clients() {
  return (
    <div className="mt-24 rounded-4xl bg-neutral-950 py-20 sm:mt-32 sm:py-32 lg:mt-56">
      <Container>
        <FadeIn className="flex items-center gap-x-8">
          <h2 className="text-center font-display text-sm font-semibold tracking-wider text-white sm:text-left">
            We’ve worked with hundreds of amazing people
          </h2>
          <div className="h-px flex-auto bg-neutral-800" />
        </FadeIn>
        <FadeInStagger faster>
          <ul
            role="list"
            className="mt-10 grid grid-cols-2 gap-x-8 gap-y-10 lg:grid-cols-4"
          >
            {clients.map(([client, logo]) => (
              <li key={client}>
                <FadeIn>
                  <Image src={logo} alt={client} unoptimized />
                </FadeIn>
              </li>
            ))}
          </ul>
        </FadeInStagger>
      </Container>
    </div>
  )
}

We also added this nice little animation to the logo where the mark is filled with a solid color on hover:

This little detail looks small but interestingly you can’t really do it without client-side navigation, because the animation would re-run when clicking the logo to go back to the homepage. Using a framework like Next.js, we’re able to keep the logo filled in while hovering, even across URL changes, which feels a lot nicer.

The menu drawer animation turned out really nice as well, pushing the whole page down when it opens:

If you look closely, the logo and button don’t just naively change color either — it’s actually driven precisely by the position of the sheet that’s sliding down, and the logo is actually partially white and partially black at the same time when the edge of the sheet is intersecting with it.

Another detail I really love is this interaction we came up with for the images on the case study pages:

We wanted the whole site to feel black and white, but showing black and white images all of the time didn’t feel right. So we came up with this treatment where the image starts off black and white, and the saturation animates back in as the image gets close to the center of the screen when scrolling. We also show the full color image on hover.

We were also careful to try and implement all of these animations in a way that’s mindful of people with vestibular motion disorders and are sensitive to these types of big animations. Using Framer Motion’s useReducedMotion hook and the motion-safe variant in Tailwind, we do things like conditionally disable the navigation menu animation, and limit the scroll-driven entrance animations to opacity only so things aren’t moving on the screen.


Developer-centric case study and blog workflow

Studio includes support for both case studies and blog posts, and as you might have guessed if you’ve played with any of our other templates, we used this an excuse to integrate MDX into the project.

Here’s an example of what a basic case study looks like — authored mostly in markdown with some common metadata and support for custom components mixed in to the content:

import logo from '@/images/clients/phobia/logomark-dark.svg'
import imageHero from './hero.jpg'
import imageJennyWilson from './jenny-wilson.jpeg'

export const caseStudy = {
  client: 'Phobia',
  title: 'Overcome your fears, find your match',
  description:
    'Find love in the face of fear — Phobia is a dating app that matches users based on their mutual phobias so they can be scared together.',
  summary: [
    'Find love in the face of fear — Phobia is a dating app that matches users based on their mutual phobias so they can be scared together.',
    'We worked with Phobia to develop a new onboarding flow. A user is shown pictures of common phobias and we use the microphone to detect which ones make them scream, feeding the results into the matching algorithm.',
  ],
  logo,
  image: { src: imageHero },
  date: '2022-06',
  service: 'App development',
  testimonial: {
    author: { name: 'Jenny Wilson', role: 'CPO of Phobia' },
    content:
      'The team at Studio went above and beyond with our onboarding, even finding a way to access the user’s microphone without triggering one of those annoying permission dialogs.',
  },
}

export const metadata = {
  title: `${caseStudy.client} Case Study`,
  description: caseStudy.description,
}

## Overview

Noticing incredibly high churn, the team at Phobia came to the conclusion that, instead of having a
fundamentally flawed business idea, they needed to improve their onboarding process.

Previously users selected their phobias manually but this led to some users selecting things they
weren’t actually afraid of to increase their matches.

To combat this, we developed a system that displays a slideshow of common phobias during
onboarding. We then use malware to surreptitiously access their microphone and detect when they
have audible reactions. We measure the pitch, volume and duration of their screams and feed that
information to the matching algorithm.

The next phase is a VR version of the onboarding flow where users are subjected to a series of
scenarios that will determine their fears. We are currently developing the first scenario, working
title: “Jumping out of a plane full of spiders”.

## What we did

<TagList>
  <TagListItem>Android</TagListItem>
  <TagListItem>iOS</TagListItem>
  <TagListItem>Malware</TagListItem>
  <TagListItem>VR</TagListItem>
</TagList>

<Blockquote
  author={{ name: 'Jenny Wilson', role: 'CPO of Phobia' }}
  image={{ src: imageJennyWilson }}
>
  The team at Studio went above and beyond with our onboarding, even finding a
  way to access the user’s microphone without triggering one of those annoying
  permission dialogs.
</Blockquote>

<StatList>
  <StatListItem value="20%" label="Churn rate" />
  <StatListItem value="5x" label="Uninstalls" />
  <StatListItem value="2.3" label="App store rating" />
  <StatListItem value="8" label="Pending lawsuits" />
</StatList>

All of the typography styles for this template are totally custom and we took a bit of a different approach this time than we have in the past — instead of writing a bunch of complex CSS to avoid our typography styles clashing with any custom components in the MDX, we created a little remark plugin called remark-rehype-wrap that makes it possible to wrap chunks of Markdown content with a wrapper element.

This way, we could wrap anything that was vanilla Markdown content with a typography class, but make sure any custom components in the document were simply not wrapped, rather than try to craft the CSS in such a way that it ignores those parts of the tree.

Both approaches totally work but it’s always fun to try new ideas and see what you learn. I’m curious to see what a solution based on the new style queries feature coming to CSS might look like in the future too!


So that’s Studio! Pull it down, tear it apart, and see if you learn a couple of new tricks.

Like all of our templates, it’s included with a one-time purchase Tailwind UI all-access license, which is the best way to support our work on Tailwind CSS and make it possible for us to keep building awesome stuff for years to come.

(The post Meet Studio: Our beautiful new agency site template appeared first on Tailwind CSS Blog.)

July 19, 2023  17:00:00

Last month, over 200 people got together in my hometown of Cambridge, Ontario to hang out, talk shop, and get a peek behind the curtain at some of the new stuff we’ve been working on.

Tailwind Connect started out as an idea for throwing a little local meetup while the team was in town, but naturally ballooned into us renting a massive hall, hiring a crew of four videographers, and racking up a catering bill that put my own wedding to shame.

But even though we got a little carried away with the production, we really tried to make it feel more like a meetup than a full-blown conference. We ran the event in the evening and kept it to just one presentation, leaving as much time as possible for people to just hang out and connect over pizza and beer.

We originally expected it to be a “drive there after work” local-heavy event, but almost half of the people who attended traveled by plane to get here. So to help everyone make the most of their time here, we set up a Discord server for the event so people traveling in could make plans with each other, and rented out the patio at the Foundry Tavern next door to the event space for the afternoon to give people a reason to meet up early and make some new friends.

Tailwind Connect attendees enjoying their lunch at the Foundry Tavern
Those two guys in the Tailwind CSS shirts flew in from Germany for this thing.

We opened up the doors for the event around 5:30pm and started the night with an hour long opening reception, where people could hang out and talk and enjoy some canapés.

Attendees talking and enjoying drinks before the event

One detail I thought we really nailed was the badges — when you bought a ticket we gave you a basic markdown field where you could write anything you wanted about yourself that might help people start interesting conversations with you.

Attendee badges laid out on a table near the event entrance

I found myself looking at everyone’s badges all night and it was an awesome way to immediately break the ice and have something to talk about.


At around 6:30pm everyone took a seat for the keynote presentation. I kicked it off with a bit of history on Tailwind CSS as a project, and how it got to where it is today.

Adam Wathan standing on stage in front of a slide highlighting some of the companies using Tailwind CSS

Six years ago it was just a couple of stylesheets I was copying and pasting from project to project. Today it’s downloaded over 25 million times per month by millions of developers, and is used by some of the world’s biggest companies to build some of the world’s biggest websites. It was really special to me to finally get a chance to celebrate how far we’ve come in person with so many people from the community.


After that, Sam Selikoff walked through some brilliant demos showing off some of the coolest new features in CSS. He demoed things like accent color, fluid typography, headline balancing, container queries, and even masonry grid, all using Tailwind CSS and the utility-first workflow.

Sam Selikoff live-coding a demo of headling balancing on stage

Sam is an awesome friend, I asked him to come up for the event and be a part of the keynote without really even having a plan for what I was going to ask him to do at all, and he put together his whole presentation in about 36 hours after he arrived and absolutely nailed it. No slides, all live-coding, and crushed it like a pro. Highly recommend his YouTube channel and training site Build UI — dude is a fantastic teacher.


Next, I gave a sneak peek at Oxide, the next evolution of the Tailwind CSS engine.

Adam Wathan on stage in front of a slide that says "Oxide"

Oxide is a lot of different pieces but they all boil down to two goals — improved performance, and simplifying the developer experience.

We’re making Tailwind more of an all-in-one CSS processing tool by integrating Lightning CSS, which means that in the next version of Tailwind, things like importing other CSS files, nesting, vendor prefixes, and syntax transforms for future CSS features will just work — no need to install or configure any additional tooling like autoprefixer or postcss-import.

Adam Wathan on stage in front of a slide that says "Lightning CSS"

Lightning CSS is written in Rust which makes it extremely fast, and we’re dipping our toes into Rust ourselves too by rewriting some of most critical paths, like scanning all of your template files for class names.

With these improvements, we’re seeing build times drop by over 50% in real-world projects, bringing the production build time down to around 150ms for even our largest projects.

Chart showing Tailwind CSS v3.4 as more than twice as fast as Tailwind CSS v3.3

Oxide is also going to bring a simplified configuration experience. We’re adding automatic content detection so you won’t need to configure the paths to all of your template files anymore, and making it even easier to add Tailwind to your own CSS by replacing the need for all of the @tailwind directives with a simple @import "tailwindcss" call at the top of your CSS file.

I also shared a very early look at an idea we’re exploring for configuring Tailwind CSS right in your CSS file instead of in JavaScript. With CSS-based configuration, automatic content detection, and the simplified import story, configuring Tailwind with some custom colors and fonts might look as simple as this in the future:

@import "tailwindcss";
@import "./fonts" layer(base);

:theme {
  --colors-neon-pink: oklch(71.7% 0.25 360);
  --colors-neon-lime: oklch(91.5% 0.258 129);
  --colors-neon-cyan: oklch(91.3% 0.139 195.8);

  --font-family-sans: "Inter", sans-serif;
  --font-family-display: "Satoshi", sans-serif;
}

We’re planning to introduce all of this stuff without any breaking changes, and many of these improvements (including Lightning CSS integration and our Rust-based template parser) will be available in Tailwind CSS v3.4 in the next couple of months.

I’ll definitely write about this stuff in a lot more detail closer to release time, but watch the keynote if you want to take a closer look before then.


We closed out the keynote with a preview of Catalyst, the new React UI kit we’ve been working on since late last year.

Adam Wathan on stage in front of a slide that says "Catalyst"

This is something that’s been on our roadmap for two years at this point, and after many months of ironing out the best approach, we finally broke ground on it last October.

Catalyst is a batteries-included component system with thoughtfully designed APIs, and includes all of the basic building blocks you need to build your own applications. It inclues things like buttons, form controls, dialogs, slide-overs, tables, dropdowns, and more.

A collage of components included in the Catalyst UI kit.

The biggest difference between Catalyst and our existing application UI component examples is that the components in Catalyst are all wired together like they’d be in a real project, instead of being isolated copy and paste code snippets.

But just like with our site templates, the code in Catalyst is yours. If you want to tweak something, you just open up the file and tweak it — it’s not a library you install via npm.

Think of it like a starting point for your own component system. You download the latest version from us, copy the contents of the /components directory into your project, then start building.

If you need to change something, change it.

When you want to create your own new components, create them.

The whole idea is that 6 months down the road the codebase feels so yours that you’ll almost forget you kickstarted it with Catalyst in the first place.

After introducing the project and giving everyone a bit of a tour, I handed things off to Steve Schoger who did an amazing job walking through all of the little design details that went into making Catalyst feel like such a polished design system.

Steve Schoger on stage explaining some visual design details in Catalyst

I wrapped things up by showing off a couple of advanced Tailwind CSS tricks we had to use to implement some of those design details, like how to implement responsive transitions with Framer Motion using CSS variables and Tailwind’s arbitrary property feature.

Adam Wathan on stage live-coding a demo with Tailwind CSS and Framer Motion

Catalyst is still a work-in-progress so don’t hold me to it, but with any luck we’ll have an early version and more details to share with Tailwind UI customers in the next month or so.


For the next three hours we got to hang out in the event space enjoying some refreshments, and getting to meet all of the people who made the trip to attend.

Attendees at Tailwind Connect socializing after the event

I mentioned it in the keynote, but this was the first time in the history of the project that we’d ever gotten a bunch of Tailwind fans into the same room, and it was a really special experience for me to finally meet so many people who have gotten so much out of using the framework. It’s easy to sort of underappreciate the impact of the work we do here when everything is just on GitHub and Twitter, and to see it manifested in physical space with real people just left me beaming with pride.

It’s really an honor to work on these projects and be able to help so many people have more fun building things for the web. Looking forward to doing another event like this in the future — a major life highlight for me for sure.

Check out the Tailwind Connect 2023 photo gallery for more photos of the event, and watch the keynote on YouTube if you couldn’t make it in person.

(The post Tailwind Connect 2023: Recap of our first in-person event appeared first on Tailwind CSS Blog.)

April 24, 2023  14:00:00

Over the last four months we’ve probably done more work on Tailwind UI than we ever have, and having finally wrapped up what we set out to achieve I’m excited to lay it all out for you.

I haven’t shared a Tailwind UI update since we released the Protocol template in December, but that’s not because we haven’t been busy.

Over the last four months we’ve probably done more work on Tailwind UI than we ever have, and having finally wrapped up what we set out to achieve I’m excited to lay it all out for you!


Commit: A beautiful changelog template for your next idea

Just a few days ago we released Commit, a brand new changelog template we designed for Tailwind UI — built of course with Tailwind CSS and Next.js.

Learn about the Commit template

Public changelogs have become a really popular way to keep people in the loop about what you’ve been working on, and to stay accountable and build your shipping muscles. They aren’t a new concept by any means of course, but I don’t think it was until Linear started publishing to their changelog site that others got excited about using them almost as an alternative to a company blog.

Commit is our take on the modern product changelog, designed as a single page website that can act as both your project homepage and a feed of everything you’ve been working on.

The Commit template in dark mode, partially scrolled down to reveal a second post

Like all of our templates, it’s loaded with features and details that make it a delight to experience and a pleasure to work on:

  • Light and dark mode support, because you can’t make anything a developer might read without optimizing it for reading at 2am with the lights off.
  • Hand-crafted typography styles, meticulously chosen font sizes, spacing, list styles, and more, designed specifically for this template.
  • Built-in syntax highlighting, powered by Shiki, and easy to customize with a few CSS variables.
  • Single-file editing experience, every entry lives in a single MDX file, so updating your changelog feels as lightweight as updating a CHANGELOG.md file in an open-source project.
  • Beautiful animations and effects, this time powered by Motion One, making it a great resource for studying how to pull off these sorts of tricks with a cutting-edge new library.

As always it’s easy to jump into the code and make it your own — here we’ve just tweaked a handful of colors and it feels like a totally different website:

The Commit template customized to match the branding of the Motion One open-source library

Check out the live demo for the full experience, and if you’re already the proud owner of a Tailwind UI all-access license, download a copy of the template to use it in your next project or just to study the source code to learn a new trick or two.


Redesigning hundreds of components for Tailwind UI

Design moves fast and with it being over three years since we first released Tailwind UI, we felt like it deserved to be put under the microscope and make sure it still felt like our best work.

We were pleasantly surprised to discover that yes, we have actually gotten better at design over the past three years, so we spent four months heads down making every component and category as pixel-perfect as we possibly could with our newfound powers.

A collage of redesigned page examples and components for Tailwind UI

Once we came out of our cave to see the sunlight again we had hundreds of redesigned components, dozens of totally new ideas put together, and a fresh batch of page examples to show them all off.

Here’s a run down of some of the types of improvements we made.

Redesigning existing patterns that felt dated

A lot of the component patterns in Tailwind UI are really timeless ideas, but as design trends change and we become better designers, the specific implementations of those patterns can start to feel like it’s from another era.

A comparison between an old component in Tailwind UI and it’s new redesigned version

We went through all of the components one-by-one and found lots of patterns we wanted to take another stab at and did our best to bring them into 2023.

Take a look at the Hero Sections category for some great examples of what these refreshed patterns look like.

Fine-tuning subtle details across the board

A lot of components didn’t really need a full on redesign as much as they needed just a little bit of extra polishing.

A comparison between an old component in Tailwind UI and it’s new fine-tuned version

We went through tons of components making subtle improvements to the spacing, typography, and contrast, and the results just feel so much sharper and cleaner.

The example above is from the Description Lists category — check it out there if you want to see it in its full browser-rendered glory.

Designing a ton of brand new examples

As we were going through all of the existing components, we kept coming up with new ideas that felt like they were missing from the original set of patterns.

A collage of new component examples in Tailwind UI

So we designed tons of brand new components, trying to fill as many holes that stood out to us as we could.

Lots of categories more than doubled in size, like the Feature Sections category which is loaded with really killer new ideas.

Adding more dark variations

It feels like almost every new website I see these days is dark by default, so it felt like we had a moral obligation to give you some more examples optimized for dark backgrounds.

A collage of new dark component examples in Tailwind UI

One of examples I really like are these new dark badges — there’s not much to them really but that little bit of opacity on the background color is such a nice effect for dark designs.

All-new page examples

Finally we took all of this new stuff and put together a bunch of brand new page examples to show them off, including the application UI example everyone has been bugging us for from the screenshots in some of our marketing components.

A collage of new page examples in Tailwind UI

Check out the updated Home Screens category for example to see some of these new designs.


So there you go, without a doubt our biggest Tailwind UI update of all time. We’ve been dripping out these improvements slowly since January and it’s all captured in the Tailwind UI changelog so check that out if you want to dig in to what’s changed in more detail.

Next up for us — digging in to a ton of ideas we have for Tailwind CSS v4.0, and exploring our first Next.js application starter kit. Excited to share more in the coming weeks!

(The post New changelog template + the biggest Tailwind UI update ever appeared first on Tailwind CSS Blog.)

March 28, 2023  14:30:00

Tailwind CSS v3.3 is here — bringing a bunch of new features people have been asking for forever, and a bunch of new stuff you didn’t even know you wanted.

Tailwind CSS v3.3

Tailwind CSS v3.3 is here — bringing a bunch of new features people have been asking for forever, and a bunch of new stuff you didn’t even know you wanted.

That covers the most exciting stuff, but check out the release notes for an exhaustive list of every single little improvement we’ve made since the last release.

Upgrading your projects is as easy as installing the latest version of tailwindcss from npm:

npm install -D tailwindcss@latest

You can also try out all of the new features on Tailwind Play, right in your browser.


Extended color palette for darker darks

One of the most common feature requests we’ve had over the years is to add darker shades for every color — usually because someone is building a dark UI and just wants more options down in that dark end of the spectrum.

Well wish granted — in Tailwind CSS v3.3 we’ve added a new 950 shade for every single color.

In the grays they act as basically a tinted black, which is great for ultra dark UIs:

Comparison between two dark user interfaces, one using slate-900 as the darkest color and the other using slate-950 as the darkest color

And in the rest of the color spectrum we optimized 950 for high contrast text and tinted control backgrounds:

Comparison between two light user interfaces, one using sky-900 as the darkest color and the other using sky-950 as the darkest color

Believe it or not the hardest part about this project was convincing ourselves to be okay with having 11 shades per color. Trying to make that look good in the color palette documentation was a nightmare.

Also pour one out for the 50 shades of gray jokes we used to be able to make.


ESM and TypeScript support

Now you can configure Tailwind CSS in ESM, or even in TypeScript:

/** @type {import('tailwindcss').Config} */
export default {
  content: [],
  theme: {
    extend: {},
  },
  plugins: [],
}

When you run npx tailwindcss init, we’ll detect if your project is an ES Module and automatically generate your config file with the right syntax.

You can also generate an ESM config file explicitly by using the --esm flag:

npx tailwindcss init --esm

To generate a TypeScript config file, use the --ts flag:

npx tailwindcss init --ts

A lot of people assume this is easy because they’re writing their own code in ESM already (even if it’s being transpiled by their build tool) but it’s actually pretty tricky — we literally have to transpile the config file for you on the fly.

It’s a bit easier to understand why this has to happen when you think of the TypeScript case, because of course Tailwind is distributed as JavaScript, and it can’t magically import an uncompiled TypeScript file.

We’re handling this with the wonderful jiti library under the hood, and using Sucrase to transpile the code with the best possible performance while keeping the installation footprint small.


Simplified RTL support with logical properties

We’ve made it possible to style multi-directional websites using our LTR and RTL variants for a while, but now you can use logical properties to do most of this styling more easily and automatically.

Using new utilities like ms-3 and me-3, you can style the start and end of an element so that your styles automatically adapt in RTL, instead of writing code like ltr:ml-3 rtl:mr-3:

Left-to-right

Tom Cook

Director of Operations

Right-to-left

تامر كرم

الرئيس التنفيذي

<div class="group flex items-center">
  <img class="shrink-0 h-12 w-12 rounded-full" src="..." alt="" />
  <div class="ltr:ml-3 rtl:mr-3">
  <div class="ms-3">
    <p class="text-sm font-medium text-slate-700 group-hover:text-slate-900">...</p>
    <p class="text-sm font-medium text-slate-500 group-hover:text-slate-700">...</p>
  </div>
</div>

We’ve added new logical property utilities for inset, margin, padding, border-radius, scroll-margin, and scroll-padding.

Here’s a full list of all of the new utilities we’ve added and what they map to:

New classPropertiesPhysical counterpart (LTR)
start-*inset-inline-startleft-*
end-*inset-inline-endright-*
ms-*margin-inline-startml-*
me-*margin-inline-endmr-*
ps-*padding-inline-startpl-*
pe-*padding-inline-endpr-*
rounded-s-*border-start-start-radius
border-end-start-radius
rounded-l-*
rounded-e-*border-start-end-radius
border-end-end-radius
rounded-r-*
rounded-ss-*border-start-start-radiusrounded-tl-*
rounded-se-*border-start-end-radiusrounded-tr-*
rounded-ee-*border-end-end-radiusrounded-br-*
rounded-es-*border-end-start-radiusrounded-bl-*
border-s-*border-inline-start-widthborder-l-*
border-e-*border-inline-end-widthborder-r-*
border-s-*border-inline-start-colorborder-l-*
border-e-*border-inline-end-colorborder-r-*
scroll-ms-*scroll-margin-inline-startscroll-ml-*
scroll-me-*scroll-margin-inline-endscroll-mr-*
scroll-ps-*scroll-padding-inline-startscroll-pl-*
scroll-pe-*scroll-padding-inline-endscroll-pr-*

These should save you a ton of code if you regularly build sites that need to support both LTR and RTL languages, and you can always combine these with the ltr and rtl variants when you need more control.


Fine-tune gradient color stop positions

We’ve added new utilities like from-5%, via-35%, and to-85% that let you adjust the actual position of each color stop in your gradients:

10%
30%
90%
<div class="bg-gradient-to-r from-indigo-500 from-10% via-purple-500 via-30% to-pink-500 to-90% ...">
  <!-- ... -->
</div>

We’ve included every value from 0% to 100% in steps of 5 out of the box, but you can of course use arbitrary values to get exactly the effect you want:

<div class="bg-gradient-to-r from-cyan-400 from-[21.56%] ...">
  <!-- ... -->
</div>

For more details, check out the gradient color stops documentation.


Line-clamp out of the box

We released our official line-clamp plugin just over two years ago and even though it uses a bunch of weird deprecated -webkit-* stuff, it works in every browser and it’s going to work forever, so we decided to just bake it into the framework itself.

Boost your conversion rate

Nulla dolor velit adipisicing duis excepteur esse in duis nostrud occaecat mollit incididunt deserunt sunt. Ut ut sunt laborum ex occaecat eu tempor labore enim adipisicing minim ad. Est in quis eu dolore occaecat excepteur fugiat dolore nisi aliqua fugiat enim ut cillum. Labore enim duis nostrud eu. Est ut eiusmod consequat irure quis deserunt ex. Enim laboris dolor magna pariatur. Dolor et ad sint voluptate sunt elit mollit officia ad enim sit consectetur enim.

Lindsay Walton
<article>
  <div>
    <time datetime="2020-03-16" class="block text-sm/6 text-gray-600">Mar 10, 2020</time>
    <h2 class="mt-2 text-lg font-semibold text-gray-900">Boost your conversion rate</h2>
    <p class="line-clamp-3 mt-4 text-sm/6 text-gray-600">
      Nulla dolor velit adipisicing duis excepteur esse in duis nostrud occaecat mollit incididunt deserunt sunt. Ut ut sunt laborum ex occaecat eu tempor labore enim adipisicing minim ad. Est in quis eu dolore occaecat excepteur fugiat dolore nisi aliqua fugiat enim ut cillum. Labore enim duis nostrud eu. Est ut eiusmod consequat irure quis deserunt ex. Enim laboris dolor magna pariatur. Dolor et ad sint voluptate sunt elit mollit officia ad enim sit consectetur enim.
    </p>
  </div>
  <div class="mt-4 flex gap-x-2.5 text-sm font-semibold leading-6 text-gray-900">
    <img src="..." class="h-6 w-6 flex-none rounded-full bg-gray-50" />
    Lindsay Walton
  </div>
</article>

So when you upgrade to v3.3, you can safely remove the line-clamp plugin if you were using it:

tailwind.config.js
module.exports = {
  // ...
  plugins: [
    require('@tailwindcss/line-clamp')
  ]
}

Don’t let the door hit you in the ass on the way out, plugin.

Check out the new line-clamp documentation to learn more about how it all works if you haven’t played with it before.


New line-height shorthand for font-size utilities

One thing we’ve found over years and years of designing beautiful stuff with Tailwind is that we literally never set a line-height without also setting the font-size at the same time.

So inspired by our color opacity modifier syntax, we decided to make it possible to save a few characters by setting them together with a single utility:

index.html
  <p class="text-lg leading-7 ...">
  <p class="text-lg/7 ...">
    So I started to walk into the water. I won't lie to you boys, I was terrified. But
    I pressed on, and as I made my way past the breakers a strange calm came over me.
    I don't know if it was divine intervention or the kinship of all living things but
    I tell you Jerry at that moment, I <em>was</em> a marine biologist.
  </p>

You can use any value defined in your line-height scale, or use arbitrary values if you need to deviate from your design tokens:

<p class="text-sm/[17px] ..."></p>

Check out the font size documentation for a few more examples.


CSS variables without the var()

In the spirit of typing less, we’ve also made it possible to omit the var() when using a CSS variable as an arbitrary value:

my-component.jsx
export function MyComponent({ company }) {
  return (
    <div
      style={{
        '--brand-color': company.brandColor,
        '--brand-hover-color': company.brandHoverColor,
      }}
      className="bg-[var(--brand-color)] hover:bg-[var(--brand-hover-color)]"
      className="bg-[--brand-color] hover:bg-[--brand-hover-color]"
    />
  )
}

That’s a pretty cool trick right there for using things like hover: with styles that come from the database or something by the way.


Configure font-variation-settings for custom font families

When using custom fonts, you’ll often want to configure things like font-feature-settings or font-variation-settings to opt-in to specific tweaks the font offers.

We’ve made it easy to do this for font-feature-settings for a while, but now you can do the same thing with font-variation-settings by providing a value for it in the sort-of options object you can plop after the font list in your config file:

tailwind.config.js
module.exports = {
  theme: {
    fontFamily: {
      sans: [
        'Inter var, sans-serif',
        {
          fontFeatureSettings: '"cv11", "ss01"',
          fontVariationSettings: '"opsz" 32',
        },
      ],
    },
  },
}

In the example above we’re using a recent release of Inter that supports using the optical size axis to trigger the “Display” variation of the font, optimized for larger sizes like headlines.


New list-style-image utilities

Ever wanted to use a picture of a carrot as your list item marker? Well now you can, with the new list-image-* utilities.

  • 5 cups chopped Porcini mushrooms
  • 1/2 cup of olive oil
  • 3lb of celery
<ul class="list-image-[url(carrot.png)] ...">
  <li>5 cups chopped Porcini mushrooms</li>
  <!-- ... -->
</ul>

We’re not going to start shipping vegetable clip art with the framework, but you can use any image you want either as an arbitrary value or configuring it in the listStyleImage section of your theme.

Check out the list style image documentation to learn more.


New hyphens utilities

Ever heard of the &shy; HTML entity? Me neither until we added support for these hyphens-* utilities.

Using hyphens-manual and a carefully placed &shy;, you can tell the browser where to insert a hyphen when it needs to break a word across multiple lines:

Officially recognized by the Duden dictionary as the longest word in German, Kraftfahrzeug­haftpflichtversicherung is a 36 letter word for motor vehicle liability insurance.

<p class="hyphens-manual ...">
    ... Kraftfahrzeug&shy;haftpflichtversicherung is a ...
</p>

Maybe a code snippet like this would be useful to include as part of your unpronounceable death metal band’s press kit so the journalists don’t screw up the hyphenation in the article that finally breaks you on to the scene.

Check out the hyphens documentation to learn more.


New caption-side utilities

Another new one for me — the <caption> element! We’ve got new caption-* utilities you can use on table captions to control whether they appear at the top or bottom of the table they’re attached to.

Table 3.1: Professional wrestlers and their signature moves.
Wrestler Signature Move(s)
"Stone Cold" Steve Austin Stone Cold Stunner, Lou Thesz Press
Bret "The Hitman" Hart The Sharpshooter
Razor Ramon Razor's Edge, Fallaway Slam
<table>
  <caption class="caption-bottom">
    Table 3.1: Professional wrestlers and their signature moves.
  </caption>
  <thead>
    <tr>
      <th>Wrestler</th>
      <th>Signature Move(s)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>"Stone Cold" Steve Austin</td>
      <td>Stone Cold Stunner, Lou Thesz Press</td>
    </tr>
    <tr>
      <td>Bret "The Hitman" Hart</td>
      <td >The Sharpshooter</td>
    </tr>
    <tr>
      <td>Razor Ramon</td>
      <td>Razor's Edge, Fallaway Slam</td>
    </tr>
  </tbody>
</table>

Check out the caption side documentation for some more examples.


So that’s Tailwind CSS v3.3! No breaking changes, just a bunch of fun new stuff. Give it a try in your projects today by updating to the latest version with npm:

npm install -D tailwindcss@latest

Yep, another release without text-shadow utilities. Remember that episode of Seinfeld where Kramer tries to see how far he can drive without stopping for gas? That’s my favorite episode.

(The post Tailwind CSS v3.3: Extended color palette, ESM/TS support, logical properties, and more appeared first on Tailwind CSS Blog.)

December 15, 2022  15:00:00

It’s been months in the making but I’m excited to finally release our next website template — Protocol, a beautiful starter kit for building amazing API reference websites.

Learn about the Protocol template

Powered by Next.js and MDX and styled with Tailwind CSS, it’s built exactly the way we’d build our own API reference documentation.

Play with the live demo or download the source if you’ve got a Tailwind UI all-access license — it’s a free update of course for all-access customers.

Loaded with design details

As usual we had a lot of fun getting carried away with the design, and putting that extra layer of polish on things to make it really delightful to browse the site.

We’ve got sticky code blocks that stay in view as you scroll through the request and response details for that endpoint:

There’s also this beautiful hover effect on the homepage cards — it follows your mouse cursor with this gradient glow that uncovers a subtle background pattern:

My favorite detail though has to be the sidebar navigation, which tracks the visible page content but using a sort of “minimap” strategy, where all visible page sections are highlighted:

Watching this animate as you scroll through the page is really a sight to behold — props to Framer Motion for doing the heavy lifting here as usual. Even if I absolutely hated React I’m pretty sure I’d still use it just to use this library, it’s really that good.

The developer experience we’d want ourselves

We spent a lot of time deciding how to wire up the actual content on this one. We explored a bunch of different options for autogenerating documentation using different standards, but for my tastes anyways it all felt a little restrictive.

Personally I want to be able to just write exactly the documentation I want. So for Protocol, we optimized for maximum control but with a lot of authoring conveniences that make it really easy to write exactly what you want, fast.

You write your endpoint documentation in MDX, mixing in a handful of little components we provide to structure things quickly:

messages.mdx
## Create a message {{ tag: 'POST', label: '/v1/messages' }}

<Row>
  <Col>

    Publishes a new message to a specific conversation.

    ### Required attributes

    <Properties>
      <Property name="conversation_id" type="string">
        Unique identifier for the conversation the message belongs to.
      </Property>
      <Property name="message" type="string">
        The message content.
      </Property>
    </Properties>

  </Col>
  <Col sticky>

    <CodeGroup title="Request" tag="POST" label="/v1/messages">

    ```bash {{ title: 'cURL' }}
    curl https://api.protocol.chat/v1/messages \
      -H "Authorization: Bearer {token}" \
      -d conversation_id="xgQQXg3hrtjh7AvZ" \
      -d message="You're what the French call 'les incompetents.'"
    ```

    ```js
    import ApiClient from '@example/protocol-api'
    const client = new ApiClient(token)
    await client.messages.create({
      conversation_id: 'xgQQXg3hrtjh7AvZ',
      message: 'You're what the French call 'les incompetents.'',
    })
    ```

    </CodeGroup>

    ```json {{ title: 'Response' }}
    {
      "id": "gWqY86BMFRiH5o11",
      "conversation_id": "xgQQXg3hrtjh7AvZ",
      "message": "You're what the French call 'les incompetents.'",
      "reactions": [],
      "created_at": 692233200,
    }
    ```

  </Col>
</Row>

This will produce documentation that looks like this:

Example of generated documentation from MDX source

To really nail the authoring experience, we even built mdx-annotations — a new library that brings the annotations feature we loved when working with Markdoc over to MDX.

It lets you pass props into tags in MDX content by annotating them with an object, like this heading:

## Create a message {{ tag: 'POST', label: '/v1/messages' }}

…which is translated into this JSX:

<Heading level={2} tag="POST" label="/v1/messages">Create a message</Heading>

This lets you move quite a bit faster because you can keep writing in Markdown and not have to drop into raw JSX just to pass along some extra data.

Adaptable design

I think this template is going to be really useful to lots of people right off-the-shelf, so it was important to us that it was easy to customize the design to match your brand.

We deliberately designed the illustrated background pattern we use in the site to feel “on brand” for basically anyone — you can tell it’s the work of a professional designer but it’s simple and leans into the “technical” motif, which is something that all API reference sites are going to have in common anyways.

The illustrated background pattern included in this template

We built the pattern in code rather than exporting it as an asset with all of the colors baked in, so it’s easy to tweak it to match your own color scheme.

For syntax highlighting, we’re using Shiki with the css-variables theme, which makes it easy to update the syntax highlighting for your brand by picking just 9 colors:

:root {
  --shiki-color-text: theme('colors.white');
  --shiki-token-constant: theme('colors.emerald.300');
  --shiki-token-string: theme('colors.emerald.300');
  --shiki-token-comment: theme('colors.zinc.500');
  --shiki-token-keyword: theme('colors.sky.300');
  --shiki-token-parameter: theme('colors.pink.300');
  --shiki-token-function: theme('colors.violet.300');
  --shiki-token-string-expression: theme('colors.emerald.300');
  --shiki-token-punctuation: theme('colors.zinc.200');
}

This is a hell of a lot less work than trying to craft your own theme from scratch!

In addition to the four icons we’ve used in our demo, we’ve included another 24 icons for a bunch of common API resource types:

Check out this screenshot, where we’ve adapted the Protocol template as if it were being used by our friends at ConvertKit to power their API reference:

Looks a lot different at a quick glance, but when you really dig in there’s actually not much that has changed here at all — just updating some button and link colors, the logo, adjusting the gradient in the illustration, and picking some different syntax highlighting colors.

Dark mode

Naturally the site includes dark mode support — it’s meant for developers, do you really think we could possibly be that ignorant? You would never forgive us.

The Protocol template design in dark mode

The dark mode version has lots of its own cool design details too — I love the different primary button treatment for instance.

Command palette with Algolia DocSearch integration

We love Algolia for documentation search, and we use it for the Tailwind CSS website as well as in our Syntax template.

We’ve wired it up for Protocol as well, but this time using Algolia’s headless autocomplete library so we had total control of the search UI:

The nice thing about this approach is we can use regular old utility classes to style everything instead of writing custom CSS to style an already-styled widget, which just feels a lot more right in a Tailwind CSS project.


And that’s it — one last Tailwind UI template to finish out 2022! We’ve got another one almost ready to go too, so keep an eye out for that in the new year. Going to have some pretty damn exciting Tailwind CSS v4.0 news to share soon too!

(The post Protocol: A beautiful starting point for your next API documentation site appeared first on Tailwind CSS Blog.)

October 19, 2022  15:30:00

Well it’s that time again! The time where we quickly go from “I really have no idea what we could even add to a new Tailwind release” to “wow, well this is actually a ridiculous amount of new stuff — we better tag a release before things get completely out of hand”.

Tailwind CSS v3.2

Tailwind CSS v3.2 is here with an absolutely massive amount of new stuff, including support for dynamic breakpoints, multiple config files in a single project, nested groups, parameterized variants, container queries, and more.

As always check out the release notes for every nitty-gritty fix and improvement, but here’s the highlight reel:

Upgrade your projects by installing the latest version of tailwindcss from npm:

npm install -D tailwindcss@latest

Or play with the new features in Tailwind Play where you can try everything out instantly, right in the browser.


Multiple config files in one project using @config

We’ve added a new @config directive that you can use in a CSS file to specify which Tailwind CSS config to use for that file:

@config "./tailwind.admin.config.js";
@tailwind base;
@tailwind components;
@tailwind utilities;

This makes it a lot easier to build multiple stylesheets in a single project that have separate Tailwind configurations. For example, you might have one config file for the customer-facing part of your site, and another config for the admin/backend area.

You’ve always technically been able to do this with enough webpack wizardry, but the new @config directive makes it super easy and accessible to everyone, even in projects where you don’t have as much control over the build tool configuration.


Browser-support-based styling with supports-*

You can now conditionally style things based on whether a certain feature is supported in the user’s browser with the supports-[...] variant, which generates @supports rules under the hood.

<div class="flex supports-[display:grid]:grid ...">
  <!-- ... -->
</div>

The supports-[...] variant takes anything you’d use with @supports (...) between the square brackets, like a property/value pair, and even expressions using and and or.

If you only need to check if a property itself is supported, you can even just specify the property name and Tailwind will fill in the blanks for you:

<div class="bg-black/75 supports-[backdrop-filter]:bg-black/25 supports-[backdrop-filter]:backdrop-blur ...">
  <!-- ... -->
</div>

ARIA attribute variants

You can now conditionally style things based on ARIA attributes with the new aria-* variants.

For example, you can update the background color of an element based on whether the aria-checked state is true:

<span class="bg-gray-600 aria-checked:bg-blue-600" aria-checked="true" role="checkbox">
  <!-- ... -->
</span>

By default we’ve included modifiers for the most common boolean ARIA attributes:

ModifierCSS
aria-checked&[aria-checked=“true”]
aria-disabled&[aria-disabled=“true”]
aria-expanded&[aria-expanded=“true”]
aria-hidden&[aria-hidden=“true”]
aria-pressed&[aria-pressed=“true”]
aria-readonly&[aria-readonly=“true”]
aria-required&[aria-required=“true”]
aria-selected&[aria-selected=“true”]

You can customize which aria-* modifiers are available by editing theme.aria or theme.extend.aria in your tailwind.config.js file:

tailwind.config.js
module.exports = {
  theme: {
    extend: {
      aria: {
        asc: 'sort="ascending"',
        desc: 'sort="descending"',
      },
    },
  },
};

If you need to use a one-off aria modifier that doesn’t make sense to include in your theme, or for more complex ARIA attributes that take specific values, use square brackets to generate a property on the fly using any arbitrary value.

Invoice # Client Amount
#100 Pendant Publishing $2,000.00
#101 Kruger Industrial Smoothing $545.00
#102 J. Peterman $10,000.25
<table>
  <thead>
    <tr>
      <th
        aria-sort="ascending"
        class="aria-[sort=ascending]:bg-[url('/img/down-arrow.svg')] aria-[sort=descending]:bg-[url('/img/up-arrow.svg')]"
      >
        Invoice #
      </th>
      <!-- ... -->
    </tr>
  </thead>
  <!-- ... -->
</table>

ARIA state modifiers can also target parent and sibling elements using the group-aria-* and peer-aria-* modifiers:

<table>
  <thead>
    <tr>
    <th aria-sort="ascending" class="group">
      Invoice #
      <svg class="group-aria-[sort=ascending]:rotate-0 group-aria-[sort=descending]:rotate-180"><!-- ... --></svg>
    </th>
    <!-- ... -->
    </tr>
  </thead>
  <!-- ... -->
</table>

Data attribute variants

You can now conditionally style things based on data attributes with the new data-* variants.

Since there are no standard data-* attributes by definition, we only support arbitrary values out of the box, for example:

<!-- Will apply -->
<div data-size="large" class="data-[size=large]:p-8">
  <!-- ... -->
</div>

<!-- Will not apply -->
<div data-size="medium" class="data-[size=large]:p-8">
  <!-- ... -->
</div>

<!-- Generated CSS -->
<style>
  .data-\[size\=large\]\:p-8[data-size="large"] {
    padding: 2rem;
  }
</style>

You can configure shortcuts for common data attribute selectors you’re using in your project under the data key in the theme section of your tailwind.config.js file:

// tailwind.config.js
module.exports = {
  theme: {
    data: {
      checked: 'ui~="checked"',
    },
  },
  // ...
};
<div data-ui="checked active" class="data-checked:underline">
  <!-- ... -->
</div>

These variants also work as group-* and peer-* variants like many other variants in the framework:

<div data-size="large" class="group">
  <div class="group-data-[size=large]:p-8">
    <!-- Will apply `p-8` -->
  </div>
</div>

<div data-size="medium" class="group">
  <div class="group-data-[size=large]:p-8">
    <!-- Will not apply `p-8` -->
  </div>
</div>

Max-width and dynamic breakpoints

We’ve added a new max-* variant that lets you apply max-width media queries based on your configured breakpoints:

<div class="max-lg:p-8">
  <!-- Will apply `p-8` until the `lg` breakpoint kicks in -->
</div>

As a general rule I would still recommend using min-width breakpoints personally, but this feature does unlock one useful workflow benefit which is not having to undo some style at a different breakpoint.

For example, without this feature you often end up doing things like this:

<div class="md:sr-only xl:not-sr-only">
  <!-- ... -->
</div>

With this feature, you can avoid undoing that style by stacking a max-* variant on the original declaration:

<div class="md:max-xl:sr-only">
  <!-- ... -->
</div>

Along with this, we’ve added support for arbitrary values, and a new min-* variant that only accepts arbitrary values, so you can do things like this:

<div class="min-[712px]:max-[877px]:right-16 ...">
  <!-- ... -->
</div>

It’s important to note that these features will only be available if your project uses a simple screens configuration.

These features are a lot more complicated than they look due to needing to ensure that all of these media queries are sorted in the final CSS in a way that gives you the expected behavior in the browser. So for now, they will only work if your screens configuration is a simple object with string values, like the default configuration:

// tailwind.config.js
module.exports = {
  theme: {
    screens: {
      sm: "640px",
      md: "768px",
      lg: "1024px",
      xl: "1280px",
      "2xl": "1536px",
    },
  },
};

If you have a complex configuration where you already have max-width breakpoints defined, or range-based media queries, or anything other than just strings, these features won’t be available. We might be able to figure that out in the future but it just creates so many questions about how the CSS should be ordered that we don’t have answers for yet.

So for now (and possibly forever), if you want to use these features, your screens configuration needs to be simple. My hope is that these features make complex screens configurations unnecessary anyways.


Dynamic group-* and peer-* variants

It’s now possible to create custom group-* and peer-* variants on the fly by passing your own selector to be “groupified” or “peerified” between square brackets:

<div class="group is-published">
  <div class="hidden group-[.is-published]:block">
    Published
  </div>
</div>

For more control, you can use the & character to mark where .group or .peer should end up in the final selector relative to the selector you are passing in:

<div>
  <input type="text" class="peer" />
  <div class="hidden peer-[:nth-of-type(3)_&]:block">
    <!-- ... -->
  </div>
</div>

Let’s be serious you’re probably going to use these features like three times in your entire life but it’s still pretty cool. Hoping we can use this as a building block to make group and peer work more automatically with variants registered by third-party plugins in the future.


Dynamic variants with matchVariant

You’ve probably noticed this new variant-[...] syntax in a lot of these new features — this is all powered by a new matchVariant plugin API that makes it possible to create what we’re calling “dynamic variants”.

Here’s an example of creating a placement-* variant for some imaginary tooltip library that uses a data-placement attribute to tell you where the tooltip is currently positioned:

let plugin = require("tailwindcss/plugin");

module.exports = {
  // ...
  plugins: [
    plugin(function ({ matchVariant }) {
      matchVariant(
        "placement",
        (value) => {
          return `&[data-placement=${value}]`;
        },
        {
          values: {
            t: "top",
            r: "right",
            b: "bottom",
            l: "left",
          },
        }
      );
    }),
  ],
};

The variant defined above would give you variants like placement-t and placement-b, but would also support the arbitrary portion in square brackets, so if this imaginary tooltip library had other potential values that you didn’t feel the need to create built-in values for, you could still do stuff like this:

<div class="placement-[top-start]:mb-2 ...">
  <!-- ... -->
</div>

When defining a custom variant with this API, it’s often important that you have some control over which order the CSS is generated in to make sure each class has the right precedence with respect to other values that come from the same variant. To support this, there’s a sort function you can provide when defining your variant:

matchVariant("min", (value) => `@media (min-width: ${value})`, {
  sort(a, z) {
    return parseInt(a) - parseInt(z);
  },
});

Nested group and multiple peer support using variant modifiers

Sometimes you can run into problems when you have multiple group chunks nested within each other because Tailwind has no real way to disambiguate between them.

To solve this, we’re adding support for variant modifiers, which are a new dynamic chunk that you can add to the end of a variant (inspired by our optional opacity modifier syntax) that you can use to give each group/peer your own identifier.

Here’s what it looks like:

<div class="group/sidebar ...">
  <!-- ... -->
  <div class="group/navitem ...">
    <a
      href="#"
      class="opacity-50 group-hover/sidebar:opacity-75 group-hover/navitem:bg-black/75"
    >
      <!-- ... -->
    </a>
  </div>
  <!-- ... -->
</div>

This lets you give each group a clear name that makes sense for that context on the fly, and Tailwind will generate the necessary CSS to make it work.

I’m really excited to have a solution out there for this because it’s something I’ve been trying to land on a good approach for solving for several years, and this is the first thing we’ve come up with that really feels like it offers the power and flexibility I think it should.


Container queries

I can barely believe it but container queries are finally real and the browser support is dangerously close to making these ready for production — in fact if you’re building an Electron app you could use these today.

Today we’re releasing @tailwindcss/container-queries which is a new first-party plugin that adds container query support to the framework, using a new @ syntax to differentiate them from normal media queries:

<div class="@container">
  <div class="block @lg:flex">
    <!-- ... -->
  </div>
</div>

Out-of-the-box we include a set of container sizes that match our default max-width scale:

NameValue
xs20rem
sm24rem
md28rem
lg32rem
xl36rem
2xl42rem
3xl48rem
4xl56rem
5xl64rem
6xl72rem
7xl80rem

You can configure which values are available using the containers key in your tailwind.config.js file:

// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      containers: {
        2xs: '16rem',
        // etc...
      },
    },
  },
}

We also include support for arbitrary values, using the @[...] syntax:

<div class="@container">
  <div class="block @[618px]:flex">
    <!-- ... -->
  </div>
</div>

…and named containers using the same variant modifier syntax we’re now shipping for group-* and peer-* variants:

<div class="@container/main">
  <!-- ... -->
  <div>
    <div class="block @lg/main:flex">
      <!-- ... -->
    </div>
  </div>
</div>

Right now we’re starting with simple min-width based container queries, but we plan to expand the scope over time, and when it feels like we’ve really nailed the APIs we’ll bring it all into core.

For complete documentation, check out the plugin on GitHub.


So there you have it — Tailwind CSS v3.2! Major improvements but just a minor version change, so no breaking changes and you should be able to update your project by just updating your dependency:

npm install -D tailwindcss@latest

Yeah I hear you in the back, still no text shadows, but hey at least you can style the sibling of a checkbox when the checkbox’s parent is the third child in a list without leaving your HTML. Priorities people.

(The post Tailwind CSS v3.2: Dynamic breakpoints, multi-config, and container queries, oh my! appeared first on Tailwind CSS Blog.)

September 9, 2022  16:24:00

We just released a stunning new personal website template for Tailwind UI, redesigned Heroicons from scratch, tagged a new version of Headless UI with some exciting new features, and more.

It’s been a busy week wrapping up a bunch of projects we’ve been working on, but everything is finally out the door so it feels like a good time for another update.

We triaged and resolved another 226 GitHub issues and pull requests since my last post, and we’re finally at under 50 open issues/PRs across our entire organization for the first time ever. So please stop finding bugs I just want to design and build beautiful stuff with Tailwind CSS for a while.

Speaking of beautiful stuff, here’s what shipped over the last couple of weeks!


Spotlight: Your new personal website

A couple of days ago we released Spotlight, a stunning new personal website template we designed for Tailwind UI.

Learn about the Spotlight template

Like our other templates, it’s built with Next.js, and this time we’re using MDX to power all of the markdown-driven stuff like the blog section.

Designing this template was a pretty fun and interesting challenge — we wanted to come up with something that was really beautiful and inspiring, but that was also unopinionated enough to feel like a good fit for almost anyone. We landed on a pretty minimalist design that gets it’s personality from little touches like rotated images, hints of color for links, and some subtle depth from shadows and layering in places like the top navigation.

Preview the Spotlight template

As always, I recommend checking out the live preview for the full experience — especially pay attention to the way the avatar and navigation works on the home page as you scroll, it’s very *chef’s kiss* when you’re playing with the real site.

We tried to structure the site the way we’d structure our own personal sites, so it includes a dedicated blog, a page for you to list some of your favorite projects you’ve worked on, an area for you to link to things like conference talks you’ve given, and a “uses” page to list all of your favorite tools and gear.

If you’ve got a Tailwind UI all-access license then you’ve already got access to this template! And if you don’t, consider it — it’s the best way to support our work on open-source projects like Tailwind CSS, Headless UI, and Heroicons.


Heroicons v2.0

Last year we released Heroicons v1.0. Well last week we released Heroicons v2.0, which is a brand new icon set, illustrated from scratch that Steve has been working on for about a year.

Heroicons v2.0

It includes 280 icons drawn in three distinct styles:

  • Outline — line icons with a 1.5px stroke, drawn in a 24px view box.
  • Solid — solid icons with filled shapes, drawn in a 24px view box.
  • Mini — solid icons with filled shapes, drawn in a 20px view box.

The biggest differences from v1 are that the outline set uses a thinner stroke, which feels a bit more modern and fashionable these days, and visually the icons are a bit more playful in style.

Even though these have “v2” in the name, it’s better to think of Heroicons v2 more like Terminator 2 than OpenSSL 2 — we feel like they represent our best work but it’s a new icon set, not strictly an upgrade from the original icon set. Don’t feel pressured to upgrade existing projects like you would with a real application dependency, but if you want to migrate, check out the release notes for everything you need to switch.

To explore all of the new icons, visit the totally redesigned Heroicons website we launched with the new set.


Headless UI v1.7

Earlier this week we tagged a new release of Headless UI, our React and Vue libraries of unstyled UI components.

Headless UI v1.7

Headless UI v1.7 includes the usual slew of bug fixes and improvements, but also some really useful new features!

Added “by” prop for controlling object comparisons

We’ve added a new by prop to the Listbox, Combobox, and RadioGroup components that make it a lot less cumbersome to bind an object as the form value.

The by prop lets you specify which property of an object should be used for comparisons, so that the bound value and the corresponding value in the list of potential values no longer have to be the exact same object instance:

import { Listbox } from '@headlessui/react'

const departments = [
  { id: 1, name: 'Marketing', contact: 'Durward Reynolds' },
  { id: 2, name: 'HR', contact: 'Kenton Towne' },
  { id: 3, name: 'Sales', contact: 'Therese Wunsch' },
  { id: 4, name: 'Finance', contact: 'Benedict Kessler' },
  { id: 5, name: 'Customer service', contact: 'Katelyn Rohan' },
]

function DepartmentPicker({ selectedDepartment, onChange }) {
  return (
    <Listbox value={selectedDepartment} by="id" onChange={onChange}>
      <Listbox.Button>{selectedDepartment.name}</Listbox.Button>
      <Listbox.Options>
        {departments.map((department) => (
          <Listbox.Option key={department.id} value={department}>
            {department.name}
          </Listbox.Option>
        ))}
      </Listbox.Options>
    </Listbox>
  )
}

This makes it a lot easier for the value to come from outside the component, and saves you having to just bind the id or similar and do a bunch of lookups yourself to find the full object when needed.

Check out the updated “Binding objects as values” documentation for each component for some more detail.

Use form controls as uncontrolled components

The Listbox, Combobox, and RadioGroup components now let you optionally pass a defaultValue instead of a value, allowing you to use them as an uncontrolled component.

import { Listbox } from '@headlessui/react'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
]

function Example() {
  return (
    <form action="/projects/1/assignee" method="post">
      <Listbox name="assignee" defaultValue={people[0]}>
        <Listbox.Button>{({ value }) => value.name}</Listbox.Button>
        <Listbox.Options>
          {people.map((person) => (
            <Listbox.Option key={person.id} value={person}>
              {person.name}
            </Listbox.Option>
          ))}
        </Listbox.Options>
      </Listbox>
      <button>Submit</button>
    </form>
  )
}

This can simplify your code when using traditional HTML forms or form APIs that collect their state using FormData instead of tracking it using React state.

Check out the “Using as an uncontrolled component” documentation for each component to see some more examples.

Data attributes for CSS-only state styling

Historically, you’ve always had to style the different states of a Headless UI component by inspecting arguments passed through a render prop and conditionally rendering whatever classes or content made sense. This could feel like a lot of boilerplate when just trying to tweak a background color or make some other CSS-only change.

In Headless UI v1.7, we’ve added a data-headlessui-state attribute to the rendered HTML that includes information about the current state so you can target it with just CSS.

We’ve also released a new @headlessui/tailwindcss plugin that gives you variants for these states so they are super easy to style with just Tailwind CSS classes:

<Listbox.Option
  key={person.id}
  value={person}
  className="ui-active:bg-blue-500 ui-active:text-white ui-not-active:bg-white ui-not-active:text-black"
>
  <CheckIcon className="hidden ui-selected:block" />
  {person.name}
</Listbox.Option>

Check out the new documentation on styling using data attributes for some more details.


Insiders support on Tailwind Play

Not everyone knows this but we ship an insiders build of Tailwind CSS to npm that is automatically built and deployed every single time a new commit lands in the repository. This makes it really easy to test out new features and fixes before they are actually tagged in a proper release.

Well now we include access to the insiders build in Tailwind Play as well, so you can play with bleeding edge stuff without even setting up a project:

Tailwind Play insiders option

We only keep the latest insiders build on Play, so if you create a demo using an insiders build know that it might break if the next insiders build changes something in some unreleased feature you were using. You shouldn’t be putting important things there anyways, come on be a professional.


Tailwind CSS + Phoenix v1.7

A while ago we started talking with the Phoenix team because they wanted to ship Tailwind CSS by default in a future release. I thought this was super exciting, and wanted to work with them to make the out-of-the-box experience really beautiful.

We designed a new splash screen and all of the necessary scaffolding for their generator system, which will ship as part of Phoenix v1.7.

New designs for Phoenix v1.7

Chris McCord the creator of Phoenix gave a great talk last week that walks through all of the Tailwind CSS stuff they are shipping, worth a watch if you’re curious to learn more.


So there you go, that’s all of the coolest stuff we’ve been working on over the last few weeks!

Over the next month or so I’m excited to build a bunch of new Tailwind UI components we’ve been designing, explore some new feature ideas for Tailwind CSS, and start doing some R&D on what it would look like to create a sort of application starter kit template with Tailwind + Next.js — think it could be pretty cool if we can nail it.

Catch you in the next update!

(The post We built you a new personal website + Heroicons v2.0, Headless UI v1.7, and more appeared first on Tailwind CSS Blog.)

August 19, 2022  12:45:00

All about the brand new Tailwind UI template we just shipped, the official Tailwind CSS job board, and a bunch of new projects coming out in the next few weeks.

Got another update for you today on some cool things we’ve shipped, and some other things that are in the works but shipping soon!

Since the Tailwind UI site templates and all-access release back in June we’ve been doing a lot of maintenance-focused stuff, like processing over 350 GitHub issues and pull requests and updating all of the new templates we released to use Next.js 12.2 and the new next/future/image component.

We did find time to ship a few new things in there too though!


Tailwind + Framer Motion = Pocket

About a week ago we shipped a brand new Tailwind UI template, built with Next.js and (of course) Tailwind CSS.

We’re calling it Pocket, and it’s a mobile app landing page loaded with tons of fun animations and interactions, powered by Framer Motion which is basically the coolest library anyone has ever made.

Learn about the Pocket template

Be sure to check out the live preview for the full experience — screenshots miss all the coolest parts, like the graph that draws in on page load, the animated mobile device UI in the features section, and the Frogger-inspired testimonials animation.

Funny story about this template — when we initially designed it and built it we actually didn’t plan to do anything in terms of animations or interactions. We put together a static design in Figma that we were all really excited about, then built the whole thing out with a plan to ship it like three and a half weeks ago.

But once we had the finished template in the browser it just kinda felt stiff. It looked great in Figma where you sort of expect everything to be still and static, but once we could see it in real life it felt more like a screenshot or something instead of a real, interactive website.

Take a look at this earlier version of the template and maybe you’ll see what I mean.

We decided to delay the release for a couple of weeks while we experimented with some things we could do to breathe some life into it, and after trying a bunch of different ideas ended up with what we have today.

I’m super stoked about how this one turned out, and I think the codebase is a really interesting case study to pour over if you want to learn how to use Framer Motion to pull off some of the cool effects we landed on.

And like all of our templates, if you’ve got a Tailwind UI all-access license, it’s included alongside all of our other components and templates at no extra cost.


Tailwind Jobs

We’ve tossed around the idea of building an official Tailwind CSS job board for about two years, and finally made the decision in the last few weeks to give it a shot and see what happens.

If you’re a company that uses Tailwind and are looking for front-end engineers or you’re a developer who wants to work at a company that uses Tailwind, check out Tailwind Jobs to learn more about it.

Tailwind Jobs

We broke ground on the codebase on July 14th, and launched the site to companies on August 2nd. We built it with Laravel, Inertia, React, and of course Tailwind CSS. Pretty amazing what you can build with tools like these in just 20 days!

We’re still figuring out the positioning and what we can do to make it the best place for companies using Tailwind to find front-end talent, so if you do any hiring at your company and have any feedback or ideas, shoot Peter an email — he’s taking the lead on this project and is looking for people to talk to about it.

One thing we’re trying to figure out for example is how to make it clear that jobs don’t have to be strictly “styling things with Tailwind CSS all day” to be a good fit — there’s really not a lot of jobs like that in the world, we don’t even have any jobs like that here at the company that actually makes Tailwind CSS itself!

But man a lot of people hate wrestling with CSS, even if they’re not writing it every single day.

There’s ton of developers out there who would be ecstatic to know that whenever they do have to do some styling, they’ll get do it with Tailwind instead of some custom CSS spaghetti.

We want the job board to be a place to find React developers, Vue developers, Laravel developers, Rails developers, whatever — as long as the person would get to use Tailwind when it’s time to style something, it’s a good fit for Tailwind Jobs.


Work in progress…

We’ve got a bunch of cool stuff in the works that will be coming out in the next few weeks:

So look for another update in a couple weeks with a bunch more stuff! With the big templates/all-access release behind us I’m excited to switch gears from creating brand new products and spend the rest of the year focused on making Tailwind CSS and Headless UI even better, and adding tons of awesome new stuff to Tailwind UI.

(The post New Tailwind CSS + Framer Motion template and Tailwind Jobs appeared first on Tailwind CSS Blog.)

June 23, 2022  19:40:00

When I was early in my programming career, I loved following thoughtbot. They were always writing interesting programming articles, producing fantastic screencasts, and publishing incredible books. I could tell they really cared about their craft and it inspired the hell out of me.

But when they launched Upcase, the thing I was most excited about wasn’t the courses or weekly videos. What I was most excited about was the source code for Upcase itself — the actual-in-production Rails codebase that they gave you access to when you subscribed.

I’d learned a ton from blog posts, books, and screencasts, but I still craved the chance to dig into a real-world codebase crafted by people I trust and study how they put all that knowledge together to build a production-ready application.

That experience was transformative for me, and so for a long time I’ve wanted to give people something like it to learn how real websites are built with Tailwind CSS.

How a layout is broken down into components, what sort of APIs those components expose, how the config has been customized — all of the interesting decisions that you can’t see when you just click view source in the browser.

So today we’re releasing our first batch of official Tailwind CSS website templates — beautiful designs engineered into production-quality codebases, powered by Tailwind CSS and Next.js.

We started designing these way back in March and I’m really excited to get these first five out the door so you can check them out and start playing with them:

  • Syntax — a documentation site template powered by Stripe’s new Markdoc library that’s perfect for things like open-source projects or product documentation.
  • Primer — a landing page template for ebooks and courses that bakes in a lot of the best practices we’ve learned releasing our own info products.
  • Salient — a stunning but simple SaaS marketing website you can quickly adapt for your next idea without burning time you wish you were spending on the actual product.
  • Keynote — a beautiful conference website design that works just as well for small local gatherings as it does for international events.
  • Transmit — an awesome podcasting template with a custom persistent audio player that you can wire up to your own RSS feed in no time.

We’ve put a ton of time and attention into both the design and the code, and everything is structured exactly the way we’d do it in a real production project. They’re the perfect starting point when you don’t want to start from a blank canvas, and the best way to learn how experts build modern websites with Tailwind CSS.

Each template can be purchased on its own for $99, or you can get all of them with all-access — the new get-everything-we-ever-add-forever package we’re launching today.


Going all in on all-access

When we started working on templates, one of the things I was really sure about is that I wanted it to be easy to get access to all of them.

This was important to me for two reasons:

  1. I want people to think of our templates more like educational resources than traditional website themes. Every template we build has its own unique learning moments, and I don’t want people to have to be selective about what they get to learn from.

  2. I don’t want to worry about ROI on a per-template basis. Nothing would suck the fun out of designing and building templates like having to make sure each one had mass appeal. I want us to have the freedom to make niche, interesting stuff with cool ideas to study, not just templates that can sell enough copies on their own to justify the work we put into them.

When I was a kid I went to this music festival in France where almost a hundred bands performed. There were a couple of bands on the bill I absolutely loved, knew every song, and would buy a ticket to see any day of the week.

Most of them though were bands I really liked, but wouldn’t have made the 90 minute drive to make it out to a show. But putting all of them on a bill together got me to buy a plane ticket to Paris, and have one of the best experiences of my life.

I want Tailwind UI to feel like a music festival, where you can wander from stage to stage, enjoy a song or two from a band you’ve never heard of, and once in a while be blown away by a performance you would have never otherwise bothered to check out.

So we’re launching a new all-access package for Tailwind UI that includes access to every template and component package that exists today and any new content we add in the future.

Get Tailwind UI all-access

It’s a one-time purchase of $299. No subscription, no upgrade costs — if we add something new to Tailwind UI, it’s yours, forever.

And if you already own everything in Tailwind UI, we’ve upgraded you to all-access for free.


So there you have it — that’s everything we’ve been working on for the last few months!

We’ve got a bunch of new templates in the works already, and we’re actively working on extracting a lot of the component ideas from these templates into new examples to include in the Marketing, Application UI, and Ecommerce component categories.

That’s one of my favorite things about working on these site templates — building real-world stuff with Tailwind is the best way for us to come up with new component ideas, and to find ways to improve both Tailwind CSS and Headless UI too.

Look forward to lots of new Tailwind UI stuff in the coming months!

(The post Tailwind UI: Site templates and all-access appeared first on Tailwind CSS Blog.)

June 7, 2022  15:00:00

It’s been about six months since we released Tailwind CSS v3.0, and even though we’ve been collecting a lot of little improvements in the codebase since then, we just didn’t have that-one-feature yet that makes you say “okay, it’s release-cuttin’ time”.

Then on a random Saturday night a couple of weeks ago, I was talking to Robin in our Discord about coming up with a way to target the html element using :has and a class deeper in the document, and explained how I thought it would look if we added support for arbitrary variants — something I’ve wanted to tackle for over a year:

Adam Wathan: I think if we do arbitrary variants, the syntax should just be that exact thing, '[html:has(&)]:bg-blue-500'. Feel like that is pretty flexible, like anything you can do with a real variant you can also do with an arbitrary variant since they are the same thing. '[&>*:not(:first-child)]:pl-4'. Robin: This is going to break my brain haha because '[html:has(&)]:bg-blue-500' would be used as a literal inside the '&'. That in combination with other variants... 🤯. Adam Wathan: 😅 it'll be a brain melter for sure. The CSS would be this lol 'html:has(\[html\:has\(\&\)\]\:bg-blue-500 { background: blue 500 }'. Robin: exactly haha. ok, now I want to try that brb.

Twenty minutes later Robin had a working proof of concept (in six lines of code!), and after another hour or so of Jordan performing regex miracles in our class detection engine, arbitrary variants were born and we had our release-worthy feature.

So here it is — Tailwind CSS v3.1! For a complete list of every fix and improvement check out the release notes, but here’s the highlights:

Upgrade your projects by installing the latest version of tailwindcss from npm:

npm install tailwindcss@latest

Or spin up a Tailwind Play to play around with all of the new goodies right in the browser.


First-party TypeScript types

We’re now shipping types for all of our JS APIs you work with when using Tailwind, most notably the tailwind.config.js file. This means you get all sorts of useful IDE support, and makes it a lot easier to make changes to your configuration without referencing the documentation quite as much.

To set it up, just add the type annotation above your config definition:

tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    // ...
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

If you’re a big TypeScript nerd you might enjoy poking around the actual type definitions — lots of interesting stuff going on there to support such a potentially complex object.

Built-in support for CSS imports in the CLI

If you’re using our CLI tool to compile your CSS, postcss-import is now baked right in so you can organize your custom CSS into multiple files without any additional configuration.

@import "tailwindcss/base";
@import "./select2-theme.css";
@import "tailwindcss/components";
@import "tailwindcss/utilities";

If you’re not using our CLI tool and instead using Tailwind as a PostCSS plugin, you’ll still need to install and configure postcss-import yourself just like you do with autoprefixer, but if you are using our CLI tool this will totally just work now.

This is especially handy if you’re using our standalone CLI and don’t want to install any node dependencies at all.

Change color opacity when using the theme function

I don’t think tons of people know about this, but Tailwind exposes a theme() function to your CSS files that lets you grab values from your config file — sort of turning them into variables that you can reuse.

select2-theme.css
.select2-dropdown {
  border-radius: theme(borderRadius.lg);
  background-color: theme(colors.gray.100);
  color: theme(colors.gray.900);
}
/* ... */

One limitation though was that you couldn’t adjust the alpha channel any colors you grabbed this way. So in v3.1 we’ve added support for using a slash syntax to adjust the opacity, like you can with the modern rgb and hsl CSS color functions:

select2-theme.css
.select2-dropdown {
  border-radius: theme(borderRadius.lg);
  background-color: theme(colors.gray.100 / 50%);
  color: theme(colors.gray.900);
}
/* ... */

We’ve made this work with the theme function in your tailwind.config.js file, too:

tailwind.config.js
module.exports = {
  content: [
    // ...
  ],
  theme: {
    extend: {
      colors: ({ theme }) => ({
        primary: theme('colors.blue.500'),
        'primary-fade': theme('colors.blue.500 / 75%'),
      })
    },
  },
  plugins: [],
}

You can even use this stuff in arbitrary values which is pretty wild — honestly surprisingly useful for weird custom gradients and stuff:

<div class="bg-[image:linear-gradient(to_right,theme(colors.red.500)_75%,theme(colors.red.500/25%))]">
  <!-- ... -->
</div>

Anything to avoid editing a CSS file am I right?

Easier CSS variable color configuration

If you like to define and configure your colors as CSS variables, you probably have some horrible boilerplate like this in your tailwind.config.js file right now:

tailwind.config.js
function withOpacityValue(variable) {
  return ({ opacityValue }) => {
    if (opacityValue === undefined) {
      return `rgb(var(${variable}))`
    }
    return `rgb(var(${variable}) / ${opacityValue})`
  }
}

module.exports = {
  theme: {
    colors: {
      primary: withOpacityValue('--color-primary'),
      secondary: withOpacityValue('--color-secondary'),
      // ...
    }
  }
}

We’ve made this way less awful in v3.1 by adding support for defining your colors with a format string instead of having to use a function:

tailwind.config.js
module.exports = {
  theme: {
    colors: {
      primary: 'rgb(var(--color-primary) / <alpha-value>)',
      secondary: 'rgb(var(--color-secondary) / <alpha-value>)',
      // ...
    }
  }
}

Instead of writing a function that receives that opacityValue argument, you can just write a string with an <alpha-value> placeholder, and Tailwind will replace that placeholder with the correct alpha value based on the utility.

If you haven’t seen any of this before, check out our updated Using CSS variables documentation for more details.

Border spacing utilities

We’ve added new set of utilities for the border-spacing property, so you can control the space between table borders when using separate borders:

State City
Indiana Indianapolis
Ohio Columbus
Michigan Detroit
<table class="border-separate border-spacing-2 ...">
  <thead>
    <tr>
      <th class="border border-slate-300 ...">State</th>
      <th class="border border-slate-300 ...">City</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td class="border border-slate-300 ...">Indiana</td>
      <td class="border border-slate-300 ...">Indianapolis</td>
    </tr>
    <!-- ... -->
  </tbody>
</table>

I know what you’re thinking — “I have never in my life wanted to build a table that looks like that…” — but listen for a second!

One situation where this is actually super useful is when building a table with a sticky header row and you want a persistent bottom border under the headings:

Scroll this table to see the sticky header row in action

Name Role
Courtney Henry Admin
Tom Cook Member
Whitney Francis Admin
Leonard Krasner Owner
Floyd Miles Member
Emily Selman Member
Kristin Watson Admin
Emma Dorsey Member
Alicia Bell Admin
Jenny Wilson Owner
Anna Roberts Member
Benjamin Russel Member
Jeffrey Webb Admin
Kathryn Murphy Member
<table class="border-separate border-spacing-0">
  <thead class="bg-gray-50">
    <tr>
      <th class="sticky top-0 z-10 border-b border-gray-300 ...">Name</th>
      <th class="sticky top-0 z-10 border-b border-gray-300 ...">Email</th>
      <th class="sticky top-0 z-10 border-b border-gray-300 ...">Role</th>
    </tr>
  </thead>
  <tbody class="bg-white">
    <tr>
      <td class="border-b border-gray-200 ...">Courtney Henry</td>
      <td class="border-b border-gray-200 ...">[email protected]</td>
      <td class="border-b border-gray-200 ...">Admin</td>
    </tr>
    <!-- ... -->
  </tbody>
</table>

You might think you could just use border-collapse here since you actually don’t want any space between the borders but you’d be mistaken. Without border-separate and border-spacing-0, the border will scroll away instead of sticking under the headings. CSS is fun isn’t it?

Check out the border spacing documentation for some more details.

Enabled and optional variants

We’ve added new variants for the :enabled and :optional pseudo-classes, which target form elements when they are, well, enabled and optional.

“But Adam why would I ever need these, enabled and optional aren’t even states, they are the defaults. Do you even make websites?”

Ouch, that hurts because it’s true — I pretty much just write emails and answer the same questions over and over again on GitHub now.

But check out this disabled button example:

<button type="button" class="bg-indigo-500 hover:bg-indigo-400 disabled:opacity-75 ..." disabled>
  Processing...
</button>

Notice how when you hover over the button, the background still changes color even though it’s disabled? Before this release, you’d usually fix that like this:

<button type="button" class="disabled:hover:bg-indigo-500 bg-indigo-500 hover:bg-indigo-400 disabled:opacity-75  ..." disabled>
  Processing...
</button>

But with the new enabled modifier, you can write it like this instead:

<button type="button" class="bg-indigo-500 hover:enabled:bg-indigo-400 disabled:opacity-75  ..." disabled>
  Processing...
</button>

Instead of overriding the hover color back to the default color when the button is disabled, we combine the hover and enabled variants to just not apply the hover styles when the button is disabled in the first place. I think that’s better!

Here’s an example combining the new optional modifier with our sibling state features to hide a little “Required” notice for fields that aren’t required:

Required
Required
<form>
  <div>
    <label for="email" ...>Email</label>
    <div>
      <input required class="peer ..." id="email" />
      <div class="peer-optional:hidden ...">
        Required
      </div>
    </div>
  </div>
  <div>
    <label for="name" ...>Name</label>
    <div>
      <input class="peer ..." id="name" />
      <div class="peer-optional:hidden ...">
        Required
      </div>
    </div>
  </div>
  <!-- ... -->
</form>

This lets you use the same markup for all of your form groups and letting CSS handle all of the conditional rendering for you instead of handling it yourself. Kinda neat!

Prefers-contrast variants

Did you know there’s a prefers-contrast media query? Well there is, and now Tailwind supports it out of the box.

Use the new contrast-more and contrast-less variants to modify your design when the user has requested more or less contrast, usually through an operating system accessibility preference like “Increase contrast” on macOS.

Try emulating `prefers-contrast: more` in your developer tools to see the changes

We need this to steal your identity.

<form>
  <label class="block">
    <span class="block text-sm font-medium text-slate-700">Social Security Number</span>
    <input class="border-slate-200 placeholder-slate-400 contrast-more:border-slate-400 contrast-more:placeholder-slate-500"/>
    <p class="mt-2 opacity-10 contrast-more:opacity-100 text-slate-600 text-sm">
      We need this to steal your identity.
    </p>
  </label>
</form>

I wrote some documentation for this but honestly I wrote more here than I did there.

Style native dialog backdrops

There’s a pretty new HTML <dialog> element with surprisingly decent browser support that is worth playing with if you like to live on the bleeding edge.

Dialogs have this new ::backdrop pseudo-element that’s rendered while the dialog is open, and Tailwind CSS v3.1 adds a new backdrop modifier you can use to style this baby:

<dialog class="backdrop:bg-slate-900/50 ...">
  <form method="dialog">
    <!-- ... -->
    <button value="cancel">Cancel</button>
    <button>Submit</button>
  </form>
</dialog>

I recommend reading the MDN Dialog documentation if you want to dig in to this thing more — it’s exciting stuff but there’s a lot to know.

Arbitrary values but for variants

Okay so this one is the real highlight for me — you know how we give you the addVariant API for creating your own custom variants?

tailwind.config.js
const plugin = require('tailwindcss/plugin')

module.exports = {
  // ...
  plugins: [
    plugin(function({ addVariant }) {
      addVariant('third', '&:nth-child(3)')
    })
  ]
}

…and you know how we have arbitrary values for using any value you want with a utility directly in your HTML?

<div class="top-[117px]">
  <!-- ... -->
</div>

Well Tailwind CSS v3.1 introduces arbitrary variants, letting you create your own ad hoc variants directly in your HTML:

<div class="[&:nth-child(3)]:py-0">
  <!-- ... -->
</div>

This is super useful for variants that sort of feel like they need to be parameterized, for example adding a style if the browser supports a specific CSS feature using a @supports query:

<div class="bg-white [@supports(backdrop-filter:blur(0))]:bg-white/50 [@supports(backdrop-filter:blur(0))]:backdrop-blur">
  <!-- ... -->
</div>

You can even use this feature to target child elements with arbitrary variants like [&>*]:

<ul role="list" class="[&>*]:p-4 [&>*]:bg-white [&>*]:rounded-lg [&>*]:shadow space-y-4">
  <li class="flex">
    <img class="h-10 w-10 rounded-full" src="..." alt="" />
    <div class="ml-3 overflow-hidden">
      <p class="text-sm font-medium text-slate-900">Kristen Ramos</p>
      <p class="text-sm text-slate-500 truncate">[email protected]</p>
    </div>
  </li>
  <!-- ... -->
</ul>

You can even style the first p inside the div in the second child li but only on hover:

Try hovering over the text “Floyd Miles”

<ul role="list" class="hover:[&>li:nth-child(2)>div>p:first-child]:text-indigo-500 [&>*]:p-4 [&>*]:bg-white [&>*]:rounded-lg [&>*]:shadow space-y-4">
  <!-- ... -->
  <li class="flex">
    <img class="h-10 w-10 rounded-full" src="..." alt="" />
    <div class="ml-3 overflow-hidden">
      <p class="text-sm font-medium text-slate-900">Floyd Miles</p>
      <p class="text-sm text-slate-500 truncate">[email protected]</p>
    </div>
  </li>
  <!-- ... -->
</ul>

Now should you do this? Probably not very often, but honestly it can be a pretty useful escape hatch when trying to style HTML you can’t directly change. It’s a sharp knife, but the best chefs aren’t preparing food with safety scissors.

Play with them a bit and I’ll bet you find they are a great tool when the situation calls for it. We’re using them in a couple of tricky spots in these new website templates we’re working on and the experience is much nicer than creating a custom class.


So that’s Tailwind CSS v3.1! It’s only a minor version change, so there are no breaking changes and you should be able to update your project by just installing the latest version:

npm install tailwindcss@latest

For the complete list of changes including bug fixes and a few minor improvements I didn’t talk about here, dig in to the release notes on GitHub.

I’ve already got a bunch of ideas for Tailwind CSS v3.2 (maybe even text shadows finally?!), but right now we’re working hard to push these new website templates over the finish line. Look for another update on that topic in the next week or two!

(The post Tailwind CSS v3.1: You wanna get nuts? Come on, let's get nuts! appeared first on Tailwind CSS Blog.)

May 27, 2022  15:00:00

It’s been a while since I’ve written about what we’ve been working on so I have a lot to share! Too much honestly — my main motivator for even getting this update out is that we’ve got even more stuff coming next week, and I feel like I’m not allowed to share that stuff until I share all of the stuff we’ve already shipped.

So put your swim suit on, sit back in your lounge chair, and prepare to soak up some vitamin CSS.


Headless UI v1.6 is out

A few weeks ago we released a new minor version of Headless UI, the unstyled UI library we built to make it possible to add React and Vue support to Tailwind UI.

Check out the release notes for all of the details, but here are some of the highlights.

Multiselect support

We’ve added a new multiple prop to both the Combobox and Listbox components so people can select more than one option.

Just add the multiple prop and bind an array as your value and you are ready to go:

function MyCombobox({ items }) {
  const [selectedItems, setSelectedItems] = useState([])

  return (
    <Combobox value={selectedItems} onChange={setSelectedItems} multiple>
      {selectedItems.length > 0 && (
        <ul>
          {selectedItems.map((item) => (
            <li key={item}>{item}</li>
          ))}
        </ul>
      )}
      <Combobox.Input />
      <Combobox.Options>
        {items.map((item) => (
          <Combobox.Option key={item} value={item}>
            {item}
          </Combobox.Option>
        ))}
      </Combobox.Options>
    </Combobox>
  )
}

Check out the combobox documentation and listbox documentation for more.

Nullable comboboxes

Prior to v1.6, if you deleted the contents of a combobox and tabbed away, it would restore the previously selected option. This makes sense a lot of the time, but sometimes you really do want to clear the value of a combobox.

We’ve added a new nullable prop that makes this possible — just add the prop and now you can delete the value without the previous value being restored:

function MyCombobox({ items }) {
  const [selectedItem, setSelectedItem] = useState([])

  return (
    <Combobox value={selectedItems} onChange={setSelectedItem} nullable>
      <Combobox.Input />
      <Combobox.Options>
        {items.map((item) => (
          <Combobox.Option key={item} value={item}>
            {item}
          </Combobox.Option>
        ))}
      </Combobox.Options>
    </Combobox>
  )
}

Easy HTML form support

Now if you add a name prop to form components like Listbox, Combobox, Switch, and RadioGroup, we’ll automatically create a hidden input that syncs with the component’s value.

This makes it super easy to send that data to the server with a regular form submission, or with something like the <Form> component in Remix.

<form action="/projects/1/assignee" method="post">
  <Listbox
    value={selectedPerson}
    onChange={setSelectedPerson}
    name="assignee"
  >
    {/* ... */}
  </Listbox>
  <button>Submit</button>
</form>

This works with simple values like numbers and string, but also with objects — we automatically serialize them into multiple fields using that square bracket notation from 1996:

<input type="hidden" name="assignee[id]" value="1" />
<input type="hidden" name="assignee[name]" value="Durward Reynolds" />

Check out the documentation if you want to read exactly what I just wrote all over again but on a different domain.

Scrollable dialog improvements

Dialogs are literally the hardest thing to build on the planet. We’ve been wrestling with gnarly scrolling issues for a while now, and think we’ve finally got it all sorted out in v1.6.

The crux of it is that we’ve changed how “click outside to close” works. We used to use this Dialog.Overlay component that you put behind your actual dialog, and we had a click handler on that that would close the dialog on click. I actually really love the simplicity of this in principle — it’s a lot less quirky to detect when a specific element is clicked than it is to detect when anything other than a specific element is clicked, especially when you have things rendered inside your dialog that themselves are rendering other things in portals and stuff.

The problem with this approach is that if you had a long dialog that required scrolling, your overlay would sit on top of your scrollbar, and trying to click the scrollbar would close the dialog. Not what you want!

So to fix this in a non-breaking way, we’ve added a new Dialog.Panel component you can use instead, and now we close the dialog any time you click outside of that component, rather than closing it specifically when the overlay is clicked:

<Dialog
  open={isOpen}
  onClose={closeModal}
  className="fixed inset-0 flex items-center justify-center ..."
>
  <Dialog.Overlay className="fixed inset-0 bg-black/25" />
  <div className="fixed inset-0 bg-black/25" />

  <div className="bg-white shadow-xl rounded-2xl ...">
  <Dialog.Panel className="bg-white shadow-xl rounded-2xl ...">
    <Dialog.Title>Payment successful</Dialog.Title>
    {/* ... */}
  </div>
  </Dialog.Panel>
</Dialog>

Check out the updated dialog documentation for more complete examples using the new panel component instead of the overlay.

Better focus trapping

One of the many reasons dialogs are the hardest thing to build on the planet is because of focus trapping. Our first attempt at this involved hijacking the tab key and manually focusing the next/previous element, so that we could circle back to the first item in the focus trap when you get to the end.

This works okay until people start using portals inside the focus trap. Now it’s basically impossible to manage because you could tab to a datepicker or something that is conceptually inside the dialog, but isn’t actually because it’s rendered in a portal for styling reasons.

Robin came up with a really cool solution for this that is super simple — instead of trying to manually control how tabbing works, just throw an invisible focusable element at the beginning of the focus trap and another one at the end. Now whenever one of these sentinel elements receives focus you just move focus to where it actually should be, based on whether you’re at the first element or the last element and whether the user was tabbing forwards or backwards.

With this approach, you don’t have to hijack the tab key at all — you just let the browser do all of the work and only move focus manually when one of your sentinel elements receives focus.

After figuring this out we noticed a couple of other libraries already doing the same thing so it’s nothing groundbreaking or new, but I thought it was pretty damn clever and worth sharing for anyone who hadn’t thought of this technique.


Team management features for Tailwind UI

When we first released Tailwind UI, the “team” was just me and Steve, so we had to keep a lot of things simple if we wanted any chance of actually getting the thing out the door with just the two of us working on it.

One of those things was team licensing. We didn’t ship with any fancy team member invitation flow or anything, we just asked people to share their Tailwind UI credentials with their team. This was good enough for us to get things out the door, because Tailwind UI doesn’t really do anything in a user-specific way, and every member of your team gets the same experience anyways.

Plus to us, having to get the email addresses of everyone on your team, enter them into some form, send each person an invitation email, and have them accept the invitation felt like administrative hell, especially when every single person gets the same experience after they sign in.

At the same time though, sharing credentials for anything is pretty low-end, and it’s not a design decision we took a lot of pride in. I use the same password (slayerfan1234) for Tailwind UI as I do for my bank account — I don’t want to share that with anyone!

So a couple of weeks ago we decided to figure it out and build something.

Interface with a copyable invite URL and list of team members

What we landed on was a purely link based invitation system, where you could just copy your invite link, share it with your team in Slack/Discord/whatever, and reset your link if needed. You can also give people either “Member” or “Owner” permissions, which control whether they can manage team members or view billing history.

This makes it super easy to invite your team without a bunch of tedious data entry, and revoke access if someone leaves right in the UI instead of by changing your shared password.

This is available now for anyone with a Tailwind UI team account — just open the dropdown menu and click “My Team” to name your team and start inviting your co-workers.

You can purchase a license for your team on the Tailwind UI website, or upgrade to a team license if you have a personal license and want to start using Tailwind UI with your team.


Updating the Vue examples in Tailwind UI to <script setup>

Since releasing Vue support for Tailwind UI, the new <script setup> syntax in Vue 3 has become the recommended way to write your single-file components.

We’ve updated all of the Vue examples in Tailwind UI to use this new format, which cuts out a ton of boilerplate:

<template>
  <Listbox as="div" v-model="selected">
    <!-- ... -->
  </Listbox>
</template>

<script setup>
import { ref } from 'vue'
import { Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions } from '@headlessui/vue'
import { CheckIcon, SelectorIcon } from '@heroicons/vue/solid'

const people = [
  { id: 1, name: 'Wade Cooper' },
  // ...
]

const selected = ref(people[3])
</script>

To me the absolute best part is that you don’t have to explicitly register anything under components anymore — any components that are in scope are automatically available to the template.

Using <script setup> also lets you use namespaced components like Listbox.Button like we do in the React flavor of Headless UI. We haven’t updated Headless UI to expose the components this way yet but we’re probably going to do it soon, which will let you shave off a ton of imports.


New Tailwind CSS language mode for VS Code

Tailwind uses a bunch of non-standard at-rules like @tailwind and @apply, so you get lint warnings in VS Code if you use the regular CSS language mode.

To get around this, we’ve always recommended people use the PostCSS Language Support plugin which gets rid of those warnings, but also removes all of the other CSS IntelliSense support.

So a few weeks ago we released a first-party Tailwind CSS language mode as part of our Tailwind CSS IntelliSense extension, which builds on the built-in CSS language mode to add Tailwind-specific syntax highlighting and fix the lint warnings you’d usually see, without losing any of CSS IntelliSense features you do want to keep.

Sample CSS code shown with lint warnings when using built-in CSS language mode, and no lint warnings when using the Tailwind CSS language mode.

Try it out by downloading the latest version of Tailwind CSS IntelliSense and choosing “Tailwind CSS” as the language mode for your CSS files.


“Generated CSS” panel in Tailwind Play

We’ve made a bunch of little improvements to Tailwind Play over the last couple of months, with my favorite being the new “Generated CSS” panel.

Tailwind Play interface with a panel showing the CSS generated for that playground.

It shows you all of the CSS that was generated from your HTML and lets you filter by layer, which is incredibly useful for troubleshooting. Internally we are using this all the time to debug weird issues around classes not being detected so we can perform whatever horrific regex surgery is necessary to make it work.

We also added a “Tidy” button (Cmd + S) to each pane that will automatically format your code (and sort your classes!) and a “Copy” button (Cmd + A Cmd + C, but you already know that) too.


Redesigning the Refactoring UI website

When we released Refactoring UI back in December 2018, Steve and I literally designed and built the final landing page the night before launch at like 1am.

What happened is we had this whole sexy landing page designed, then I was writing up the announcement email to send to everyone on our mailing list and we both thought “man the content in this email is great and a lot more compelling than what we have in this landing page design”.

But that content didn’t really fit into what we had designed, so at the eleventh hour we scrapped everything we had designed and whipped together a much simpler page based on the new content. It looked fine but it wasn’t the super beautiful experience we really wanted it to be.

So a few weeks ago we decided to finally design something new.

Header section of redesigned Refactoring UI website.

I’m still extremely proud of this book — probably more so than anything we’ve ever made. It’s got a 4.68 rating on Goodreads with over 1100 ratings and almost 200 reviews, which feels pretty incredible to me for a self-published ebook.

Looking forward to doing a second edition one day with everything we’ve learned since!


Tailwind CSS templates are coming soon

We’ve teased this a bit on Twitter, but for the last couple of months we’ve been working really hard on a bunch of full-fledged Tailwind CSS website templates.

Here’s a sneak peek at one of them — a documentation site template built with Next.js and Stripe’s new Markdoc library:

Artboards for a documentation site design that includes mobile and desktop layouts, and light and dark color schemes.

I’m unreasonably excited about getting these out. I’m really proud of Tailwind UI as a product, but one of the limitations of the copy-and-pasteable-code-snippet format is that we don’t get an opportunity to really show you how to componentize things, minimize duplication, and architect things as a complete, production-ready website.

The templates we’re working on now are going to be amazing at filling that gap. On top of just getting beautiful templates to use as a starting point for your own projects, you’ll be able to dig through the code and study exactly how we build websites with Tailwind CSS ourselves.

We haven’t set an exact release date on these yet but we’re hoping to have something out next month. Will share more as we make more progress!

(The post Headless UI v1.6, Tailwind UI team management, Tailwind Play improvements, and more appeared first on Tailwind CSS Blog.)

February 24, 2022  12:00:00

We just released Headless UI v1.5, which includes a brand new Combobox component. Comboboxes are like select controls but with autocomplete/typeahead functionality, and are a great alternative to a regular select when you’re working with large datasets and want to quickly filter for the right option.

Headless UI v1.5

Like all other Headless UI components, the combobox abstracts away all of the complex accessibility considerations but leaves the styling completely up to you, giving you total control to design exactly the combobox you want without worrying about things like keyboard navigation or screen reader support.

Here’s a quick demo if you’d like to see it in action:

Wade Cooper
Arlene McCoy
Devon Webb
Tom Cook
Tanya Fox
Hellen Schmidt

We’ve intentionally designed it so that you have full control over filtering the actual results. You can do basic string comparisons, use a fuzzy search library like Fuse.js, or even make server-side requests to an API — whatever makes sense for your project.

Here’s what it looks like to filter the results using a basic string comparison:

import { useState } from 'react'
import { Combobox } from '@headlessui/react'

const people = [
  'Wade Cooper',
  'Arlene McCoy',
  'Devon Webb',
  'Tom Cook',
  'Tanya Fox',
  'Hellen Schmidt',
]

function MyCombobox() {
  const [selectedPerson, setSelectedPerson] = useState(people[0])
  const [query, setQuery] = useState('')

  const filteredPeople =
    query === ''
      ? people
      : people.filter((person) => {
          return person.toLowerCase().includes(query.toLowerCase())
        })

  return (
    <Combobox value={selectedPerson} onChange={setSelectedPerson}>
      <Combobox.Input onChange={(event) => setQuery(event.target.value)} />
      <Combobox.Options>
        {filteredPeople.map((person) => (
          <Combobox.Option key={person} value={person}>
            {person}
          </Combobox.Option>
        ))}
      </Combobox.Options>
    </Combobox>
  )
}

Command palettes

Comboboxes are not only great as standalone inputs, but they can also be used as a lower-level primitive for building more complex components, such as command palettes.

This is actually what originally motivated us to create the combobox component in the first place — we wanted to add a new command palettes category to Tailwind UI and needed this component to make that happen.

If you happen to have a Tailwind UI license, be sure to browse the new Command Palettes category to see how these turned out. And if you’re wondering, we also added a new Comboboxes category as well.

Command palette from Tailwind UI

Riding on the excitement of the new command palettes, we also just published a new in-depth screencast on building a command palette from scratch with Tailwind CSS, React and Headless UI.

It covers tons of interesting Tailwind tricks for getting the design and animations just right, and teaches you a ton about how to use the new combobox component and wire it into your app.

Try it out

If you already have Headless UI installed in your project, be sure to upgrade to v1.5 to get the new Combobox component. This is a minor update so there are no breaking changes.

# For React
npm install @headlessui/react

# For Vue
npm install @headlessui/vue

Be sure to also check out the official website for the latest documentation.

(The post Headless UI v1.5: The One With Comboboxes appeared first on Tailwind CSS Blog.)

January 24, 2022  19:00:00

People have been talking about the best way to sort your utility classes in Tailwind projects for at least four years. Today we’re excited to announce that you can finally stop worrying about it with the release of our official Prettier plugin for Tailwind CSS.

This plugin scans your templates for class attributes containing Tailwind CSS classes, and then sorts those classes automatically following our recommended class order.

HTML
<!-- Before -->
<button class="text-white px-4 sm:px-8 py-2 sm:py-3 bg-sky-700 hover:bg-sky-800">...</button>

<!-- After -->
<button class="bg-sky-700 px-4 py-2 text-white hover:bg-sky-800 sm:px-8 sm:py-3">...</button>

It works seamlessly with custom Tailwind configurations, and because it’s just a Prettier plugin, it works anywhere Prettier works — including every popular editor and IDE, and of course on the command line.

To get started, install prettier-plugin-tailwindcss as a dev-dependency:

Terminal
npm install -D prettier prettier-plugin-tailwindcss

Then add the plugin to your Prettier configuration file:

.prettierrc
{
  "plugins": ["prettier-plugin-tailwindcss"]
}

You can also load the plugin by using the --plugin flag with the Prettier CLI, or by using the plugins option with the Prettier API.


How classes are sorted

At its core, all this plugin does is organize your classes in the same order that Tailwind orders them in your CSS.

This means that any classes in the base layer will be sorted first, followed by classes in the components layer, and then finally classes in the utilities layer.

<!-- `container` is a component so it comes first -->
<div class="container mx-auto px-6">
  <!-- ... -->
</div>

Utilities themselves are sorted in the same order we sort them in the CSS as well, which means that any classes that override other classes always appear later in the class list:

<div class="pt-2 p-4">
<div class="p-4 pt-2">
  <!-- ... -->
</div>

The actual order of the different utilities is loosely based on the box model, and tries to put high impact classes that affect the layout at the beginning and decorative classes at the end, while also trying to keep related utilities together:

<div class="text-gray-700 shadow-md p-3 border-gray-300 ml-4 h-24 flex border-2">
<div class="ml-4 flex h-24 border-2 border-gray-300 p-3 text-gray-700 shadow-md">
  <!-- ... -->
</div>

Modifiers like hover: and focus: are grouped together and sorted after any plain utilities:

<div class="hover:opacity-75 opacity-50 hover:scale-150 scale-125">
<div class="scale-125 opacity-50 hover:scale-150 hover:opacity-75">
  <!-- ... -->
</div>

Responsive modifiers like md: and lg: are grouped together at the end in the same order they’re configured in your theme — which is smallest to largest by default:

<div class="lg:grid-cols-4 grid sm:grid-cols-3 grid-cols-2">
<div class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4">
  <!-- ... -->
</div>

Any custom classes that don’t come from Tailwind plugins (like classes for targeting a third-party library) are always sorted to the front, so it’s easy to see when an element is using them:

<div class="p-3 shadow-xl select2-dropdown">
<div class="select2-dropdown p-3 shadow-xl">
  <!-- ... -->
</div>

Customization

We think Prettier gets it right when it comes to being opinionated and offering little in terms of customizability — at the end of the day the biggest benefit to sorting your classes is that it’s just one less thing to argue with your team about.

We’ve tried really hard to come up with a sort order that is easy to understand and communicates the most important information as fast as possible.

The plugin will respect your tailwind.config.js file and work with any Tailwind plugins you’ve installed, but there is no way to change the sort order. Just like with Prettier, we think that the benefits of auto-formatting will quickly outweigh any stylistic preferences you have and that you’ll get used to it pretty fast.

Ready to try it out? Check out the full documentation on GitHub →

(The post Automatic Class Sorting with Prettier appeared first on Tailwind CSS Blog.)

December 17, 2021  18:50:00

Today we’re announcing the next version of the Tailwind CSS Typography plugin, which brings easy dark mode support, a brand new customization API, and the not-prose class I wasn’t sure we’d ever figure out how to support.

Today we’re announcing the next version of the Tailwind CSS Typography plugin, which brings easy dark mode support, a brand new customization API, and the not-prose class I wasn’t sure we’d ever figure out how to support.

For a full tour of everything that’s new, check out the official release video on our YouTube channel.

Tailwind CSS Typography v0.5 is designed for Tailwind CSS v3.0, so make sure you’re on the latest version of Tailwind, then install the new plugin release from npm:

npm install -D @tailwindcss/typography@latest

To learn more about everything the plugin provides, check out our update typography plugin documentation.


Easy dark mode support

We’ve added a prose-invert class you can use to easily swap out your typography colors in dark mode:

<body class="bg-white dark:bg-gray-900">
  <article class="prose dark:prose-invert">
    {{ markdown }}
  </article>
</body>

The dark themes are hand-crafted by our expert design team, and automatically adapt to whatever gray scale you’re using.


Pick your gray scale

Tailwind CSS v3.0 ships with five different sets of grays by default, and the updated typography plugin includes classes for each one, making it easy to match your typography to the rest of your site:

<article class="prose prose-slate">
  {{ markdown }}
</article>

We’ve simplified how we define color themes internally too, which makes it easier to add your own if you need to.

Check out the documentation to learn more.


HTML-based customization API

We’ve added tons of modifiers you can use to tweak specific elements in your prose styles, directly in your HTML:

<article class="prose prose-img:rounded-xl prose-headings:underline prose-a:text-blue-600">
  {{ markdown }}
</article>

This makes it easy to do things like style links to match your brand, add a border radius to images, and tons more.

Check out the element modifiers documentation to learn more.


Undo prose styles

Ever needed to stick some non-content HTML in the middle of your content? Now you can wrap that with not-prose to make sure the prose styles don’t interfere with it:

<article class="prose">
  <h1>My Heading</h1>
  <p>...</p>

  <div class="not-prose">
    <!-- Some HTML that needs to be prose-free -->
  </div>

  <p>...</p>
  <!-- ... -->
</article>

Ready to try it out? Check out the typography plugin documentation to learn more and get started.

(The post Effortless Typography, Even in Dark Mode appeared first on Tailwind CSS Blog.)

December 16, 2021  16:00:00

Tailwind CSS is written in JavaScript and distributed as an npm package, which means you’ve always had to have Node.js and npm installed to use it.

Today we’re announcing a new standalone CLI build that gives you the full power of Tailwind CLI in a self-contained executable — no Node.js or npm required.

Tailwind CSS is written in JavaScript and distributed as an npm package, which means you’ve always had to have Node.js and npm installed to use it.

This has made it harder to integrate into projects where using npm isn’t always common, and with tools like Rails and Phoenix both moving away from npm by default, we needed to find a way for people to use Tailwind in these projects without forcing them to adopt an entirely separate ecosystem of tooling.

Today we’re announcing a new standalone CLI build that gives you the full power of Tailwind CLI in a self-contained executable — no Node.js or npm required.


Get started

To install it, grab the executable for your platform from the latest release on GitHub, making sure to give it executable permissions:

Terminal
# Example for macOS arm64
curl -sLO https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-macos-arm64
chmod +x tailwindcss-macos-arm64
mv tailwindcss-macos-arm64 tailwindcss

Now you can use it just like our npm-distributed CLI tool:

Terminal
# Create a tailwind.config.js file
./tailwindcss init

# Start a watcher
./tailwindcss -i input.css -o output.css --watch

# Compile and minify your CSS for production
./tailwindcss -i input.css -o output.css --minify

We’ve even bundled the latest versions of all of our first-party plugins, so if you want to use them in your project, just require them in your tailwind.config.js file like you would in a Node-based project:

tailwind.config.js
module.exports = {
  // ...
  plugins: [
    require('@tailwindcss/forms'),
    require('@tailwindcss/typography'),
  ]
}

You get all the power of our standard npm-distributed CLI in a convenient, portable package — no dependencies required.


How does it work?

We didn’t rewrite Tailwind in Rust or anything (yet…) — we’re actually using pkg, a really cool project by Vercel that lets you turn a Node.js project into an executable that can be run without installing Node.js by bundling all of the parts your project needs right into the executable itself.

This is what makes it possible for you to still use a tailwind.config.js file with the full power of JavaScript, rather than a static format like JSON.


Which CLI should I use?

If you are already using npm in your project, use the npm-distributed version of our CLI that we’ve always provided. It’s simpler to update, the file size is smaller, and you’re already in the ecosystem anyways — there’s no benefit at all to using the standalone build.

If on the other hand you’re working on a project where you don’t otherwise need Node.js or npm, the standalone build can be a great choice. If Tailwind was the only reason you had a package.json file, this is probably going to feel like a nicer solution.

(The post Standalone CLI: Use Tailwind CSS without Node.js appeared first on Tailwind CSS Blog.)

December 9, 2021  16:00:00

Tailwind CSS v3.0 is here — bringing incredible performance gains, huge workflow improvements, and a seriously ridiculous number of new features.

Tailwind CSS v3.0 is here — bringing incredible performance gains, huge workflow improvements, and a seriously ridiculous number of new features.

For a tour of some of the coolest new features, check out the “What’s new in Tailwind CSS v3.0” video on our YouTube channel.

Tailwind CSS v3.0 has got to be our most exciting release ever, including improvements like:

  • Just-in-Time, all the time — lightning fast build times, stackable variants, arbitrary value support, better browser performance, and more.
  • Every color out of the box — including all of the extended palette colors like cyan, rose, fuchsia, and lime, and fifty shades of grey gray.
  • Colored box shadows — for fun glow and reflection effects, and more natural shadows on colored backgrounds.
  • Scroll snap API — a comprehensive and composable set of utilities for CSS-only scroll snapping.
  • Multi-column layout — so you can finally build that online newspaper you’ve been dreaming about.
  • Native form control styling — make checkboxes, radio buttons, and file inputs match your brand without reinventing the wheel.
  • Print modifier — control how your site looks when someone prints it, right from your HTML.
  • Modern aspect ratio API — no more padding hacks, well unless you need to support Safari 14, which you probably do, but still.
  • Fancy underline styles — the missing piece to making that side-project of yours finally take off.
  • RTL and LTR modifiers — for complete control when building multi-directional websites.
  • Portrait and landscape modifiers — honestly just because they were really easy to add.
  • Arbitrary properties — now Tailwind supports CSS properties we’ve never even heard of.
  • Play CDN — the new Just-in-Time engine squeezed into a CDN script that runs right in the browser.
  • Tons of other utilities — including support for touch-action, will-change, flex-basis, text-indent, scroll-behavior, and more.

Plus a beautiful, brand-spanking new documentation website, loaded with improved content and examples on every page.

To start playing with Tailwind CSS v3.0 today, grab the latest release from npm:

npm install -D tailwindcss@latest postcss autoprefixer

…or head over to Tailwind Play to try out the latest features right in the browser.

Tailwind CSS v3.0 is a new major version of the framework and there are some minor breaking changes, but we’ve worked really hard to make the upgrade process as smooth as possible, and for most projects you should be able to install v3.0 without making any changes.

For example, Tailwind UI is probably the biggest Tailwind project on earth and every template is totally compatible with both v2 and v3 with no changes required.

For more details and step-by-step instructions on migrating to v3.0, check out the upgrade guide.


Just-in-Time, all the time

Back in March we introduced the brand new Just-in-Time engine which brought huge performance gains, unlocked exciting new features like arbitrary values, and made complex variant configurations a thing of the past.

In Tailwind CSS v3.0, the new engine has gone stable and replaced the classic engine, so every Tailwind project can benefit from these improvements out of the box.


Every color out of the box

Before the new engine, we always had to be careful with CSS file size in development, and one of the biggest trade-offs we had to make was carefully limiting the color palette.

In v3.0, every single color in the extended color palette is enabled by default, including lime, cyan, sky, fuchsia, rose, and fifty shades of gray.

Check out the color palette reference to learn more.


Colored box shadows

People have been asking us for colored shadows for years, but supporting it in a composable way that actually made sense was way harder than I expected it to be.

After about five false starts, we finally figured out an approach we liked, and now Tailwind CSS v3.0 includes colored shadows:

shadow-cyan-500/50

shadow-blue-500/50

shadow-indigo-500/50

<button class="bg-cyan-500 shadow-lg shadow-cyan-500/50 ...">Subscribe</button>
<button class="bg-blue-500 shadow-lg shadow-blue-500/50 ...">Subscribe</button>
<button class="bg-indigo-500 shadow-lg shadow-indigo-500/50 ...">Subscribe</button>

Learn more in the Box Shadow Color documentation.


Scroll snap API

We’ve added a comprehensive set of utilities for the CSS Scroll Snap module, giving you the power to build very rich scroll snapping experiences directly in your HTML:

Scroll in the grid of images to see the expected behaviour

snap point
<div class="snap-x ...">
  <div class="snap-center ...">
    <img src="https://images.unsplash.com/photo-1604999565976-8913ad2ddb7c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=320&h=160&q=80" />
  </div>
  <div class="snap-center ...">
    <img src="https://images.unsplash.com/photo-1540206351-d6465b3ac5c1?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=320&h=160&q=80" />
  </div>
  <div class="snap-center ...">
    <img src="https://images.unsplash.com/photo-1622890806166-111d7f6c7c97?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=320&h=160&q=80" />
  </div>
  <div class="snap-center ...">
    <img src="https://images.unsplash.com/photo-1590523277543-a94d2e4eb00b?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=320&h=160&q=80" />
  </div>
  <div class="snap-center ...">
    <img src="https://images.unsplash.com/photo-1575424909138-46b05e5919ec?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=320&h=160&q=80" />
  </div>
  <div class="snap-center ...">
    <img src="https://images.unsplash.com/photo-1559333086-b0a56225a93c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=320&h=160&q=80" />
  </div>
</div>

Start with the new Scroll Margin utilities and work your way through the whole API to learn more.


Multi-column layout

We’ve added support for columns — the newspaper layout kind. These are actually super useful, and are great for things like footer navigation layouts too.

Expedita quo ea quod laborum ullam ipsum enim. Deleniti commodi et. Nam id laborum placeat natus eum. Aliquid aut aut soluta nesciunt culpa magni. Velit possimus autem et aut repudiandae culpa rerum. Qui blanditiis ut qui quia expedita necessitatibus sed. Autem sed ut saepe doloremque aut placeat voluptas ipsum.

Eligendi error nisi recusandae velit numquam nihil aperiam enim. Eum et molestias. Id qui cum veritatis id ea quidem ea rerum saepe. Iste itaque fugiat sequi. Voluptatem quae minus. Maxime ullam ea praesentium recusandae vero est quas. Quia minima fugiat aut laborum impedit facere autem sit qui. Et eos et ullam necessitatibus. Ut voluptatem saepe natus itaque maiores sit repellat aut natus assumenda.

Blanditiis ipsa officia dolores exercitationem nemo beatae voluptatem eos rerum velit asperiores. Non quisquam accusantium officia nisi eius necessitatibus.

Quaerat quia ad voluptatem laudantium natus. Aut ipsa et numquam delectus aliquam. Recusandae libero consequatur dolorum. Animi culpa rerum molestiae ut non et molestias aliquid aut nemo. Sint dolorem dolorem. Iure dolorum amet ea sit perferendis.

Et illum ut officia nisi commodi. Quia et mollitia possimus modi. Delectus aliquid quam eos consectetur.

Accusantium et et qui non sed modi. Corrupti deserunt culpa eos vitae neque aperiam. Repellat tenetur fugit.

Deleniti distinctio ad corrupti nisi. Mollitia qui est natus cumque. Officia dolor qui perferendis necessitatibus saepe excepturi asperiores quos voluptas. Est suscipit facere nihil expedita suscipit quibusdam. Quod cupiditate vero distinctio. Sed est soluta nostrum magnam et saepe blanditiis aut. Vero dolores repellendus et libero minima explicabo provident. Culpa aut dolorem est.

<div class="columns-1 sm:columns-3 ...">
  <p>...</p>
  <!-- ... -->
</div>

Learn more in the columns documentation — and check out the new break-after/inside/before utilities as well.


Native form control styling

We’ve added support for the new accent-color property, as well as a modifier for styling file input buttons to make it easier than ever to put your own touch on native form controls:

Current profile photo
<form>
  <div class="flex items-center space-x-6">
    <div class="shrink-0">
      <img class="h-16 w-16 object-cover rounded-full" src="https://images.unsplash.com/photo-1580489944761-15a19d654956?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1361&q=80" alt="Current profile photo" />
    </div>
    <label class="block">
      <span class="sr-only">Choose profile photo</span>
      <input type="file" class="block w-full text-sm text-slate-500
        file:mr-4 file:py-2 file:px-4
        file:rounded-full file:border-0
        file:text-sm file:font-semibold
        file:bg-violet-50 file:text-violet-700
        hover:file:bg-violet-100
      "/>
    </label>
  </div>
  <label class="mt-6 flex items-center justify-center space-x-2 text-sm font-medium text-slate-600">
    <input type="checkbox" class="accent-violet-500" checked/>
    <span>Yes, send me all your stupid updates</span>
  </label>
</form>

Learn more in the accent color and file input buttons documentation.


The new print modifier lets you style how your site should look when animals people print it:

<div>
  <article class="print:hidden">
    <h1>My Secret Pizza Recipe</h1>
    <p>This recipe is a secret, and must not be shared with anyone</p>
    <!-- ... -->
  </article>
  <div class="hidden print:block">
    Are you seriously trying to print this? It's secret!
  </div>
</div>

I’d say “learn more in the print styles documentation” but it wouldn’t be true.


Modern aspect ratio API

We’ve added support for the new native aspect-ratio property, which is starting to see solid browser support:

<iframe class="w-full aspect-video ..." src="https://www.youtube.com/..."></iframe>

Learn more in the aspect ratio documentation.


Fancy underline styles

Now you can change underline colors, thickness, and more:

I’m Derek, an astro-engineer based in Tatooine. I like to build X-Wings at My Company, Inc. Outside of work, I like to watch pod-racing and have light-saber fights.

<p>
  I’m Derek, an astro-engineer based in Tatooine. I like to build X-Wings at
  <a href="#" class="underline decoration-sky-500 decoration-2">My Company, Inc</a>. Outside of work, I
  like to <a href="#" class="underline decoration-pink-500 decoration-dotted decoration-2">watch pod-racing</a>
  and have <a href="#" class="underline decoration-indigo-500 decoration-wavy decoration-2">light-saber</a>
  fights.
</p>

Learn more in the text decoration color, text decoration style, text decoration thickness, and text underline offset documentation.


RTL and LTR modifiers

We’ve added experimental support for multi-directional layouts with new rtl and ltr modifiers:

Left-to-right

Tom Cook

Director of Operations

Right-to-left

تامر كرم

الرئيس التنفيذي

<div class="group flex items-center">
  <img class="shrink-0 h-12 w-12 rounded-full" src="..." alt="" />
  <div class="ltr:ml-3 rtl:mr-3">
    <p class="text-sm font-medium text-slate-700 group-hover:text-slate-900">...</p>
    <p class="text-sm font-medium text-slate-500 group-hover:text-slate-700">...</p>
  </div>
</div>

Learn more in the RTL support documentation.


Portrait and landscape modifiers

Use the new portrait and landscape modifiers to conditionally add styles when the viewport is in a specific orientation:

<div>
  <div class="portrait:hidden">
    <!-- ... -->
  </div>
  <div class="landscape:hidden">
    <p>
      This experience is designed to be viewed in landscape. Please rotate your
      device to view the site.
    </p>
  </div>
</div>

The documentation for this feature has even less content than this section of this post does.


Arbitrary properties

This might be illegal but we’ve made it possible to add totally arbitrary CSS that you can combine with modifiers like hover, lg, and whatever else:

<div class="[mask-type:luminance] hover:[mask-type:alpha]">
  <!-- ... -->
</div>

This is what inline styles want to be when they grow up. Learn more in the arbitrary properties documentation.


Play CDN

There’s no way to make a sensible CSS-based CDN build for Tailwind CSS v3.0 so we had to do something different — we built a JavaScript library.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Example</title>
    <script src="https://cdn.tailwindcss.com/"></script>
  </head>
  <body>
    <!-- -->
  </body>
</html>

Add that script tag to any HTML document and you can use every Tailwind feature, right in the browser. It’s meant for development purposes only, but it sure is a fun way to build little demos or hack on a new idea.

Learn more in the Play CDN documentation.


So there it is — Tailwind CSS v3.0! Head over to the new documentation site to get started with it today.

For a comprehensive list of every single change, check out the changelog on GitHub.

(The post Tailwind CSS v3.0 appeared first on Tailwind CSS Blog.)

August 11, 2021  19:30:00

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.

Almost 6 months in the making, we finally released the first all-new component kit for Tailwind UI since the initial launch back in February 2020!

Tailwind UI Ecommerce available now

Tailwind UI 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:

Preview one of the new product page examples

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” 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.

If you like what you see, consider purchasing a license — 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.

Want to check it out? Visit the Tailwind UI website →

(The post Introducing Tailwind UI Ecommerce appeared first on Tailwind CSS Blog.)

July 29, 2021  12:00:00

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.

Headless UI v1.4

Tabs

Earlier this year we started working on Tailwind UI 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.

Product details interface design from Tailwind UI Ecommerce.

Here’s what we ended up with:

import { Tab } from '@headlessui/react'

function MyTabs() {
  return (
    <Tab.Group>
      <Tab.List>
        <Tab>Tab 1</Tab>
        <Tab>Tab 2</Tab>
        <Tab>Tab 3</Tab>
      </Tab.List>
      <Tab.Panels>
        <Tab.Panel>Content 1</Tab.Panel>
        <Tab.Panel>Content 2</Tab.Panel>
        <Tab.Panel>Content 3</Tab.Panel>
      </Tab.Panels>
    </Tab.Group>
  )
}

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

import { Disclosure } from '@headlessui/react'
import MyLink from './MyLink'

function MyDisclosure() {
  return (
    <Disclosure>
      <Disclosure.Button>Open mobile menu</Disclosure.Button>
      <Disclosure.Panel>
        <Disclosure.Button as={MyLink} href="/home">
          Home
        </Disclosure.Button>
        {/* ... */}
      </Disclosure.Panel>
    </Disclosure>
  )
}

The same thing works with Popover components, too:

import { Popover } from '@headlessui/react'
import MyLink from './MyLink'

function MyPopover() {
  return (
    <Popover>
      <Popover.Button>Solutions</Popover.Button>
      <Popover.Panel>
        <Popover.Button as={MyLink} href="/insights">
          Insights
        </Popover.Button>
        {/* ... */}
      </Popover.Panel>
    </Popover>
  )
}

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:

import { Popover } from '@headlessui/react'

function MyPopover() {
  return (
    <Popover>
      <Popover.Button>Terms</Popover.Button>
      <Popover.Panel>
        {({ close }) => (
          <button
            onClick={async () => {
              await fetch('/accept-terms', { method: 'POST' })
              close()
            }}
          >
            Read and accept
          </button>
        )}
      </Popover.Panel>
    </Popover>
  )
}

For more details, check out the updated Popover and Disclosure 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:

# For React
npm install @headlessui/react

# For Vue
npm install @headlessui/vue

Check out the official website for the latest documentation, and check out Tailwind UI if you want to play tons of styled examples.

Ready to try it out? Visit the Headless UI website →

(The post Headless UI v1.4: The One With Tabs appeared first on Tailwind CSS Blog.)

June 17, 2021  14:00:00

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

For the full details, check out the release notes 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 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:

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.

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 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 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.

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:

<div class="before:block before:bg-blue-500 after:flex after:bg-pink-300"></div>

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:

<div class="before:content-['hello'] before:block ..."></div>

You can even grab the content from an attribute using the CSS attr() function:

<div
  before="hello world"
  class="before:content-[attr(before)] before:block ..."
></div>

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.

We’ve added variants for the first-letter and first-line pseudo-elements, so you can do stuff like drop caps:

<p class="first-letter:text-4xl first-letter:font-bold first-letter:float-left">
  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.
</p>

Selected text variants

This feature is only available in Just-in-Time mode.

We’ve added a new selection variant that makes it super easy to style highlighted to match your design:

<p class="selection:bg-pink-200">
  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.
</p>

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:

<body class="selection:bg-pink-200">
  <!-- ... -->
  <p>
    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.
  </p>
</body>

List marker variants

This feature is only available in Just-in-Time mode.

You can use the new marker variant to style the bullets or numbers at the beginning of a list:

<h1>WrestleMania XII Results</h1>

<ol class="marker:text-gray-500 marker:font-medium">
  <li>
    The British Bulldog, Owen Hart, and Vader defeated Ahmed Johnson, Jake
    Roberts, and Yokozuna
  </li>
  <li>Roddy Piper defeated Goldust</li>
  <li>Stone Cold Steve Austin defeated Savio Vega</li>
  <li>The Ultimate Warrior defeated Hunter Hearst Helmsley</li>
  <li>The Undertaker defeated Diesel</li>
  <li>Shawn Michaels defeated Bret Hart</li>
</ol>

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.

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:

<label>
  <input type="checkbox" class="peer sr-only">
  <span class="h-4 w-4 bg-gray-200 peer-checked:bg-blue-500">
  <!-- ... -->
</label>

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 and looks like this:

.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.

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:

<div class="relative">
  <input id="name" class="peer ...">
  <label for="name" class="peer-placeholder-shown:top-4 peer-focus:top-0 ...">
</div>

Shorthand color opacity syntax

This feature is only available in 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:

<div class="bg-red-500 bg-opacity-25">
<div class="bg-red-500/25">

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:

<div class="bg-gradient-to-r from-red-500/50"></div>

The opacity values are taken from your opacity scale, but you can also use arbitrary opacity values using square bracket notation:

<div class="bg-red-500/[0.31]"></div>

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.

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:

<div class="col-start-[73] placeholder-[#aabbcc] object-[50%] ..."></div>

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:

<!-- Is this a font size utility, or a text color utility? -->
<div class="text-[var(--mystery-var)]"></div>

Now you can provide a hint to the engine by prefixing the arbitrary value with the type name:

<div class="text-[color:var(--mystery-var)]"></div>

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:

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

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

/* 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.

@screen sm { /* ... */ }
@media screen(sm) { /* ... */ }

Caret color utilities

This feature is only available in Just-in-Time mode.

You can now set the color of the cursor in form fields using the new caret-{color} utilities:

<input class="caret-red-500" />

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:

<div
  class="bg-origin-border p-4 border-4 border-dashed ..."
  style="background-image: url(...)"
>
  Background is rendered under the border
</div>

<div
  class="bg-origin-padding p-4 border-4 border-dashed ..."
  style="background-image: url(...)"
>
  Background is rendered within the border but on top of any padding
</div>

<div
  class="bg-origin-content p-4 border-4 border-dashed ..."
  style="background-image: url(...)"
>
  Background is rendered within any padding and under the content
</div>

Learn more in the background origin documentation.

Simplified transform and filter composition

This feature is only available in Just-in-Time mode.

The transform, filter, and backdrop-filter classes are no longer necessary to “enable” their respective set of composable utilities.

<div class="transform scale-50 filter grayscale backdrop-filter backdrop-blur-sm">
<div class="scale-50 grayscale backdrop-blur-sm">

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:

<div class="scale-105 -translate-y-1 hover:transform">
<div class="hover:scale-105 hover:-translate-y-1">

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.

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.

<div
  class="border-2 border-t-blue-500 border-r-pink-500 border-b-green-500 border-l-yellow-500"
>
  <!-- ... -->
</div>

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:

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:

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:

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.


Upgrading

To upgrade to Tailwind CSS v2.2, install the latest release from npm:

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 in the release notes.

Ready to upgrade? Get it from npm →

(The post Tailwind CSS v2.2 appeared first on Tailwind CSS Blog.)

April 14, 2021  16:01:00

Last year we released Tailwind UI — 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.

Tailwind UI: Now for React and Vue

It’s been a long journey 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 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.

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 (
    <Switch
      checked={enabled}
      onChange={setEnabled}
      className={classNames(
        enabled ? 'bg-indigo-600' : 'bg-gray-200',
        'relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500'
      )}
    >
      <span className="sr-only">Use setting</span>
      <span
        aria-hidden="true"
        className={classNames(
          enabled ? 'translate-x-5' : 'translate-x-0',
          'pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200'
        )}
      />
    </Switch>
  )
}

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.

Want to check it out? Visit the Tailwind UI website →

(The post Tailwind UI: Now with React + Vue support appeared first on Tailwind CSS Blog.)

April 14, 2021  16:00:00

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.

Headless UI

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.

import { useState } from 'react'
import { Dialog } from '@headlessui/react'

function MyDialog() {
  let [isOpen, setIsOpen] = useState(true)

  return (
    <Dialog open={isOpen} onClose={setIsOpen}>
      <Dialog.Overlay />

      <Dialog.Title>Deactivate account</Dialog.Title>
      <Dialog.Description>
        This will permanently deactivate your account
      </Dialog.Description>

      <p>
        Are you sure you want to deactivate your account? All of your data will
        be permanently removed. This action cannot be undone.
      </p>

      <button onClick={() => setIsOpen(false)}>Deactivate</button>
      <button onClick={() => setIsOpen(false)}>Cancel</button>
    </Dialog>
  )
}

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.

<template>
  <Disclosure>
    <DisclosureButton> Is team pricing available? </DisclosureButton>
    <DisclosurePanel>
      Yes! You can purchase a license that you can share with your entire team.
    </DisclosurePanel>
  </Disclosure>
</template>

<script>
  import {
    Disclosure,
    DisclosureButton,
    DisclosurePanel,
  } from '@headlessui/vue'

  export default {
    components: { Disclosure, DisclosureButton, DisclosurePanel },
  }
</script>

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.

import { useState } from 'react'
import { RadioGroup } from '@headlessui/react'

function MyRadioGroup() {
  let [plan, setPlan] = useState('startup')

  return (
    <RadioGroup value={plan} onChange={setPlan}>
      <RadioGroup.Label>Plan</RadioGroup.Label>
      <RadioGroup.Option value="startup">
        {({ checked }) => (
          <span className={checked ? 'bg-blue-200' : ''}>Startup</span>
        )}
      </RadioGroup.Option>
      <RadioGroup.Option value="business">
        {({ checked }) => (
          <span className={checked ? 'bg-blue-200' : ''}>Business</span>
        )}
      </RadioGroup.Option>
      <RadioGroup.Option value="enterprise">
        {({ checked }) => (
          <span className={checked ? 'bg-blue-200' : ''}>Enterprise</span>
        )}
      </RadioGroup.Option>
    </RadioGroup>
  )
}

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.

<template>
  <Popover class="relative">
    <PopoverButton>Solutions</PopoverButton>

    <PopoverPanel class="absolute z-10">
      <div>
        <a href="/analytics">Analytics</a>
        <a href="/engagement">Engagement</a>
        <a href="/security">Security</a>
        <a href="/integrations">Integrations</a>
      </div>

      <img src="/solutions.jpg" alt="" />
    </PopoverPanel>
  </Popover>
</template>

<script>
  import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'

  export default {
    components: { Popover, PopoverButton, PopoverPanel },
  }
</script>

TransitionRoot and TransitionChild (for Vue)

Headless UI already had a Transition component for React, but we’ve always recommended the native <transition> that already ships with Vue for Vue users. There are some limitations to the native transition though, and things can get complicated when trying to co-ordinate nested transitions that are supposed to run in parallel.

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.

<template>
  <!-- This `show` prop controls all nested `Transition.Child` components. -->
  <TransitionRoot :show="isOpen">
    <!-- Background overlay -->
    <TransitionChild
      enter="transition-opacity"
      ease-linear
      duration-300"
      enter-from="opacity-0"
      enter-to="opacity-100"
      leave="transition-opacity"
      ease-linear
      duration-300"
      leave-from="opacity-100"
      leave-to="opacity-0"
    >
      <!-- … -->
    </TransitionChild>

    <!-- Sliding sidebar -->
    <TransitionChild
      enter="transition"
      ease-in-out
      duration-300
      transform"
      enter-from="-translate-x-full"
      enter-to="translate-x-0"
      leave="transition"
      ease-in-out
      duration-300
      transform"
      leave-from="translate-x-0"
      leave-to="-translate-x-full"
    >
      <!-- … -->
    </TransitionChild>
  </TransitionRoot>
</template>

<script>
  import { ref } from "vue";
  import { Transition, TransitionChild } from "@headlessui/vue";

  export default {
    components: { TransitionRoot: Transition, TransitionChild },

    setup() {
      const isShowing = ref(true);

      return {
        isShowing,
      };
    },
  };
</script>

Try it out

Head over to our brand new documentation website 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.

Want to try it out? Visit the Headless UI website →

(The post Headless UI v1.0 appeared first on Tailwind CSS Blog.)

April 5, 2021  19:00:00

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:

For the full details, check out the release notes on GitHub.


JIT engine in core

The brand-new JIT engine we announced in March 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:

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

<div class="filter blur-md grayscale invert ...">
  <!-- ... -->
</div>

…and here’s what backdrop-filter looks like:

<div class="backdrop-filter backdrop-blur backdrop-brightness-50 ...">
  <!-- ... -->
</div>

Check out the filter and 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:

<div class="mix-blend-multiply ...">
  <!-- ... -->
</div>

Check out the documentation to learn more.

New isolation utilities

We’ve added new isolate and isolation-auto utilities for working with the isolation property:

<div class="isolate ...">
  <!-- ... -->
</div>

This can be really helpful for scoping blending mode features or z-index adjustments and is super powerful. Check out the documentation to learn more.

I also highly recommend this great article by Josh Comeau 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:

npm install tailwindcss@latest

If you were previously using @tailwindcss/jit, you can now switch back to tailwindcss and update your PostCSS configuration accordingly.

Ready to upgrade? Get it from npm →

(The post Tailwind CSS v2.1 appeared first on Tailwind CSS Blog.)

March 29, 2021  19:00:00

Just over a year ago we released the very first version of Heroicons, 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.

Heroicons

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:

import { BeakerIcon } from '@heroicons/react/solid'

function MyComponent() {
  return (
    <div>
      <BeakerIcon className="h-5 w-5 text-blue-500" />
      <p>...</p>
    </div>
  )
}

Check out the documentation on GitHub to learn more.

Figma Assets

We’ve also published an official Heroicons Figma file 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.

Want to start playing with it? Visit the Heroicons website →

(The post Heroicons v1.0 appeared first on Tailwind CSS Blog.)

March 15, 2021  16:30:00

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.

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.

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.

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

npm install -D @tailwindcss/jit tailwindcss postcss autoprefixer
// 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, then install it, play with it, bend it, break it, and let us know what you think!

Ready to try it out? Get started →

(The post Just-In-Time: The Next Generation of Tailwind CSS appeared first on Tailwind CSS Blog.)

March 8, 2021  19:00:00

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.

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.

Examples of James' previous work
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) 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!

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.

Designs James created for Tailwind UI
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!

Want to keep up with James’ work? Follow James on Twitter →

(The post Welcoming James McDonald to Tailwind Labs appeared first on Tailwind CSS Blog.)

February 16, 2021  16:05:00

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 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
  2. The Utility-First Workflow
  3. Responsive Design
  4. Hover, Focus and Other States
  5. Composing Utilities with @apply
  6. Extracting Reusable Components
  7. Customizing Your Design System
  8. Optimizing for Production

Watch the series on YouTube and grab the source code for each lesson on GitHub.

What are you waiting for? Start watching →

(The post "Tailwind CSS: From Zero to Production" on YouTube appeared first on Tailwind CSS Blog.)

February 1, 2021  13:35:00

We started working with David Luhr last summer on a project-by-project basis to help us develop a Figma version of Tailwind UI (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 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.

Want to keep up with David’s work? Follow David on Twitter →

(The post Welcoming David Luhr to Tailwind Labs appeared first on Tailwind CSS Blog.)

January 24, 2021  20:00:00

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:

npm install @tailwindcss/line-clamp
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:

<p class="line-clamp-3">
  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.
</p>

For more details, check out the documentation over on the GitHub repository.

(The post Multi-line truncation with @tailwindcss/line-clamp appeared first on Tailwind CSS Blog.)

November 18, 2020  17:45:00

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:

…and a bunch of other little things 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 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 to get started fast.

Also how about that brand new website 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.

New Tailwind CSS color palette

We’ve added an extra light 50 shade for every color, so they go from 50–900 now:

<div class="bg-gray-50">I can't believe it's not white.</div>

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.

New Tailwind CSS alternate gray

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:

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.


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:

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:

<div class="bg-white dark:bg-black">
  <h1 class="text-gray-900 dark:text-white">Dark mode</h1>
  <p class="text-gray-500 dark:text-gray-300">
    The feature you've all been waiting for.
  </p>
</div>

Works with hover and stuff too:

<button
  class="bg-gray-900 hover:bg-gray-800 dark:bg-white dark:hover:bg-gray-50"
>
  <!-- ... -->
</button>

And responsive stuff:

<div class="... lg:bg-white lg:dark:bg-black">
  <!-- ... -->
</div>

And responsive hover stuff:

<button class="... lg:dark:bg-white lg:dark:hover:bg-gray-50">
  <!-- ... -->
</button>

Check out the dark mode docs 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:

<h1 class="... 2xl:text-9xl">Godzilla</h1>

Exciting I know but also let’s be serious you’ve been able to add this yourself 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:

<button
  class="... focus:outline-none focus:ring-2 focus:ring-blue-300 focus:ring-opacity-50"
>
  <!-- ... -->
</button>

You can even offset them to create a sort of halo effect with ring-offset-{width} utilities:

<button
  class="... focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-300 focus:ring-opacity-50"
>
  <!-- ... -->
</button>

Using a bunch of CSS custom property voodoo we’ve even made them automatically combine with regular box-shadows, too:

<button class="shadow-sm focus:ring-2 ...">
  <!-- Both the shadow and ring will render together -->
</button>

The ring width documentation 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 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:

<!-- This will be a nice rounded checkbox with an indigo focus ring and an indigo checked state -->
<input
  type="checkbox"
  class="h-4 w-4 rounded border-gray-300 focus:border-indigo-300 focus:ring-2 focus:ring-indigo-200 focus:ring-opacity-50 text-indigo-500"
/>

It’s not included out of the box but you can add it to your tailwind.config.js file with a single line:

tailwind.config.js
module.exports = {
  // ...
  plugins: [require('@tailwindcss/forms')],
}

Check out the @tailwindcss/forms documentation for more information.


Default line-heights per font-size

Every font-size utility in Tailwind now comes paired with a sensible default line-height:

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

<p class="text-xl">This will have a line-height of 1.75rem automatically.</p>

If you want to override this, you can still do it by layering on a leading utility:

<p class="text-3xl leading-normal">Come on don't do this to me.</p>

Check out the font size documentation 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:

<span class="ml-0.5">Just a little nudge.</span>

…as well as a bunch of new values at the top end as well like 72, 80, and 96:

<div class="p-96">This is too much padding.</div>

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:

<div class="top-8">
  <!-- .... -->
</div>

We’ve extended the default typography scale with new 7xl, 8xl, and 9xl values:

<h1 class="text-9xl font-bold">What is this, an Apple website?</h1>

And we’ve also extended the default opacity scale with steps of 10, as well as 5 and 95 values:

<figure class="opacity-5">
  <blockquote>You can't see me.</blockquote>
  <figcaption>John Cena</figcaption>
</figure>

Peruse the entire default config file 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:

.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 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.

<p class="overflow-ellipsis overflow-hidden">
  Look ma no whitespace-nowrap ipsum...
</p>

Check out the new text overflow documentation 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:

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:

tailwind.config.js
module.exports = {
  // ...
  variants: {
    extend: {
      backgroundColor: ['focus-visible'],
    },
  },
}

Giddy up.


Group-hover and focus-within by default

One of the things we learned working on Tailwind UI 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:

<div class="group ...">
  <span class="group-hover:text-blue-600 ...">Da ba dee da ba daa</span>
</div>

Check out the 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:

<button class="... transition duration-150 ease-in-out">Count them</button>

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:

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:

<button class="... transition">Count them again</button>

Of course you can override this by layering on separate duration or timing function utilities:

<button class="... transition duration-300 ease-out">We're back baby</button>

Learn more about transitions in the transition property documentation.


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 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 — 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.

(The post Tailwind CSS v2.0 appeared first on Tailwind CSS Blog.)

October 13, 2020  18:30:00

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…

For the complete summary of changes check out the release notes on GitHub.


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.

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:

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.

<div class="grid grid-flow-col auto-cols-max">
  <div>1</div>
  <div>2</div>
  <div>3</div>
</div>

Here’s a list of the new utilities that are included out of the box:

ClassCSS
auto-cols-autogrid-auto-columns: auto;
auto-cols-mingrid-auto-columns: min-content;
auto-cols-maxgrid-auto-columns: max-content;
auto-cols-frgrid-auto-columns: minmax(0, 1fr);
auto-rows-autogrid-auto-rows: auto;
auto-rows-mingrid-auto-rows: min-content;
auto-rows-maxgrid-auto-rows: max-content;
auto-rows-frgrid-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, 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.

<button class="... focus:outline-none focus:shadow-outline">
  <!-- ... -->
</button>

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.

<!-- Use `outline-white` on dark backgrounds -->
<div class="bg-gray-900">
  <button class="... focus:outline-white">
    <!-- ... -->
  </button>
</div>

<!-- Use `outline-black` on light backgrounds -->
<div class="bg-white">
  <button class="... focus:outline-black">
    <!-- ... -->
  </button>
</div>

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:

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

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:

ClassValue
rounded-xl0.75rem (12px)
rounded-2xl1rem (16px)
rounded-3xl1.5rem(24px)

…and an extended set of smaller values for both the rotate and skew utilities:

ClassValue
rotate-11deg
rotate-22deg
rotate-33deg
rotate-66deg
rotate-1212deg
skew-11deg
skew-22deg

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:

# 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 to simplify the upgrade to Tailwind CSS v2.0 later this fall.

Want to talk about this post? Discuss this on GitHub →

(The post Tailwind CSS v1.9.0 appeared first on Tailwind CSS Blog.)

October 7, 2020  13:00:00

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 file, extract components with @apply, or include 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, 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 and let us know what you think!

Want to talk about this post? Discuss this on GitHub →

(The post Introducing Tailwind Play appeared first on Tailwind CSS Blog.)

October 6, 2020  18:30:00

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 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.

Headless UI Logo

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:

import { Menu } from '@headlessui/react'

function MyDropdown() {
  return (
    <Menu as="div" className="relative">
      <Menu.Button className="px-4 py-2 rounded bg-blue-600 text-white ...">Options</Menu.Button>
      <Menu.Items className="absolute mt-1 right-0">
        <Menu.Item>
          {({ active }) => (
            <a className={`${active && 'bg-blue-500 text-white'} ...`} href="/account-settings">
              Account settings
            </a>
          )}
        </Menu.Item>
        <Menu.Item>
          {({ active }) => (
            <a className={`${active && 'bg-blue-500 text-white'} ...`} href="/documentation">
              Documentation
            </a>
          )}
        </Menu.Item>
        <Menu.Item disabled>
          <span className="opacity-75 ...">Invite a friend (coming soon!)</span>
        </Menu.Item>
      </Menu.Items>
    </Menu>
  )
}

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. Pretty nice that you didn’t have to do that yourself, right?

Here’s a fully-styled live demo (taken from Tailwind UI) 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:

To learn more and dive in, head over to the Headless UI website 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 — something I was really started getting into towards the end of 2017. 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, 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, 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 was the first library I saw that got me excited about this idea back in 2017, Reach UI and Reakit started development in 2018, and React Aria 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.

Want to talk about this post? Discuss this on GitHub →

(The post Headless UI: Unstyled, Accessible UI Components appeared first on Tailwind CSS Blog.)

September 23, 2020  18:30:00

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?” 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?” is a series of 12 short videos that teach you everything you need to know about some of our favorite new Tailwind features, including:

…with more to come soon! You can check out the whole series on our brand new YouTube channel.

And don’t forget to like and subscribe.

Want to talk about this post? Discuss this on GitHub →

(The post "What's new in Tailwind CSS?" on YouTube appeared first on Tailwind CSS Blog.)

September 4, 2020  20:15:00

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)
  • New place-items, place-content, place-self, justify-items, and justify-self utilities (#2306)
  • New preserveHtmlElements option for purge (#2283)
  • New layers mode for purge (#2288)
  • Support configuring variants as functions (#2309)
  • Dark mode variant (experimental) (#2279)

Changes

  • CSS within @layer at-rules are now grouped with the corresponding @tailwind at-rule (#2312)

Deprecations

  • The conservative purge mode has been deprecated in favor of the new layers mode (#2288)

Check out the full release notes on GitHub for more details.

Want to talk about this post? Discuss this on GitHub →

(The post Tailwind CSS v1.8.0 appeared first on Tailwind CSS Blog.)

August 27, 2020  18:30:00

Back in February we released Tailwind UI, 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 <transition> component for enter/leave transitions with a very utility-friendly API:

<transition
  enter-active-class="transition-opacity duration-75"
  enter-from-class="opacity-0"
  enter-to-class="opacity-100"
  leave-active-class="transition-opacity duration-150"
  leave-from-class="opacity-100"
  leave-to-class="opacity-0"
>
  <div v-show="isShowing">
    <!-- Will fade in and out -->
  </div>
</transition>

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, 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 <Transition> component to at least get the current Tailwind UI experience for React users up to par with what’s possible in Vue and Alpine.js.

Here’s what it looks like to use:

import { Transition } from '@tailwindui/react'
import { useState } from 'react'

function MyComponent() {
  const [isOpen, setIsOpen] = useState(false)

  return (
    <div>
      <button onClick={() => setIsOpen(!isOpen)}>Toggle</button>
      <Transition
        show={isOpen}
        enter="transition-opacity duration-75"
        enterFrom="opacity-0"
        enterTo="opacity-100"
        leave="transition-opacity duration-150"
        leaveFrom="opacity-100"
        leaveTo="opacity-0"
      >
        {/* Will fade in and out */}
      </Transition>
    </div>
  )
}

Read the documentation 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.

Want to talk about this post? Discuss this on GitHub →

(The post Utility-Friendly Transitions with @tailwindui/react appeared first on Tailwind CSS Blog.)

August 25, 2020  13:00:00

A few months back we quietly released Heroicons, 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, which makes it easier than ever to search for icons and quickly copy them to your clipboard as Tailwind-ready HTML or JSX.

heroicons.com

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.

<svg
  class="h-6 w-6 text-indigo-500"
  xmlns="http://www.w3.org/2000/svg"
  fill="none"
  viewBox="0 0 24 24"
  stroke="currentColor"
>
  <path
    stroke-linecap="round"
    stroke-linejoin="round"
    stroke-width="2"
    d="M17 8l4 4m0 0l-4 4m4-4H3"
  />
</svg>

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 pretty soon:

We’ve got a bunch of other “Hero” domains 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 and open an issue to make a suggestion.

Want to talk about this post? Discuss this on GitHub →

(The post Introducing Heroicons.com appeared first on Tailwind CSS Blog.)

August 18, 2020  19:15:00

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:

<div class="bg-gradient-to-r from-orange-400 via-red-500 to-pink-500">
  <!-- ... -->
</div>

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:

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.

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:

ClassCSS
bg-clip-borderbackground-clip: border-box
bg-clip-paddingbackground-clip: padding-box
bg-clip-contentbackground-clip: content-box
bg-clip-textbackground-clip: text

Combined with the new gradient features, you can use this to do cool gradient text stuff like this:

<h1 class="text-6xl font-bold">
  <span class="bg-clip-text text-transparent bg-gradient-to-r from-teal-400 to-blue-500">
    Greetings from Tailwind v1.7.
  </span>
</h1>

Only responsive variants are enabled for the backgroundClip plugin by default:

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:

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.

<div class="flex">
  <div><!-- ... --></div>
  <!-- This container will act as a phantom container, and its children will be treated as part of the parent flex container -->
  <div class="contents">
    <div><!-- ... --></div>
    <div><!-- ... --></div>
  </div>
  <div><!-- ... --></div>
</div>

Learn more about it in this great article by Rachel Andrew.

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:

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:

<div class="divide-y divide-dashed">
  <div><!-- ... --></div>
  <div><!-- ... --></div>
  <div><!-- ... --></div>
  <div><!-- ... --></div>
</div>

These utilities include responsive variants by default:

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:

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:

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:

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:

- <div class="col-gap-4 row-gap-2 ...">
+ <div class="gap-x-4 gap-y-2 ...">

To opt-in to the new names now, enable the removeDeprecatedGapUtilities flag in your tailwind.config.js file:

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:

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!

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

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:

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:

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:

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:

tailwind.config.js
module.exports = {
  experimental: {
    extendedFontSizeScale: true,
  },
}

Want to talk about this post? Discuss this on GitHub →

(The post Tailwind CSS v1.7.0 appeared first on Tailwind CSS Blog.)

August 10, 2020  13:30:00

Back in May we published our first job posting 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 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 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 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 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 →

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 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 →

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 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 →

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.

Want to talk about this post? Discuss this on GitHub →

(The post From Nine Hundred to One: How We Hired Robin Malfait appeared first on Tailwind CSS Blog.)

July 28, 2020  16:58:33

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 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
<button type="button" class="bg-indigo-600 ..." disabled>
  <svg class="animate-spin h-5 w-5 mr-3 ..." viewBox="0 0 24 24">
    <!-- ... -->
  </svg>
  Processing
</button>

These are completely customizable as always, using the animation and keyframes sections of your tailwind.config.js theme:

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. For behind the scenes details about the design rationale, check out the pull request.

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.

These can be useful in conjunction with transition and animation utilities to disable problematic motion for users who are sensitive to it:

<div class="... transition duration-150 ease-in-out motion-reduce:transition-none ..."></div>

…or to explicitly opt-in to motion to make sure it’s only being shown to users who haven’t opted out:

<div class="... motion-safe:transition duration-150 ease-in-out ..."></div>

These can be combined with responsive variants and pseudo-class variants as well:

<!-- With responsive variants -->
<div class="sm:motion-reduce:translate-y-0"></div>

<!-- With pseudo-class variants -->
<div class="motion-reduce:hover:translate-y-0"></div>

<!-- With responsive and pseudo-class variants -->
<div class="sm:motion-reduce:hover:translate-y-0"></div>

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:

tailwind.config.js
module.exports = {
  // ...
  variants: {
    translate: ['responsive', 'hover', 'focus', 'motion-safe', 'motion-reduce'],
  },
}

For more details, check out the updated variants documentation.

New overscroll-behavior utilities

We’ve also added new utilities for the 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.

<div class="overscroll-y-contain ...">
  <!-- ... -->
</button>

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:

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…

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

npx tailwindcss build -o compiled.css

Your kids are going to be so grateful for the extra time you get to spend together.

Want to talk about this post? Discuss this on GitHub →

(The post Tailwind CSS v1.6.0 appeared first on Tailwind CSS Blog.)

July 19, 2020  19:00:00

Today we are super excited to share that Simon Vrachliotis 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 way back in February 2017.

He also created the first-ever Tailwind CSS video course, 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!

Want to talk about this post? Discuss this on GitHub →

(The post Simon Vrachliotis Joins Tailwind Labs appeared first on Tailwind CSS Blog.)

July 18, 2020  15:43:02

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?

You might know Brad as the creator of the amazing Tailwind CSS IntelliSense plugin 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 to keep up with what he’s working on and for sneak peaks of some exciting new projects!

Want to talk about this post? Discuss this on GitHub →

(The post Welcoming Brad Cornes to the Team appeared first on Tailwind CSS Blog.)

July 15, 2020  18:55:18

I was hoping to save v1.5.0 for something really exciting but we needed a new feature to support the new @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 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:

<article class="prose md:prose-lg">
  <!-- Content -->
</article>

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:

plugin(function ({ addComponents })) {
  addComponents({
    '.card': {
      // ...
    }
  }, {
    variants: ['responsive']
  })
})

…or using the array shorthand you might be familiar with from the addUtilities API:

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:

@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!

<!-- Only lock the width at `md` sizes and above -->
<div class="md:container">
  <!-- ... -->
</div>

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:

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

<button class="focus-visible:outline-none focus-visible:shadow-outline ...">
  Click me
</button>

It’s not enabled for anything by default, but you can enable it in the variants section of your config file:

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 and corresponding PostCSS plugin 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:

<input type="checkbox" class="bg-white checked:bg-blue-500" />

It’s not enabled for anything by default, but you can enable it in the variants section of your config file:

tailwind.config.js
module.exports = {
  // ...
  variants: {
    backgroundColor: ['responsive', 'hover', 'focus', 'checked'],
  },
}

Want to talk about this post? Discuss this on GitHub →

(The post Tailwind CSS v1.5.0 appeared first on Tailwind CSS Blog.)

July 13, 2020  16:35:02

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.

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

<article class="prose lg:prose-xl">
  <h1>Garlic bread with cheese: What the science tells us</h1>
  <p>
    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.
  </p>
  <p>
    But a recent study shows that the celebrated appetizer may be linked to a series of rabies cases
    springing up around the country.
  </p>
  <!-- ... -->
</article>

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 to learn more and try it out today.

Want to talk about this post? Discuss this on GitHub →

(The post Introducing Tailwind CSS Typography appeared first on Tailwind CSS Blog.)

June 30, 2020  18:05:31

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!

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.

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, which is the format we wanted to use to author our posts.

# My first MDX post

MDX is a really cool authoring format because it lets
you embed React components right in your markdown:

<MyComponent myProp={5} />

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

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.

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:

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

{
  resourceQuery: /preview/,
  use: [
    ...mdx,
    createLoader(function (src) {
      if (src.includes('<!--​more​-->')) {
        const [preview] = src.split('<!--​more​-->')
        return this.callback(null, preview)
      }

      const [preview] = src.split('<!--​/excerpt​-->')
      return this.callback(null, preview.replace('<!--​excerpt​-->', ''))
    }),
  ],
},

It lets us define the excerpt for each post either by sticking <!--​more--> after the intro paragraph, or by wrapping the excerpt in a pair of <!--​excerpt--> and <!--​/excerpt--> tags, allowing us to write an excerpt that’s completely independent from the post content.

export const meta = {
  // ...
}

This is the beginning of the post, and what we'd like to
show on the homepage.

<!--​more-->

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:

{
  use: [
    ...mdx,
    createLoader(function (src) {
      const content = [
        'import Post from "@/components/Post"',
        'export { getStaticProps } from "@/getStaticProps"',
        src,
        'export default (props) => <Post meta={meta} {...props} />',
      ].join('\n')

      if (content.includes('<!--​more-->')) {
        return this.callback(null, content.split('<!--​more-->').join('\n'))
      }

      return this.callback(null, content.replace(/<!--​excerpt-->.*<!--\/excerpt-->/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:

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:

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 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 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.

Want to talk about this post? Discuss this on GitHub →

(The post Building the Tailwind Blog with Next.js appeared first on Tailwind CSS Blog.)

June 23, 2020  18:52:03

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.

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:

Screen capture showing CSS linting in action

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!

Screen capture showing markup linting in action

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 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.

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!

Want to talk about this post? Discuss this on GitHub →

(The post Introducing linting for Tailwind CSS IntelliSense appeared first on Tailwind CSS Blog.)