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

[Feature Proposal] Coloured Box Shadow #654

Closed
adrianb93 opened this issue Feb 13, 2019 · 27 comments
Closed

[Feature Proposal] Coloured Box Shadow #654

adrianb93 opened this issue Feb 13, 2019 · 27 comments

Comments

@adrianb93
Copy link

Heya,

I think it'd be neat if box shadows worked with colour variations. I tried to reach for it for a hover state and really wished it was there.

@benface
Copy link
Contributor

benface commented Feb 15, 2019

You can already generate all the box shadow utilities you need with the shadows key in the config:

  shadows: {
    'red': '0 2px 4px 0 rgba(255, 0, 0, 0.10)',
    'green': '0 2px 4px 0 rgba(0, 255, 0, 0.10)',
    'blue': '0 2px 4px 0 rgba(0, 0, 255, 0.10)',
    'custom-hex-color': '0 2px 4px 0 #af9cdd',
  },

Do you mean that you would like Tailwind to automatically generate shadow utilities based on the colors key? If so, I don't think that's a good idea for three reasons:

  1. There is no precedent for it; even the textColors, backgroundColors, and borderColors keys only generate utilities for the colors they contain (even though the default config file uses a single colors variable that it then assigns to textColors, backgroundColors, and borderColors).

  2. A box-shadow declaration has more than just a color; it also has offsets (x and y), optionally a blur radius, and optionally a spread radius as well. Unfortunately, there is no box-shadow-color property in CSS so you have to set all of these values even if you just want to change the color. Tailwind would have no way of knowing what to set them to for the colors defined in colors.

  3. It would generate a lot of classes that most people don't need by default because Tailwind comes with a pretty large color palette, increasing the file size of the CDN version considerably.

@adrianb93
Copy link
Author

Yeah, I guess shadow-md-blue would look out of place compared to border border-blue, given shadow-md shadow-blue is not possible. You've got a point there.

@adamwathan
Copy link
Member

Going to close this as not something I plan to add to the default config out of the box right now, but of course can add in your own config 👍 Can definitely see a use case for things like focus styles on different colored buttons for example.

@andrew-ireland
Copy link

As another person who reached for/wished for this functionality today, is it possible to generate color shadows based on the base box-shadow sizes determined by choice (rather than automatically)?

Could boxShadow be extended with a colors section where users could set a range box-shadow colors in reference to the main theme colors (or something similar), with boxShadow sizing being handled separately? I.e shadow-xl-blue would be generated if the boxShadow config was extended with the color blue.

For me personally, this would be more maintainable than hardcoding the rgba values into boxShadows as is currently done in the config. Currently assuming that users will only use black shadows seems limiting to me

@justinekizhak
Copy link

What should I add in my config to get this?

I am looking for a coloured version of my own shadow shadow-blur, something like
shadow-blur-blue-500 which will equal to box-shadow: 0 0 5px 1px #4299E1;

@janhesters
Copy link

Update:

If you find this thread, here is how the config should look:

module.exports = {
  theme: {
    extend: {
      boxShadow: {
        blue: '0 4px 14px 0 rgba(19, 51, 81, 0.39)',
      },
    },
  },
};

And then you can use it with shadow-blue.

@mrmurphy
Copy link

Here's an example of adding all shadow variants for all colors in the new Tailwind UI:

let makeShadow = (name, rgb) => {
  let obj = {};

  obj[name + "-xs"] = `0 0 0 1px rgba(${rgb}, 0.05)`;
  obj[name + "-xs"] = `0 0 0 1px rgba(${rgb}, 0.05)`;
  obj[name + "-sm"] = `0 1px 2px 0 rgba(${rgb}, 0.05)`;
  obj[name] = `0 1px 3px 0 rgba(${rgb}, 0.1), 0 1px 2px 0 rgba(${rgb}, 0.06)`;
  obj[
    name + "-md"
  ] = `0 4px 6px -1px rgba(${rgb}, 0.1), 0 2px 4px -1px rgba(${rgb}, 0.06)`;
  obj[
    name + "-lg"
  ] = `0 10px 15px -3px rgba(${rgb}, 0.1), 0 4px 6px -2px rgba(${rgb}, 0.05)`;
  obj[
    name + "-xl"
  ] = `0 20px 25px -5px rgba(${rgb}, 0.1), 0 10px 10px -5px rgba(${rgb}, 0.04)`;
  obj[name + "-2xl"] = `0 25px 50px -12px rgba(${rgb}, 0.25)`;
  obj[name + "-inner"] = `inset 0 2px 4px 0 rgba(${rgb}, 0.06)`;
  return obj;
};

module.exports = {
  theme: {
    extend: {
      boxShadow: {
        ...makeShadow("cool-gray", "71, 85, 104"),
        ...makeShadow("gray", "75, 85, 98"),
        ...makeShadow("red", "223, 39, 44"),
        ...makeShadow("orange", "207, 57, 24"),
        ...makeShadow("yellow", "158, 88, 28"),
        ...makeShadow("green", "16, 122, 87"),
        ...makeShadow("teal", "13, 116, 128"),
        ...makeShadow("blue", "29, 100, 236"),
        ...makeShadow("indigo", "87, 81, 230"),
        ...makeShadow("purple", "125, 59, 236"),
        ...makeShadow("pink", "213, 34, 105")
      }
    }
  },
  variants: {},
  plugins: [require("@tailwindcss/ui")]
};

@mrmurphy
Copy link

that will allow you to use shadow-blue-lg, for example

@corysimmons
Copy link

Thanks for the workarounds. I agree this should be in core.

@cerinoligutom
Copy link

This should be in core.

@cerinoligutom
Copy link

Here's an example of adding all shadow variants for all colors in the new Tailwind UI:

let makeShadow = (name, rgb) => {
  let obj = {};

  obj[name + "-xs"] = `0 0 0 1px rgba(${rgb}, 0.05)`;
  obj[name + "-xs"] = `0 0 0 1px rgba(${rgb}, 0.05)`;
  obj[name + "-sm"] = `0 1px 2px 0 rgba(${rgb}, 0.05)`;
  obj[name] = `0 1px 3px 0 rgba(${rgb}, 0.1), 0 1px 2px 0 rgba(${rgb}, 0.06)`;
  obj[
    name + "-md"
  ] = `0 4px 6px -1px rgba(${rgb}, 0.1), 0 2px 4px -1px rgba(${rgb}, 0.06)`;
  obj[
    name + "-lg"
  ] = `0 10px 15px -3px rgba(${rgb}, 0.1), 0 4px 6px -2px rgba(${rgb}, 0.05)`;
  obj[
    name + "-xl"
  ] = `0 20px 25px -5px rgba(${rgb}, 0.1), 0 10px 10px -5px rgba(${rgb}, 0.04)`;
  obj[name + "-2xl"] = `0 25px 50px -12px rgba(${rgb}, 0.25)`;
  obj[name + "-inner"] = `inset 0 2px 4px 0 rgba(${rgb}, 0.06)`;
  return obj;
};

module.exports = {
  theme: {
    extend: {
      boxShadow: {
        ...makeShadow("cool-gray", "71, 85, 104"),
        ...makeShadow("gray", "75, 85, 98"),
        ...makeShadow("red", "223, 39, 44"),
        ...makeShadow("orange", "207, 57, 24"),
        ...makeShadow("yellow", "158, 88, 28"),
        ...makeShadow("green", "16, 122, 87"),
        ...makeShadow("teal", "13, 116, 128"),
        ...makeShadow("blue", "29, 100, 236"),
        ...makeShadow("indigo", "87, 81, 230"),
        ...makeShadow("purple", "125, 59, 236"),
        ...makeShadow("pink", "213, 34, 105")
      }
    }
  },
  variants: {},
  plugins: [require("@tailwindcss/ui")]
};

Building on top of this, this is what I did.

{
  boxShadow: theme => ({
    xs: '0 0 0 1px rgba(0, 0, 0, 0.05)',
    sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
    default: '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)',
    md: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)',
    lg: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',
    xl: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)',
    '2xl': '0 25px 50px -12px rgba(0, 0, 0, 0.25)',
    inner: 'inset 0 2px 4px 0 rgba(0, 0, 0, 0.06)',
    outline: '0 0 0 3px rgba(66, 153, 225, 0.5)',
    none: 'none',
    // Build on top of our existing colors
    ...Object.entries(theme('colors'))
      .map(([key, value]) => makeShadow(key, value))
      .reduce((acc, cur) => ({ ...acc, ...cur }), {}),
  }),
}

@corysimmons
Copy link

corysimmons commented Jun 25, 2020

@cerino-ligutom Only works with strings. ie: black: '#000' Doesn't work with objects like primary: { 100: '#ff0', 200: '#f00' }

I see what you're doing though and might be able to get it working with objs as well.


My solution with objs as well:

const hexRgb = require('hex-rgb') // yarn add hex-rgb
const defaultTheme = require('tailwindcss/defaultTheme')

