Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add background gradient support #2176

Merged
merged 5 commits into from Aug 18, 2020
Merged

Add background gradient support #2176

merged 5 commits into from Aug 18, 2020

Conversation

adamwathan
Copy link
Member

@adamwathan adamwathan commented Aug 17, 2020

This PR introduces two new core plugins:

  • backgroundImage
  • gradientColorStops

TL;DR, together they allow you to easily compose custom gradients on the fly, directly in your HTML:

<div class="bg-gradient-to-r from-orange-400 via-red-500 to-pink-500">
  <!-- ... -->
</div>

image

They can even be combined with the new backgroundClip utilites to do cool text gradient effects:

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

image

The backgroundImage plugin

The backgroundImage plugin is a pretty vanilla core plugin that just bakes in support for the background-image property, letting you add whatever values you want:

// tailwind.config.js
module.exports = {
  theme: {
    backgroundImage: {
      dog: 'url(dog.jpg)',
    },
  },
}

By default we include 8 background image values, mapping to common linear gradient directions:

// 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))',
    },
  },
}

This generates classes like bg-gradient-to-t, bg-gradient-to-br, etc.

I considered a lot of options for these class names but ultimately decided on this convention for a few reasons:

  • Use the full word gradient because we don't shorten words anywhere else (we either use the full word or an acronym, never something like grad)
  • Include the word to because bg-gradient-t is not clear to me — my mind thinks it's a gradient starting from the top, not ending at the top
  • Use short values for t, tr, l, bl, etc. to match the border-radius classes and keep things short, since those conventions are already known by Tailwind users

Under the hood these classes are very simple, and all of the real magic lives in the gradientColorStops plugin that generates the --gradient-color-stops custom property.

By default, only responsive variants are generated:

// tailwind.config.js
module.exports = {
  variants: {
    backgroundImage: ['responsive'],
  },
}

The gradientColorStops plugin

The gradientColorStops plugin uses a clever custom-property-based implementation to support composable gradients, very similar to how we support composable transforms.

It is configured under the gradientColorStops key and by default uses your entire color palette:

// tailwind.config.js
module.exports = {
  theme: {
    gradientColorStops: (theme) => theme('colors'),
  },
}

It generates three sets of classes using the following naming convention:

  • from-{color}, for the gradient starting color
  • via-{color}, for the gradient mid color
  • to-{color}, for the gradient ending color

These classes work together to generate a final value for the --gradient-color-stops custom property that is used by the default set of background gradient utilities.

So to create a gradient, you combine one of the background-image utilities, with 1-3 of these color stop utilities to produce the gradient you want on the fly, without pregenerating a combinatoric explosion of classes in advance:

<div class="bg-gradient-to-r from-blue-500 via-teal-500 to-green-500">
  <!-- ... -->
</div>

By default, responsive, hover, and focus variants are generated:

// tailwind.config.js
module.exports = {
  variants: {
    backgroundImage: ['responsive', 'hover', 'focus'],
  },
}

These new utilities are implemented very carefully to support a handful of useful behaviors.

from on its own fades to transparent

If you use a from-{color} utility on its own, the to color defaults to transparent, so you fade to transparent without any extra classes.

<div class="bg-gradient-to-r from-red-500">
  <!-- ... -->
</div>

image

You might be familiar with a bug in Safari that makes fading to transparent very annoying: you can't simply fade to transparent, you have to fade to a fully transparent version of the from color using rgba or hsla syntax, otherwise things will look awful in Safari:

image

Our implementation cleverly dodges that bug, by dynamically generating the right to color by parsing the from color and generating the correct rgba value for the to color.

Here's the definition for from-blue-500 for example:

.from-blue-500 {
  --gradient-from-color: #4299e1;
  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
}

Here, rgba(66, 153, 225, 0) is the perfectly transparent version of #4299e1, so you can properly fade to transparent in Safari with no extra work.

We only support this on from and not to for technical reasons, if you'd like to fade from transparent to a color, simply reverse the direction of the gradient:

<div class="bg-gradient-to-l from-white">
  <!-- ... -->
</div>

image

from and to without via works with no extra CSS

The via-{color} utilities override the --gradient-color-stops custom property to include a middle color stop, but if no via-{color} utility is used, the from-{color} utilities default to a two-stop value.

So both of these just automatically work:

<div class="bg-gradient-to-r from-red-500 to-yellow-500"><!-- ... --></div>
<div class="bg-gradient-to-r from-red-500 via-yellow-400 to-green-400"><!-- ... --></div>

image

Here's the CSS for one of the via utilities:

.via-red-500 {
  --gradient-via-color: #f56565;
  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
}

You can also fade from a color, via a color, to transparent by leaving off the to:

<div class="bg-gradient-to-r from-red-500 via-orange-400"><!-- ... --></div>

image

Release strategy

These features introduce no breaking changes and will be part of the next minor release (v1.7).

@dillingham
Copy link

dillingham commented Aug 17, 2020

Looks great, awesome work Adam!

‪Might be cool to have a shorthand:‬

‪text-gradient-r in place of‬
bg-clip-text text-transparent bg-gradient-to-r‬

Also seems like to-r or to-l could be implied when you say -r
it’s going to the right etc if you wanted from the right.. you put -l 🤔

@adamwathan adamwathan merged commit 7945f0f into master Aug 18, 2020
@adamwathan adamwathan deleted the gradients branch August 18, 2020 12:07
@apt-exploit
Copy link

Tailwind team, I have some problems integrating to my webpack. In its documentation, when using only CLI the entry.css file, it is processed in an output.css file and when integrating in my webpack it converts my entry.css into an AST "Javascript" file, this is affecting the size of the document. . My question in general, how is it correct to work with Webpack? Is it necessary to use style-loader and css-loader or only postcss?

@adamwathan
Copy link
Member Author

@apt-exploit Best to start a new discussion thread if you want help with that issue, it'll get lost in the comments here and I'm afraid you won't get an answer 😞

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants