diff --git a/packages/tailwindcss-language-server/src/util/color.ts b/packages/tailwindcss-language-server/src/util/color.ts
index c1378e8bdc32ff8166dd58912cb57d2fe710b68e..4e4b5985fdcfedda75373453c32c04218f8a7f55 100644
--- a/packages/tailwindcss-language-server/src/util/color.ts
+++ b/packages/tailwindcss-language-server/src/util/color.ts
@@ -1,6 +1,8 @@
const dlv = require('dlv')
import { State } from './state'
import removeMeta from './removeMeta'
+import { TinyColor } from '@ctrl/tinycolor'
+import { ensureArray, dedupe, flatten } from './array'
const COLOR_PROPS = [
'caret-color',
@@ -19,159 +21,6 @@ 'stroke',
'text-decoration-color',
]
-const COLOR_NAMES = {
- transparent: 'rgba(0, 0, 0, 0.01)',
- aliceblue: '#f0f8ff',
- antiquewhite: '#faebd7',
- aqua: '#0ff',
- aquamarine: '#7fffd4',
- azure: '#f0ffff',
- beige: '#f5f5dc',
- bisque: '#ffe4c4',
- black: '#000',
- blanchedalmond: '#ffebcd',
- blue: '#00f',
- blueviolet: '#8a2be2',
- brown: '#a52a2a',
- burlywood: '#deb887',
- burntsienna: '#ea7e5d',
- cadetblue: '#5f9ea0',
- chartreuse: '#7fff00',
- chocolate: '#d2691e',
- coral: '#ff7f50',
- cornflowerblue: '#6495ed',
- cornsilk: '#fff8dc',
- crimson: '#dc143c',
- cyan: '#0ff',
- darkblue: '#00008b',
- darkcyan: '#008b8b',
- darkgoldenrod: '#b8860b',
- darkgray: '#a9a9a9',
- darkgreen: '#006400',
- darkgrey: '#a9a9a9',
- darkkhaki: '#bdb76b',
- darkmagenta: '#8b008b',
- darkolivegreen: '#556b2f',
- darkorange: '#ff8c00',
- darkorchid: '#9932cc',
- darkred: '#8b0000',
- darksalmon: '#e9967a',
- darkseagreen: '#8fbc8f',
- darkslateblue: '#483d8b',
- darkslategray: '#2f4f4f',
- darkslategrey: '#2f4f4f',
- darkturquoise: '#00ced1',
- darkviolet: '#9400d3',
- deeppink: '#ff1493',
- deepskyblue: '#00bfff',
- dimgray: '#696969',
- dimgrey: '#696969',
- dodgerblue: '#1e90ff',
- firebrick: '#b22222',
- floralwhite: '#fffaf0',
- forestgreen: '#228b22',
- fuchsia: '#f0f',
- gainsboro: '#dcdcdc',
- ghostwhite: '#f8f8ff',
- gold: '#ffd700',
- goldenrod: '#daa520',
- gray: '#808080',
- green: '#008000',
- greenyellow: '#adff2f',
- grey: '#808080',
- honeydew: '#f0fff0',
- hotpink: '#ff69b4',
- indianred: '#cd5c5c',
- indigo: '#4b0082',
- ivory: '#fffff0',
- khaki: '#f0e68c',
- lavender: '#e6e6fa',
- lavenderblush: '#fff0f5',
- lawngreen: '#7cfc00',
- lemonchiffon: '#fffacd',
- lightblue: '#add8e6',
- lightcoral: '#f08080',
- lightcyan: '#e0ffff',
- lightgoldenrodyellow: '#fafad2',
- lightgray: '#d3d3d3',
- lightgreen: '#90ee90',
- lightgrey: '#d3d3d3',
- lightpink: '#ffb6c1',
- lightsalmon: '#ffa07a',
- lightseagreen: '#20b2aa',
- lightskyblue: '#87cefa',
- lightslategray: '#789',
- lightslategrey: '#789',
- lightsteelblue: '#b0c4de',
- lightyellow: '#ffffe0',
- lime: '#0f0',
- limegreen: '#32cd32',
- linen: '#faf0e6',
- magenta: '#f0f',
- maroon: '#800000',
- mediumaquamarine: '#66cdaa',
- mediumblue: '#0000cd',
- mediumorchid: '#ba55d3',
- mediumpurple: '#9370db',
- mediumseagreen: '#3cb371',
- mediumslateblue: '#7b68ee',
- mediumspringgreen: '#00fa9a',
- mediumturquoise: '#48d1cc',
- mediumvioletred: '#c71585',
- midnightblue: '#191970',
- mintcream: '#f5fffa',
- mistyrose: '#ffe4e1',
- moccasin: '#ffe4b5',
- navajowhite: '#ffdead',
- navy: '#000080',
- oldlace: '#fdf5e6',
- olive: '#808000',
- olivedrab: '#6b8e23',
- orange: '#ffa500',
- orangered: '#ff4500',
- orchid: '#da70d6',
- palegoldenrod: '#eee8aa',
- palegreen: '#98fb98',
- paleturquoise: '#afeeee',
- palevioletred: '#db7093',
- papayawhip: '#ffefd5',
- peachpuff: '#ffdab9',
- peru: '#cd853f',
- pink: '#ffc0cb',
- plum: '#dda0dd',
- powderblue: '#b0e0e6',
- purple: '#800080',
- rebeccapurple: '#663399',
- red: '#f00',
- rosybrown: '#bc8f8f',
- royalblue: '#4169e1',
- saddlebrown: '#8b4513',
- salmon: '#fa8072',
- sandybrown: '#f4a460',
- seagreen: '#2e8b57',
- seashell: '#fff5ee',
- sienna: '#a0522d',
- silver: '#c0c0c0',
- skyblue: '#87ceeb',
- slateblue: '#6a5acd',
- slategray: '#708090',
- slategrey: '#708090',
- snow: '#fffafa',
- springgreen: '#00ff7f',
- steelblue: '#4682b4',
- tan: '#d2b48c',
- teal: '#008080',
- thistle: '#d8bfd8',
- tomato: '#ff6347',
- turquoise: '#40e0d0',
- violet: '#ee82ee',
- wheat: '#f5deb3',
- white: '#fff',
- whitesmoke: '#f5f5f5',
- yellow: '#ff0',
- yellowgreen: '#9acd32',
-}
-
export function getColor(
state: State,
keys: string[]
@@ -179,42 +28,68 @@ ): { documentation?: string } {
const item = dlv(state.classNames.classNames, keys)
if (!item.__rule) return null
const props = Object.keys(removeMeta(item))
+ if (props.length === 0) return null
const nonCustomProps = props.filter((prop) => !prop.startsWith('--'))
- if (nonCustomProps.length !== 1) return null
- const prop = nonCustomProps[0]
- if (COLOR_PROPS.indexOf(prop) === -1) return null
+
+ const areAllCustom = nonCustomProps.length === 0
- const namedColor = COLOR_NAMES[item[prop].toLowerCase()]
- if (namedColor) {
- return { documentation: namedColor }
+ if (
+ !areAllCustom &&
+ nonCustomProps.some((prop) => !COLOR_PROPS.includes(prop))
+ ) {
+ // they should all be color-based props
+ return null
}
- // matches: rgba(<r>, <g>, <b>, var(--bg-opacity))
- // TODO: support other formats? e.g. hsla, css level 4
- const match = item[prop].match(
- /^\s*rgba\(\s*(?<r>[0-9]{1,3})\s*,\s*(?<g>[0-9]{1,3})\s*,\s*(?<b>[0-9]{1,3})\s*,\s*var/
+ const propsToCheck = areAllCustom ? props : nonCustomProps
+
+ const colors = flatten(
+ propsToCheck.map((prop) => ensureArray(item[prop]).map(createColor))
)
- if (match) {
- return {
- documentation: `rgb(${match.groups.r}, ${match.groups.g}, ${match.groups.b})`,
- }
+
+ // check that all of the values are valid colors
+ if (colors.some((color) => !color.isValid)) {
+ return null
}
- return {}
-}
+ // check that all of the values are the same color
+ const colorStrings = colors.map((color) => color.toRgbString())
+ if (dedupe(colorStrings).length !== 1) {
+ return null
+ }
-export function isColor(str: any): boolean {
- return (
- typeof str === 'string' &&
- /^(?:#|0x)(?:[a-f0-9]{3,4}|[a-f0-9]{6}|[a-f0-9]{8})\b|(?:rgb|hsl)a?\([^\)]*\)$/.test(
- str.trim()
- )
- )
+ return { documentation: colorStrings[0] }
}
export function getColorFromString(str: string): string {
- if (isColor(str)) {
- return str
+ if (str === 'transparent') {
+ return 'rgba(0, 0, 0, 0.01)'
}
- return COLOR_NAMES[str] || null
+ const color = new TinyColor(str)
+ if (color.isValid) {
+ return color.toRgbString()
+ }
+ return null
+}
+
+function createColor(str: string): TinyColor {
+ if (str === 'transparent') {
+ return new TinyColor({ r: 0, g: 0, b: 0, a: 0.01 })
+ }
+
+ // matches: rgba(<r>, <g>, <b>, var(--bg-opacity))
+ // TODO: support other formats? e.g. hsla, css level 4
+ const match = str.match(
+ /^\s*rgba\(\s*(?<r>[0-9]{1,3})\s*,\s*(?<g>[0-9]{1,3})\s*,\s*(?<b>[0-9]{1,3})\s*,\s*var/
+ )
+
+ if (match) {
+ return new TinyColor({
+ r: match.groups.r,
+ g: match.groups.g,
+ b: match.groups.b,
+ })
+ }
+
+ return new TinyColor(str)
}