// https://github.com/tailwindcss/tailwindcss/issues/654#issuecomment-606746700
const makeShadow = (name, rgb) => {
    const obj = {}
    obj[`${name}-xs`] = `0 0 0 1px rgba(${rgb}, 0.05)`
    obj[`${name}-xs`] = `0 0 0 1px rgba(${rgb}, 0.05)`
    obj[`${name}-sm`] = `0 1px 2px 0 rgba(${rgb}, 0.05)`
    obj[name] = `0 1px 3px 0 rgba(${rgb}, 0.1), 0 1px 2px 0 rgba(${rgb}, 0.06)`
    obj[`${name}-md`] = `0 4px 6px -1px rgba(${rgb}, 0.1), 0 2px 4px -1px rgba(${rgb}, 0.06)`
    obj[`${name}-lg`] = `0 10px 15px -3px rgba(${rgb}, 0.1), 0 4px 6px -2px rgba(${rgb}, 0.05)`
    obj[`${name}-xl`] = `0 20px 25px -5px rgba(${rgb}, 0.1), 0 10px 10px -5px rgba(${rgb}, 0.04)`
    obj[`${name}-2xl`] = `0 25px 50px -12px rgba(${rgb}, 0.25)`
    obj[`${name}-inner`] = `inset 0 2px 4px 0 rgba(${rgb}, 0.06)`
    return obj
}

module.exports = {
    theme: {
        colors: {
            ...defaultTheme.colors,
            // Custom
            primary: {
                100: '#d2e1ed',
                200: '#a5c4dc',
                300: '#78a6ca',
                400: '#4b89b9',
                500: '#1e6ba7',
                600: '#185686',
                700: '#124064',
                800: '#0c2b43',
                900: '#061521',
            },
            font: {
                100: '#d6d7d7',
                200: '#adb0b0',
                300: '#848888',
                400: '#5b6161',
                500: '#323939',
                600: '#282e2e',
                700: '#1e2222',
                800: '#141717',
                900: '#0a0b0b',
            },
            danger: {
                100: '#ffd8d6',
                200: '#ffb1ac',
                300: '#fe8983',
                400: '#fe6259',
                500: '#fe3b30',
                600: '#cb2f26',
                700: '#98231d',
                800: '#661813',
                900: '#330c0a',
            },
        },

        // https://github.com/tailwindcss/tailwindcss/issues/654#issuecomment-619279397
        boxShadow: theme => {
            // Handle color objects as well
            const fresh = Object.values(
                Object.entries(theme('colors')).reduce((acc, curr) => {
                    const [k, v] = curr
                    if (typeof v === 'string' && v !== 'transparent' && v !== 'currentColor') {
                        const { red, green, blue } = hexRgb(v)
                        acc[k] = makeShadow(k, `${red}, ${green}, ${blue}`)
                    }
                    if (typeof v === 'object') {
                        Object.entries(v).forEach(([_k, _v]) => {
                            const { red, green, blue } = hexRgb(_v)
                            acc[`${k}-${_k}`] = makeShadow(
                                `${k}-${_k}`,
                                `${red}, ${green}, ${blue}`,
                            )
                        })
                    }
                    return acc
                }, {}),
            ).reduce((acc, cur) => ({ ...acc, ...cur }), {})

            return {
                xs: '0 0 0 1px rgba(0, 0, 0, 0.05)',
                sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
                default: '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)',
                md: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)',
                lg: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',
                xl: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)',
                '2xl': '0 25px 50px -12px rgba(0, 0, 0, 0.25)',
                inner: 'inset 0 2px 4px 0 rgba(0, 0, 0, 0.06)',
                outline: '0 0 0 3px rgba(66, 153, 225, 0.5)',
                none: 'none',
                ...fresh,
            }
        },
    },
}

Produces shadows like:

.md\:shadow-shadow-primary-100-sm {
  box-shadow: 0 1px 2px 0 rgba(210, 225, 237, 0.05);
}

@fabien-ml
Copy link

Based on @corysimmons solution but removing the black shadow boilerplate code and dependency to and external hexToRgb package :

const colors = require('tailwindcss/colors')

function hexToRgb(hex) {
  var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
  return result
    ? {
        red: parseInt(result[1], 16),
        green: parseInt(result[2], 16),
        blue: parseInt(result[3], 16),
      }
    : null
}

