New Features In v3.1

This is a summary version that summarizes the improvements and added features from v3.0.6 to v3.1.0.


New opacity handler syntax

Color and transparency are usually a common combination. We used to have text-green-500 text-opacity-50. Now you can use the following abbreviations.

  • text-green-500/50
  • underline-green-500/50
  • placeholder-green-500/50
  • bg-indigo-500/$primary-opacity
  • bg-green-500/[0.17]
  • ...

New border side color and border side opacity

The new border side color utilities allow you to set different border-color for different sides

  • border-t-green-200
  • border-t-green-200/50
  • border-b-green-200
  • border-l-yellow-300
  • border-r-blue-400

New content utility

The content utilities generate the corresponding content css, such as content: '', which can be very helpful in many cases.

  • content-👍
  • before:content-["👍"]
  • content-open-quote
  • after:content-[attr(value)]
  • content

Add more gradient color defaults

Creating gradient colors has always been a hassle, and you can't be sure that the resulting gradient color will look good. So we've added more gradient color defaults to make it easier for users to use. The gradient colors from coolhue.

  • bg-gradient-1
  • bg-gradient-2
  • ...
  • bg-gradient-60

New animation utilities

The previous animation utilities had no interactivity at all, so we added a new set of animations (thanks to the hard work of animate.css) and added the corresponding operation utilities.


Animation Trigger

  • animated

You need to use animated to trigger the animation(animated animate-flash), just like you do with filter blur-sm. If you need the animation to play infinite, you also need to use an animate-loop. This for getting better customizability. Most of the time we probably need just trigger it once, not a loop.

animate-spin animate-ping animate-pulse animate-bounce are special cases, you can trigger them without using animated and they will loop at the same time. Since these are previously existing utilities, suddenly modifying them may cause a lot of trouble for users. You can understand how they work through the configuration file.

export default {
  animation: {
    'spin': 'spin 1s linear infinite',
    'ping': 'ping 1s cubic-bezier(0, 0, 0.2, 1) infinite',
    'pulse': 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite',
    'bounce': 'bounce 1s infinite',
    'shock': {
      animation: 'shock',
      transformOrigin: 'center bottom',
    'flash': 'flash',
    'bubble': 'bubble',
    'rubber-band': 'rubberBand',
    'shake-x': 'shakeX',
    // ...

Animation Delay

The animation-delay utility specifies a delay for the start of an animation.

  • animate-delay-100
  • animate-delay-1500
  • animate-delay-[1s]
  • animate-delay-$time-delay
  • animate-delay-1.5s
  • animate-delay-200ms

Animation Direction

The animation-direction utility defines whether an animation should be played forwards, backward or in alternate cycles.

  • animate-reverse
  • animate-alternate
  • animate-alternate-reverse

Animation Duration

The animation-duration utility defines how long an animation should take to complete one cycle.

  • animate-duration-100
  • animate-duration-1500
  • animate-duration-1.5s
  • animate-duration-[1.3s]
  • animate-duration-$time-duration

Animation Fill Mode

The animation-fill-mode utility specifies a style for the element when the animation is not playing (before it starts, after it ends, or both).

  • animate-fill-none
  • animate-fill-forwards
  • animate-fill-backwards
  • animate-fill-both

Animation Iteration Count

The animation-iteration-count utility specifies the number of times an animation should be played.

  • animate-loop
  • animate-repeat-1
  • animate-repeat-12
  • animate-repeat-[23]

Animation Play State

The animation-play-state utility specifies whether the animation is running or paused.

  • animate-paused
  • animate-running

Animate Timing Function

The animation-timing-function specifies the speed curve of an animation.

  • animate-ease
  • animate-ease-in
  • animate-ease-linear
  • animate-ease-out
  • animate-ease-in-out
  • animate-ease-[cubic-bezier(0.25,0.1,0.25,1)]

New time handler

We also added a new time handler, which allows you to use the corresponding time, like s and ms, directly in the className. duration-{time} delay-{time} animate-duration-{time} animate-delay-{time}.

  <div class="duration-1.5s delay-200ms animate-duration-1.3s">...</div>

You can control its switch by configuring handlers.time

export default {
  theme: {
    // ...
  handlers: {
    static: true,
    time: false,

Dark mode support for Typography

We added a nice dark mode for the typography plugin, you can enable the feature via the dark configuration, which will add the corresponding dark mode css based on your darkMode configuration.

import typography from 'windicss/plugin/typography'
import { defineConfig } from 'windicss/helpers'

export default defineConfig({
  darkMode: 'class',
  plugins: [
      dark: true,

Rtl support for Typography

Previously, typography did not support rtl very well, we solved this problem.

import typography from 'windicss/plugin/typography'
import { defineConfig } from 'windicss/helpers'

export default defineConfig({
  plugins: [
      rtl: true,

Icon Library for Windi

Added an icon library for windicss, powered by

npm install --save-dev @windicss/plugin-icons

Let's simply create a close button or menu. It is completely pure css and is generated on demand.

<div class="bg-gray-200 text-gray-800 hover:bg-gray-300 rounded-full w-8 h-8 flex items-center justify-center">
  <i class="icon-close"></i>

<div class="bg-gray-200 text-gray-800 hover:bg-gray-300 rounded w-12 h-12 flex items-center justify-center">
  <i class="icon-menu"></i>

More Improvements

  • better square brackets support
  • support include and exclude option in config file
  • support attributify prefix
  • support safelist in cli
  • support watch config file in cli


New API for checking if a utility is a windi utility

Processor.test('bg-red-500') // -> boolean
Processor.validate('font-bold text-green-300 sm:dark:hover:text-lg') // -> boolean

Support add completion in addDynamic API

addDynamic('line-clamp', ({ Utility, Property, Style }) => {
  // ...
}, {
  group: 'lineClamp',
  completions: [