Create a Tailwind CSS theme

In this guide, we’ll walk through the process of creating a custom Tailwind theme for your forms and inputs. Tailwind has risen to the forefront of CSS utility class libraries, and FormKit was authored with its capabilities in mind. Let’s get started!

SFC Build tool

This guide assumes you are using a standard Vue 3 build tool like Vite, Nuxt 3, or Vue CLI that will allow you to import .vue single file components.

Don't include the default theme

If you plan to use Tailwind CSS for your form styles then please ensure that your project is not importing the base genesis theme that ships with FormKit — otherwise you will get weird styling results.

Inline usage

In the context of a .vue file that represents a component, it's possible to create a Tailwind theme using the section-key class props or the classes prop provided by FormKit.

If your component represents your entire form and your project only requires a single form, this may be all that you need. Here is an example of applying the same Tailwind classes to a FormKit text input using both the section-key props and the classes prop:

Load live example

This is a low-barrier way to apply Tailwind styles to your FormKit forms, but what if you have multiple forms? Copy-pasting class lists between components is not ideal and will lead to inadvertent variations in styling across your project over time.

Let's explore how we can apply Tailwind classes globally to all FormKit inputs within our project.

Using @formkit/themes

FormKit ships a first-party package @formkit/themes that includes Tailwind CSS support — making it easy to create a Tailwind CSS theme for FormKit in your project.

The package enables you to author your theme as a JavaScript object grouped by input type and sectionKey. Additionally, you can access a number of Tailwind variants based on input and form state such as formkit-invalid: and formkit-disabled: which allow you to dynamically change your input styling.

To get started we need to add the package to our project.

npm install @formkit/themes

From there we need to do two things:

  • Add the formKitTailwind plugin from @formkit/themes to our project's tailwind.config.js file.
  • Create a theme file (something like tailwind-theme.js) in our project.
  • Import the generateClasses helper function from @formkit/themes and use it with our theme in our FormKit config options.
// tailwind.config.js
const formKitTailwind = require('@formkit/themes/tailwindcss');

module.exports {
  ...
  content: [
    ...
    './tailwind-theme.js',
  ],
  plugins: [
    formKitTailwind
  ]
  ...
}
// tailwind-theme.js
export default {
  // our theme will go here.
  // ...
  // text: {
  //   label: 'font-bold text-gray-300',
  //   ...
  // }
  // ...
}
// app.js
import { createApp } from 'vue'
import App from './App.vue'
import { plugin, defaultConfig } from '@formkit/vue'
import { generateClasses } from '@formkit/themes'
import myTailwindTheme from './tailwind-theme.js'
import '../dist/index.css' // wherever your Tailwind styles exist

createApp(App)
  .use(
    plugin,
    defaultConfig({
      config: {
        classes: generateClasses(myTailwindTheme),
      },
    })
  )
  .mount('#app')

Once this setup is complete we are ready to begin writing our Tailwind theme!

Our first Tailwind input

To start, let's apply some sensible classes to a text style input. This will cover a large surface area because we'll easily re-use these styles to other text-like inputs such as email, password, date, etc.

To specifically target text inputs we'll create a text key in our theme object and then apply classes to each sectionKey as needed.

Here is a text input with Tailwind classes applied:

Load live example

Using variants

The formKitTailwind plugin from @formkit/themes provides a number of variants you can use in your class lists to dynamically respond to input and form state.

Group variants

If you're using variants in a nested case, the variants may be linked to its parent instead of itself. To fix that, add to the outer section group/{modifier}, and use the variant with the same modifier formkit-invalid/{modifier}:

The currently provided variants are:

  • formkit-disabled:
  • formkit-invalid:
  • formkit-checked:
  • formkit-errors:
  • formkit-complete:
  • formkit-loading:
  • formkit-submitted:
  • formkit-multiple:
  • formkit-prefix-icon
  • formkit-suffix-icon

You use these variants in the same way you use the built-in Tailwind variants such as dark: and hover:.

Let's add some variants for formkit-invalid and formkit-disabled to our text input:

Load live example

A complete Tailwind theme — recreating Genesis CSS

Create a Tailwind CSS Theme - Vue School Course

11 mins

Now we're cooking! To create a comprehensive theme all we need to do is define class lists for the sectionKeys of all the other input types we'll use in our project.

There are some improvements we can make though. The generateClasses helper function from @formkit/themes allows for a special global key that will apply to all inputs. This is helpful for sectionKeys such as help and messages that are usually styled the same across all input types in a project.

Global and Family Class Lists

By using the global and family: keys in your theme object you can apply a class lists to all inputs that have a given sectionKey either globally or within a family of inputs. This is useful for things like labels or help text when you want to share styling across a wide variety of inputs.

Let's create a "Kitchen Sink" of input types, each having their defined class lists applied. Here is the theme in isolation for better readability:

And here is our Tailwind theme when it is applied to all available FormKit inputs:

FormKit icons

FormKit inputs ship with their own decorator icons that can be used in place of browser-default styles that typcially ship with checkboxes, radios, select inputs, and more.

If you want to use these types of icons in your Tailwind theme be sure to import them from @formkit/icons and include them in your FormKit config.

Load live example

Selective overrides

And there we have it! All FormKit inputs styled with Tailwind utility classes across our entire project.

If we need to override any specific one-offs within our project, we can do so using the section-key class props or the classes prop on a given FormKit input within our project which was covered in the opening section of this guide.

Of particular importance when doing an override is the special $reset modifier for class lists. When the FormKit class system encounters a $reset class it will erase the current class list for the given section and only collect class names that occur after the $reset token was encountered. This is valuable in a system like Tailwind where it would be painful to have to write override classes or individually disable classes for every globally configured class when deviating from our theme:

Load live example

Next steps

This guide has walked through creating a Tailwind theme that covers all input types included in FormKit, but there's still more that could be done in your own project.

Here are some ways to take the above guide even further:

  • Add dark-mode support using Tailwind's built-in dark: modifier.
  • Combine multiple variants such as formkit-invalid:formkit-submitted: to add extra emphasis to invalid fields when a user tries to submit an incomplete form.
  • Publish your theme as an npm package for easy importing and sharing between projects.

Hopefully, this guide helped you understand how classes are applied to FormKit inputs and how you can leverage the formKitTailwind plugin from the @formkit/themes package to make use of Tailwind in your FormKit projects. If you want to dive in deeper, try reading about the core internals of FormKit and the FormKit schema!

Want more? Start by reading about FormKit core.Dig deeper