function makeShadow(name, rgb) {
  const obj = {}

  const nameWithDash = name ? `${name}-`: ''
  const defaultName = name ? name : 'DEFAULT'

  obj[`${nameWithDash}xs`] = `0 0 0 1px rgba(${rgb}, 0.05)`
  obj[`${nameWithDash}sm`] = `0 1px 2px 0 rgba(${rgb}, 0.05)`
  obj[defaultName] = `0 1px 3px 0 rgba(${rgb}, 0.1), 0 1px 2px 0 rgba(${rgb}, 0.06)`
  obj[`${nameWithDash}md`] = `0 4px 6px -1px rgba(${rgb}, 0.1), 0 2px 4px -1px rgba(${rgb}, 0.06)`
  obj[`${nameWithDash}lg`] = `0 10px 15px -3px rgba(${rgb}, 0.1), 0 4px 6px -2px rgba(${rgb}, 0.05)`
  obj[`${nameWithDash}xl`] = `0 20px 25px -5px rgba(${rgb}, 0.1), 0 10px 10px -5px rgba(${rgb}, 0.04)`
  obj[`${nameWithDash}2xl`] = `0 25px 50px -12px rgba(${rgb}, 0.25)`
  obj[`${nameWithDash}inner`] = `inset 0 2px 4px 0 rgba(${rgb}, 0.06)`
  return obj
}

function buildShadowPalette(theme) {

  // default tailwindcss black shadows 
  const defaultPalette = { 
    ...makeShadow('', '0, 0, 0'),
    outline: '0 0 0 3px rgba(66, 153, 225, 0.5)',
    none: 'none'
  }

  const coloredShadowPalette = Object.values(
    Object.entries(theme('colors')).reduce((acc, curr) => {
      const [k, v] = curr
      if (typeof v === 'string' && v !== 'transparent' && v !== 'currentColor') {
          const { red, green, blue } = hexToRgb(v)
          acc[k] = makeShadow(k, `${red}, ${green}, ${blue}`)
      }
      if (typeof v === 'object') {
          Object.entries(v).forEach(([_k, _v]) => {
              const { red, green, blue } = hexToRgb(_v)
              acc[`${k}-${_k}`] = makeShadow(
                  `${k}-${_k}`,
                  `${red}, ${green}, ${blue}`,
              )
          })
      }
      return acc
    }, {})
  )
  
  return coloredShadowPalette.reduce((acc, cur) => ({ ...acc, ...cur }), defaultPalette)
}

module.exports = {
  theme: {
    colors: {
      primary: colors.teal,
      neutral: colors.coolGray,
      info: colors.blue,
      success: colors.emerald,
      warning: colors.amber,
      danger: colors.rose
    },
    boxShadow: (theme) => {
      return {
        ...buildShadowPalette(theme),
      }
    },
  },
  variants: {},
  plugins: [],
}

Example here

@NoahBres
Copy link

@fabien-ml Your snippet is awesome! Just a little improvement because it threw an exception for me:

const colors = require('tailwindcss/colors')

function hexToRgb(hex) {
  const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
  hex = hex.replace(shorthandRegex, function (m, r, g, b) {
    return r + r + g + g + b + b;
  });

  var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
  return result
    ? {
        red: parseInt(result[1], 16),
        green: parseInt(result[2], 16),
        blue: parseInt(result[3], 16),
      }
    : null
}

function makeShadow(name, rgb) {
  const obj = {}

  const nameWithDash = name ? `${name}-`: ''
  const defaultName = name ? name : 'DEFAULT'

  obj[`${nameWithDash}xs`] = `0 0 0 1px rgba(${rgb}, 0.05)`
  obj[`${nameWithDash}sm`] = `0 1px 2px 0 rgba(${rgb}, 0.05)`
  obj[defaultName] = `0 1px 3px 0 rgba(${rgb}, 0.1), 0 1px 2px 0 rgba(${rgb}, 0.06)`
  obj[`${nameWithDash}md`] = `0 4px 6px -1px rgba(${rgb}, 0.1), 0 2px 4px -1px rgba(${rgb}, 0.06)`
  obj[`${nameWithDash}lg`] = `0 10px 15px -3px rgba(${rgb}, 0.1), 0 4px 6px -2px rgba(${rgb}, 0.05)`
  obj[`${nameWithDash}xl`] = `0 20px 25px -5px rgba(${rgb}, 0.1), 0 10px 10px -5px rgba(${rgb}, 0.04)`
  obj[`${nameWithDash}2xl`] = `0 25px 50px -12px rgba(${rgb}, 0.25)`
  obj[`${nameWithDash}inner`] = `inset 0 2px 4px 0 rgba(${rgb}, 0.06)`
  return obj
}

