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 support for colored box shadows #5979

Merged
merged 6 commits into from
Nov 4, 2021
Merged

Add support for colored box shadows #5979

merged 6 commits into from
Nov 4, 2021

Conversation

adamwathan
Copy link
Member

@adamwathan adamwathan commented Nov 4, 2021

This PR adds support for specifying a box shadow color using a composable API:

<div class="shadow-xl shadow-blue-500/20">

The motivation for this is to make it possible to tint your shadows to look better on colored backgrounds, which is a design principle that @joshwcomeau outlines beautifully in this blog post:

https://www.joshwcomeau.com/css/designing-shadows/

Here you can see the default black-based shadow on the left, and a tinted shadow on the right:

image

The difference is kind of subtle but the tinted ones look more natural when you get the values right.

The composable API is very flexible (almost too flexible really), so this is sort of an advanced feature that non-designers might not have an easy time getting great results with. I hope to provide some guidance around how to approach using these APIs in the documentation for v3 when it's released.

Under the hood, all colors in a shadow are replaced by the color you specify. For example, given this shadow in your config:

boxShadow: {
  // ...
  fancy: '0 1px 3px rgba(24, 53, 76, 0.11), 0 2px 9px rgba(11, 22, 99, 0.42)'
}

...if you write the following HTML:

<div class="shadow-fancy shadow-blue-500">

...the final rendered box shadow will be this:

box-shadow: 0 1px 3px #3B82F6, 0 2px 9px #3B82F6

Notice that even though in the original shadow the two separate shadow layers had separate colors, in the final shadow both layers are the exact same color. This is the only practical approach I felt was reasonable after attempting to approach this feature from scratch like 12 times.

This is a little weird because the default Tailwind shadow use different alpha values for the shadow layers. For this reason, this PR also tweaks the default shadows to use the same alpha value for each layer. By doing this, when you specify a shadow color the colored shadow will have the same transparency and blending characteristics as the default shadow.

We've tried to make the new shadows as visually identical to the old ones as possible, which you can preview in this Play:

https://play.tailwindcss.com/S8d4uoZ2sz?layout=preview

It's also worth noting that if you change a shadow at a different breakpoint, it will override any custom color.

<!-- This shadow will NOT be red at md screens and above -->
<div class="shadow-lg shadow-red-500 md:shadow-xl">

To preserve the color, it needs to be respecified:

<!-- This shadow WILL be red at md screens and above -->
<div class="shadow-lg shadow-red-500 md:shadow-xl md:shadow-red-500">

This is the only way we could make it possible to switch from a color back to the default shadow color at a different breakpoint. In the future we may come up with a terser syntax (like shadow-xl/red-500) to make this feel less annoying, but in practice I don't expect this to be a huge pain point anyways.

All of the colors are available as shadow colors by default, and if you need to customize them separately you can do so under the boxShadowColor key in your config.

@Soviut
Copy link
Contributor

Soviut commented Nov 4, 2021

I would love if it were possible to control the shadow alpha, with either shadow-alpha-50 or shadow-red-500/50.

@adamwathan
Copy link
Member Author

I would love if it were possible to control the shadow alpha, with either shadow-alpha-50 or shadow-red-500/50.

Yeah this works as part of this PR too 👍🏻

shadow-red-500/20

Copy link
Member

@RobinMalfait RobinMalfait left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Beautiful!

Comment on lines 1789 to 1792
// Don't override color if the whole shadow is a variable
if (shadow.x === undefined || shadow.y === undefined) {
continue
}
Copy link
Member

@RobinMalfait RobinMalfait Nov 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we encode this information in each shadow itself? Like an isValid flag or something?
Not a requirement, but I noticed the same logic in multiple places.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I agree or even have the parse function return an error or something if it doesn’t parse a proper value. Moar tuples? Is this Elm?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hah, I think we can just encode it as a valid flag. When we re-print, then we can use the raw value in case the value is not valid.

E.g.:

// Input: 0 1px 2px -1px rgb(0 0 0 / 0.1)
// Parsed:
{
  raw: '0 1px 2px -1px rgb(0 0 0 / 0.1)',
  x: '0',
  y: '1px',
  blur: '2px',
  spread: '-1px',
  color: 'rgb(0 0 0 / 0.1)',
  valid: true
}
// Printed: 0 1px 2px -1px rgb(0 0 0 / 0.1)
// Input: var(--abc)
// Parsed:
{
  raw: 'var(--abc)',
  color: 'var(--abc)',
  valid: false
}
// Printed: var(--abc)

@adamwathan adamwathan merged commit d1f066d into master Nov 4, 2021
@adamwathan adamwathan deleted the colored-shadows branch November 4, 2021 14:14
@robdekort
Copy link

This is beautiful and still so flexible. Lovely!

@snaptopixel
Copy link

Seems like a nice hands-off approach could be to calculate and include a shadow-color variable as part of the bg- classes and have the shadow- utils use it by default? Something like https://codepen.io/fathom-casey/pen/VwzQydd

@Soviut
Copy link
Contributor

Soviut commented Nov 5, 2021

@snaptopixel I feel like that makes a lot of assumptions about elements being nested and would result in a lot of confusion as to why shadows are behaving differently when the user hasn't explicitly applied classes to them.

An example where I use shadows without nesting are with modals that have an overlay that darkens the background. The overlay is a separate element while the floating modal dialog is a sibling, rather than a descendant. The background colour of the overlay wouldn't transmit to the dialog so I'd have to be manually setting shadow colour in that case.

@benjamincrozat
Copy link

Will this be available in version 2 since it's not a breaking change?

@noam-elbaz
Copy link

Can't contribute anything intelligent to the development of this feature other than - awesome! looking forward. this is useful.

@captlevi01
Copy link

captlevi01 commented Nov 13, 2021

on which version can we try this, 3.0.0-alpha.2 or 2.2.19?

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.

8 participants