function buildShadowPalette(theme) {

  // default tailwindcss black shadows 
  const defaultPalette = { 
    ...makeShadow('', '0, 0, 0'),
    outline: '0 0 0 3px rgba(66, 153, 225, 0.5)',
    none: 'none'
  }

  const coloredShadowPalette = Object.values(
    Object.entries(theme('colors')).reduce((acc, curr) => {
      const [k, v] = curr
      if (typeof v === 'string' && v !== 'transparent' && v !== 'currentColor') {
          const { red, green, blue } = hexToRgb(v)
          acc[k] = makeShadow(k, `${red}, ${green}, ${blue}`)
      }
      if (typeof v === 'object') {
          Object.entries(v).forEach(([_k, _v]) => {
              const { red, green, blue } = hexToRgb(_v)
              acc[`${k}-${_k}`] = makeShadow(
                  `${k}-${_k}`,
                  `${red}, ${green}, ${blue}`,
              )
          })
      }
      return acc
    }, {})
  )
  
  return coloredShadowPalette.reduce((acc, cur) => ({ ...acc, ...cur }), defaultPalette)
}

module.exports = {
  theme: {
    colors: {
      primary: colors.teal,
      neutral: colors.coolGray,
      info: colors.blue,
      success: colors.emerald,
      warning: colors.amber,
      danger: colors.rose
    },
    boxShadow: (theme) => {
      return {
        ...buildShadowPalette(theme),
      }
    },
  },
  variants: {},
  plugins: [],
}

To ensure that all the 3 letter hex codes are converted properly.
Worked perfectly after that addition!

@edgarasben
Copy link

edgarasben commented Jan 22, 2021

This should be definitely included in the core. I often finding black shadows looking very bad on different backgrounds that just white.

Here is one of many cases me using blue shadow:
image

Ideally, the shadow could react to the element or the background colour (choosing one of two). But not sure if that's possible in CSS.

@ghost
Copy link

ghost commented Apr 22, 2021

@fabien-ml Your snippet is awesome! Just a little improvement because it threw an exception for me:

const colors = require('tailwindcss/colors')

function hexToRgb(hex) {
  const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
  hex = hex.replace(shorthandRegex, function (m, r, g, b) {
    return r + r + g + g + b + b;
  });

  var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
  return result
    ? {
        red: parseInt(result[1], 16),
        green: parseInt(result[2], 16),
        blue: parseInt(result[3], 16),
      }
    : null
}

function makeShadow(name, rgb) {
  const obj = {}

  const nameWithDash = name ? `${name}-`: ''
  const defaultName = name ? name : 'DEFAULT'

  obj[`${nameWithDash}xs`] = `0 0 0 1px rgba(${rgb}, 0.05)`
  obj[`${nameWithDash}sm`] = `0 1px 2px 0 rgba(${rgb}, 0.05)`
  obj[defaultName] = `0 1px 3px 0 rgba(${rgb}, 0.1), 0 1px 2px 0 rgba(${rgb}, 0.06)`
  obj[`${nameWithDash}md`] = `0 4px 6px -1px rgba(${rgb}, 0.1), 0 2px 4px -1px rgba(${rgb}, 0.06)`
  obj[`${nameWithDash}lg`] = `0 10px 15px -3px rgba(${rgb}, 0.1), 0 4px 6px -2px rgba(${rgb}, 0.05)`
  obj[`${nameWithDash}xl`] = `0 20px 25px -5px rgba(${rgb}, 0.1), 0 10px 10px -5px rgba(${rgb}, 0.04)`
  obj[`${nameWithDash}2xl`] = `0 25px 50px -12px rgba(${rgb}, 0.25)`
  obj[`${nameWithDash}inner`] = `inset 0 2px 4px 0 rgba(${rgb}, 0.06)`
  return obj
}

function buildShadowPalette(theme) {

  // default tailwindcss black shadows 
  const defaultPalette = { 
    ...makeShadow('', '0, 0, 0'),
    outline: '0 0 0 3px rgba(66, 153, 225, 0.5)',
    none: 'none'
  }

  const coloredShadowPalette = Object.values(
    Object.entries(theme('colors')).reduce((acc, curr) => {
      const [k, v] = curr
      if (typeof v === 'string' && v !== 'transparent' && v !== 'currentColor') {
          const { red, green, blue } = hexToRgb(v)
          acc[k] = makeShadow(k, `${red}, ${green}, ${blue}`)
      }
      if (typeof v === 'object') {
          Object.entries(v).forEach(([_k, _v]) => {
              const { red, green, blue } = hexToRgb(_v)
              acc[`${k}-${_k}`] = makeShadow(
                  `${k}-${_k}`,
                  `${red}, ${green}, ${blue}`,
              )
          })
      }
      return acc
    }, {})
  )
  
  return coloredShadowPalette.reduce((acc, cur) => ({ ...acc, ...cur }), defaultPalette)
}

module.exports = {
  theme: {
    colors: {
      primary: colors.teal,
      neutral: colors.coolGray,
      info: colors.blue,
      success: colors.emerald,
      warning: colors.amber,
      danger: colors.rose
    },
    boxShadow: (theme) => {
      return {
        ...buildShadowPalette(theme),
      }
    },
  },
  variants: {},
  plugins: [],
}

To ensure that all the 3 letter hex codes are converted properly.
Worked perfectly after that addition!

Thanks for this! Used it to create a custom plugin:
shadowpaletteplugin.js

const plugin = require('tailwindcss/plugin');

function hexToRgb(hex) {
    var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
    return result
      ? {
          red: parseInt(result[1], 16),
          green: parseInt(result[2], 16),
          blue: parseInt(result[3], 16),
        }
      : null
  }
  
  function makeShadow(name, rgb) {
    const obj = {}

    const slicedName = name.includes('DEFAULT') ? name.slice(0, name.indexOf('-DEFAULT')) : name;
    const nameWithDash = slicedName ? `${slicedName}-`: '';
  
    obj[`.shadow-${nameWithDash}xs`] = {
        boxShadow: `0 0 0 1px rgba(${rgb}, 0.05)`
    }
    obj[`.shadow-${nameWithDash}sm`] = {
        boxShadow: `0 1px 2px 0 rgba(${rgb}, 0.05)`
    }
    obj[`.shadow-${nameWithDash}md`] = {
        boxShadow: `0 4px 6px -1px rgba(${rgb}, 0.1), 0 2px 4px -1px rgba(${rgb}, 0.06)`
    }
    obj[`.shadow-${nameWithDash}lg`] = {
        boxShadow: `0 10px 15px -3px rgba(${rgb}, 0.1), 0 4px 6px -2px rgba(${rgb}, 0.05)`
    }
    obj[`.shadow-${nameWithDash}xl`] = {
        boxShadow: `0 20px 25px -5px rgba(${rgb}, 0.1), 0 10px 10px -5px rgba(${rgb}, 0.04)`
    }
    obj[`.shadow-${nameWithDash}2xl`] = {
        boxShadow: `0 25px 50px -12px rgba(${rgb}, 0.25)`
    }
    obj[`.shadow-${nameWithDash}3xl`] = {
        boxShadow: `0 0 8px 0 rgba(${rgb}, 0.5)`
    }
    obj[`.shadow-${nameWithDash}inner`] = {
        boxShadow: `inset 0 2px 4px 0 rgba(${rgb}, 0.06)`
    }
    return obj
  }
  
  function buildShadowPalette(theme) {    
    const coloredShadowPalette = Object.values(
      Object.entries(theme('colors')).reduce((acc, curr) => {
        const [k, v] = curr
        if (typeof v === 'string' && v !== 'transparent' && v !== 'currentColor') {
            const { red, green, blue } = hexToRgb(v)
            acc[k] = makeShadow(k, `${red}, ${green}, ${blue}`)
        }
        if (typeof v === 'object') {
          
          Object.entries(v).forEach(([_k, _v]) => {
                const { red, green, blue } = hexToRgb(_v)
                acc[`${k}-${_k}`] = makeShadow(
                    `${k}-${_k}`,
                    `${red}, ${green}, ${blue}`,
                )
            })
        }
        return acc
      }, {})
    )
    
    return coloredShadowPalette.reduce((acc, cur) => ({ ...acc, ...cur }), {})
  }


module.exports = plugin(function ({theme, addUtilities, variants}) {
    addUtilities([
        buildShadowPalette(theme)
        ], {
        variants: [...variants('boxShadow'), 'active']
    })
  });

And in the tailwind.config.js file, add:

module.exports = {
  theme: {
    colors: {
      primary: colors.teal,
      neutral: colors.coolGray,
      info: colors.blue,
      success: colors.emerald,
      warning: colors.amber,
      danger: colors.rose
    },
  plugins: [
      require('./shadowpaletteplugin')
  ],
}

@WBDC
Copy link

WBDC commented Jun 28, 2021

This should be part of the core.
If we can add text, background, etc colors in the config, e.g.: blueish: '#5e34eb', which will generate the the classes, e.g.: ring-blueish, then the shadows should be generated too.

P.s.: Just started to play around with TW, but so far, I think I will use it in future projects.

@adamwathan

Going to close this as not something I plan to add to the default config out of the box right now, but of course can add in your own config 👍 Can definitely see a use case for things like focus styles on different colored buttons for example.

This was your closure comment over a year ago. Still not considering to add it?

@jwbaldwin
Copy link

jwbaldwin commented Jul 17, 2021

function hexToRgb(hex) {
  const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
  hex = hex.replace(shorthandRegex, function (m, r, g, b) {
    return r + r + g + g + b + b;
  });

  var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
  return result
    ? {
        red: parseInt(result[1], 16),
        green: parseInt(result[2], 16),
        blue: parseInt(result[3], 16),
      }
    : null
}

This also now needs to handle triplets like #000 and #fff

Updated:

// https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb/19765382
function hexToRgb(hex) {
  // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
  var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
  hex = hex.replace(shorthandRegex, function(m, r, g, b) {
    return r + r + g + g + b + b;
  });

  var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
  return result ? {
    red: parseInt(result[1], 16),
    green: parseInt(result[2], 16),
    blue: parseInt(result[3], 16)
  } : null;
}

Thanks for the solution @isMandyCoding! So helpful :)

@typedashutosh
Copy link

typedashutosh commented Jul 20, 2021

I'd to combine @isMandyCoding 's plugin with @jwbaldwin 's converter to create this. This code works fine for me.

// shadowpaletteplugin.js

const plugin = require('tailwindcss/plugin')

// https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb/19765382
function hexToRgb(hex) {
	// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
	var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i
	hex = hex.replace(shorthandRegex, function (m, r, g, b) {
		return r + r + g + g + b + b
	})

	var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
	return result
		? {
				red: parseInt(result[1], 16),
				green: parseInt(result[2], 16),
				blue: parseInt(result[3], 16)
		  }
		: null
}

function makeShadow(name, rgb) {
	const obj = {}

	const slicedName = name.includes('DEFAULT') ? name.slice(0, name.indexOf('-DEFAULT')) : name
	const nameWithDash = slicedName ? `${slicedName}-` : ''

	obj[`.shadow-${nameWithDash}xs`] = {
		boxShadow: `0 0 0 1px rgba(${rgb}, 0.05)`
	}
	obj[`.shadow-${nameWithDash}sm`] = {
		boxShadow: `0 1px 2px 0 rgba(${rgb}, 0.05)`
	}
	obj[`.shadow-${nameWithDash}md`] = {
		boxShadow: `0 4px 6px -1px rgba(${rgb}, 0.1), 0 2px 4px -1px rgba(${rgb}, 0.06)`
	}
	obj[`.shadow-${nameWithDash}lg`] = {
		boxShadow: `0 10px 15px -3px rgba(${rgb}, 0.1), 0 4px 6px -2px rgba(${rgb}, 0.05)`
	}
	obj[`.shadow-${nameWithDash}xl`] = {
		boxShadow: `0 20px 25px -5px rgba(${rgb}, 0.1), 0 10px 10px -5px rgba(${rgb}, 0.04)`
	}
	obj[`.shadow-${nameWithDash}2xl`] = {
		boxShadow: `0 25px 50px -12px rgba(${rgb}, 0.25)`
	}
	obj[`.shadow-${nameWithDash}3xl`] = {
		boxShadow: `0 0 8px 0 rgba(${rgb}, 0.5)`
	}
	obj[`.shadow-${nameWithDash}inner`] = {
		boxShadow: `inset 0 2px 4px 0 rgba(${rgb}, 0.06)`
	}
	return obj
}

function buildShadowPalette(theme) {
	const coloredShadowPalette = Object.values(
		Object.entries(theme('colors')).reduce((acc, curr) => {
			const [k, v] = curr
			if (typeof v === 'string' && v !== 'transparent' && v !== 'currentColor') {
				const { red, green, blue } = hexToRgb(v)
				acc[k] = makeShadow(k, `${red}, ${green}, ${blue}`)
			}
			if (typeof v === 'object') {
				Object.entries(v).forEach(([_k, _v]) => {
					const { red, green, blue } = hexToRgb(_v)
					acc[`${k}-${_k}`] = makeShadow(`${k}-${_k}`, `${red}, ${green}, ${blue}`)
				})
			}
			return acc
		}, {})
	)

	return coloredShadowPalette.reduce((acc, cur) => ({ ...acc, ...cur }), {})
}

module.exports = plugin(function ({ theme, addUtilities, variants }) {
	addUtilities([buildShadowPalette(theme)], {
		variants: [...variants('boxShadow'), 'active']
	})
})
// tailwind.config.js

module.exports = {
	purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'],
	darkMode: false, // or 'media' or 'class'
	theme: {
		extend: {}
	},
	variants: {
		extend: {}
	},
	plugins: [require('./shadowpaletteplugin')]
}

Although this works as expected, but shadows like shadow-color-300-md etc. are hardly visible and are so much that it exists because it does. So there's no point generating 80 different shades for every color just slowing down your development process. Rather adding required shadows manually is still a good idea.

Thanks Everyone!

@deadcoder0904
Copy link

deadcoder0904 commented Jul 25, 2021

Is this possible with JIT mode without requiring the plugin?

Edit: It's not possible. I went with 1 custom property hah 🎃

#nprogress .peg {
	box-shadow: 0 0 20px rebeccapurple, 0 0 15px rebeccapurple;
	@apply block absolute right-0 w-[140px] h-full opacity-100 rotate-3 translate-x-0 -translate-y-1;
}

@adamwathan
Copy link
Member

I've started prototyping support for colored shadows and am hoping it makes it into v3 in the fall.

@typedashutosh
Copy link

typedashutosh commented Jul 25, 2021

@adamwathan
This shadow plugin looked fascinating to me at first but since shadow colors had an alpha varying from 0.05 to 0.25, shadows for color like red-300 were just unusable because it was so subtle to be visible. Then I realized that I was generating over 80 shadows which were completely useless to me. Then I just removed this shadow plugin thing as it was slowing down my development process and I finally decided to define usable shadows myself.
Also in production, I never used more then five or say seven shadows.
Although having an option is much appreciated.

@adamwathan
Copy link
Member

Yep it's not an easy problem to solve, agree with all the concerns you've presented. Hoping we find a clever approach that makes it feel useful but only a few weeks of R&D will say for sure.

@dhanushkac
Copy link

Found this thread while looking for this feature. Something nice to have and would definitely increase the ground with tailwind.

@thomasin
Copy link

thomasin commented Nov 9, 2021

A possible alternative, using whatever colour-to-rgb function you want. I haven't used it much (tested on v2 with jit), may have downsides

(Use like class="shadow shadow-green-300")

In theme object:

    boxShadow: {
        sm: '0 1px 2px 0 rgba(var(--box-shadow-color), 0.05)',
        DEFAULT: '0 1px 3px 0 rgba(var(--box-shadow-color), 0.1), 0 1px 2px 0 rgba(var(--box-shadow-color), 0.06)',
        md: '0 4px 6px -1px rgba(var(--box-shadow-color), 0.1), 0 2px 4px -1px rgba(var(--box-shadow-color), 0.06)',
        lg: '0 10px 15px -3px rgba(var(--box-shadow-color), 0.1), 0 4px 6px -2px rgba(var(--box-shadow-color), 0.05)',
        xl: '0 20px 25px -5px rgba(var(--box-shadow-color), 0.1), 0 10px 10px -5px rgba(var(--box-shadow-color), 0.04)',
        '2xl': '0 25px 50px -12px rgba(var(--box-shadow-color), 0.25)',
        inner: 'inset 0 2px 4px 0 rgba(var(--box-shadow-color), 0.06)',
        none: 'none',
    },

Plugin (note: depending on your rgb function, will need to handle invalid colour entries by returning early)

    plugin(function({ addUtilities, theme }) {
      const colors = theme('colors')

      const boxShadowColors = Object.entries(colors).reduce((boxShadowColors_, [colorKey, colorValue]) => {
        const [r, g, b] = toRgba(colorValue)
        return { ...boxShadowColors_, [`.shadow-${colorKey}`]: { '--box-shadow-color': `${r},${g},${b}` } }
      }, {})

      addUtilities(boxShadowColors)
    }),

For default colours, can set a base variable in css file or alternately in the box shadow properties themselves like var(--box-shadow-color, 0, 0, 0)

*, ::before, ::after {
  @apply shadow-black;
}

@NoahBres
Copy link

NoahBres commented Nov 9, 2021

@thomasin This has been merged into the latest tailwind branch: #5979

@thomasin
Copy link

thomasin commented Nov 9, 2021

Oh amazing! Thanks for the heads up

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

No branches or pull requests