Add react color picker, show tailwind colors all the time
This commit is contained in:
11
package-lock.json
generated
11
package-lock.json
generated
@@ -10,6 +10,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tailwindcss/vite": "^4.0.6",
|
"@tailwindcss/vite": "^4.0.6",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
|
"react-colorful": "^5.6.1",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"tailwindcss": "^4.0.6"
|
"tailwindcss": "^4.0.6"
|
||||||
},
|
},
|
||||||
@@ -3250,6 +3251,16 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-colorful": {
|
||||||
|
"version": "5.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz",
|
||||||
|
"integrity": "sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8.0",
|
||||||
|
"react-dom": ">=16.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-dom": {
|
"node_modules/react-dom": {
|
||||||
"version": "19.0.0",
|
"version": "19.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz",
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tailwindcss/vite": "^4.0.6",
|
"@tailwindcss/vite": "^4.0.6",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
|
"react-colorful": "^5.6.1",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"tailwindcss": "^4.0.6"
|
"tailwindcss": "^4.0.6"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import { useState } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
|
import { HexColorPicker } from 'react-colorful'
|
||||||
|
import { hexToRgb, rgbToHex, rgbToHsl, hslToRgb, rgbToHsb, hsbToRgb, findClosestTailwindColor } from '../utils/colors'
|
||||||
|
import { TailwindColors } from './TailwindColors'
|
||||||
|
|
||||||
type RGB = {
|
type RGB = {
|
||||||
r: number
|
r: number
|
||||||
@@ -23,21 +26,102 @@ type ColorFormat = 'hex' | 'rgb' | 'hsl' | 'hsb' | 'tailwind'
|
|||||||
export function ColorPicker() {
|
export function ColorPicker() {
|
||||||
const [color, setColor] = useState('#6366F1') // Default to indigo-500
|
const [color, setColor] = useState('#6366F1') // Default to indigo-500
|
||||||
const [format, setFormat] = useState<ColorFormat>('hex')
|
const [format, setFormat] = useState<ColorFormat>('hex')
|
||||||
|
const [displayValue, setDisplayValue] = useState(color)
|
||||||
|
const [closestTailwind, setClosestTailwind] = useState<{ name: string; hex: string }>({ name: 'indigo-500', hex: '#6366F1' })
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const rgb = hexToRgb(color)
|
||||||
|
if (!rgb) return
|
||||||
|
|
||||||
|
// Always update closest Tailwind color
|
||||||
|
const closest = findClosestTailwindColor(color)
|
||||||
|
setClosestTailwind(closest)
|
||||||
|
|
||||||
|
switch (format) {
|
||||||
|
case 'hex':
|
||||||
|
setDisplayValue(color)
|
||||||
|
break
|
||||||
|
case 'rgb':
|
||||||
|
setDisplayValue(`rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`)
|
||||||
|
break
|
||||||
|
case 'hsl': {
|
||||||
|
const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b)
|
||||||
|
setDisplayValue(`hsl(${hsl.h}, ${hsl.s}%, ${hsl.l}%)`)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'hsb': {
|
||||||
|
const hsb = rgbToHsb(rgb.r, rgb.g, rgb.b)
|
||||||
|
setDisplayValue(`hsb(${hsb.h}, ${hsb.s}%, ${hsb.b}%)`)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'tailwind':
|
||||||
|
setDisplayValue(closest.name)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}, [color, format])
|
||||||
|
|
||||||
|
const handleInputChange = (value: string) => {
|
||||||
|
let newHex = color
|
||||||
|
|
||||||
|
if (format === 'hex' && /^#[0-9A-Fa-f]{6}$/.test(value)) {
|
||||||
|
newHex = value
|
||||||
|
} else if (format === 'rgb') {
|
||||||
|
const match = value.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/)
|
||||||
|
if (match) {
|
||||||
|
const [_, r, g, b] = match.map(Number)
|
||||||
|
if (r <= 255 && g <= 255 && b <= 255) {
|
||||||
|
newHex = rgbToHex(r, g, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (format === 'hsl') {
|
||||||
|
const match = value.match(/^hsl\((\d+),\s*(\d+)%,\s*(\d+)%\)$/)
|
||||||
|
if (match) {
|
||||||
|
const [_, h, s, l] = match.map(Number)
|
||||||
|
if (h <= 360 && s <= 100 && l <= 100) {
|
||||||
|
const rgb = hslToRgb(h, s, l)
|
||||||
|
newHex = rgbToHex(rgb.r, rgb.g, rgb.b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (format === 'hsb') {
|
||||||
|
const match = value.match(/^hsb\((\d+),\s*(\d+)%,\s*(\d+)%\)$/)
|
||||||
|
if (match) {
|
||||||
|
const [_, h, s, b] = match.map(Number)
|
||||||
|
if (h <= 360 && s <= 100 && b <= 100) {
|
||||||
|
const rgb = hsbToRgb(h, s, b)
|
||||||
|
newHex = rgbToHex(rgb.r, rgb.g, rgb.b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setColor(newHex)
|
||||||
|
setDisplayValue(value)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full max-w-2xl mx-auto p-6 space-y-6">
|
<div className="w-full max-w-7xl mx-auto p-6">
|
||||||
<div className="flex flex-col items-center gap-6">
|
<h1 className="text-3xl font-bold text-center mb-8">Color Picker</h1>
|
||||||
<h1 className="text-3xl font-bold">Color Picker</h1>
|
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-[1fr_2fr] gap-8">
|
||||||
|
{/* Left Column: Color Picker and Input */}
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div className="flex flex-col items-center gap-4 p-6 bg-white rounded-xl shadow-sm">
|
||||||
{/* Color Preview */}
|
{/* Color Preview */}
|
||||||
<div
|
<div
|
||||||
className="w-32 h-32 rounded-lg shadow-lg border border-zinc-200"
|
className="w-32 h-32 rounded-lg shadow-lg border border-zinc-200"
|
||||||
style={{ backgroundColor: color }}
|
style={{ backgroundColor: color }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Color Input */}
|
<div className="text-sm text-zinc-600">
|
||||||
<div className="w-full max-w-md">
|
Closest Tailwind: <span className="font-medium">{closestTailwind.name}</span>
|
||||||
<div className="flex gap-2 mb-4">
|
</div>
|
||||||
|
|
||||||
|
{/* Color Picker */}
|
||||||
|
<div className="w-full max-w-[200px]">
|
||||||
|
<HexColorPicker color={color} onChange={setColor} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Format Buttons */}
|
||||||
|
<div className="flex flex-wrap gap-2 justify-center w-full">
|
||||||
{(['hex', 'rgb', 'hsl', 'hsb', 'tailwind'] as const).map((f) => (
|
{(['hex', 'rgb', 'hsl', 'hsb', 'tailwind'] as const).map((f) => (
|
||||||
<button
|
<button
|
||||||
key={f}
|
key={f}
|
||||||
@@ -53,23 +137,24 @@ export function ColorPicker() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="relative">
|
{/* Color Input */}
|
||||||
|
<div className="w-full">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={color}
|
value={displayValue}
|
||||||
onChange={(e) => setColor(e.target.value)}
|
onChange={(e) => handleInputChange(e.target.value)}
|
||||||
className="w-full px-4 py-2 rounded-md border border-zinc-300 focus:outline-none focus:ring-2 focus:ring-indigo-500"
|
className="w-full px-4 py-2 rounded-md border border-zinc-300 focus:outline-none focus:ring-2 focus:ring-indigo-500"
|
||||||
placeholder="Enter color value..."
|
placeholder="Enter color value..."
|
||||||
/>
|
/>
|
||||||
<input
|
|
||||||
type="color"
|
|
||||||
value={color}
|
|
||||||
onChange={(e) => setColor(e.target.value)}
|
|
||||||
className="absolute right-2 top-1/2 -translate-y-1/2 w-8 h-8 p-0 border-0 rounded-md cursor-pointer"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Right Column: Tailwind Colors */}
|
||||||
|
<div className="bg-white rounded-xl shadow-sm p-4">
|
||||||
|
<TailwindColors onColorSelect={setColor} selectedColor={color} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
40
src/components/TailwindColors.tsx
Normal file
40
src/components/TailwindColors.tsx
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { useMemo } from 'react'
|
||||||
|
import { TAILWIND_COLORS } from '../utils/colors'
|
||||||
|
|
||||||
|
interface TailwindColorsProps {
|
||||||
|
onColorSelect: (color: string) => void
|
||||||
|
selectedColor?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TailwindColors({ onColorSelect, selectedColor }: TailwindColorsProps) {
|
||||||
|
const colorEntries = useMemo(() => Object.entries(TAILWIND_COLORS), [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full overflow-x-auto">
|
||||||
|
<div className="grid grid-cols-[repeat(auto-fit,minmax(100px,1fr))] gap-4">
|
||||||
|
{colorEntries.map(([colorName, shades]) => (
|
||||||
|
<div key={colorName} className="space-y-2">
|
||||||
|
<div className="text-sm font-medium text-zinc-600 capitalize">{colorName}</div>
|
||||||
|
<div className="grid grid-rows-[repeat(11,1fr)] gap-1">
|
||||||
|
{Object.entries(shades).map(([shade, hex]) => (
|
||||||
|
<button
|
||||||
|
key={`${colorName}-${shade}`}
|
||||||
|
onClick={() => onColorSelect(hex)}
|
||||||
|
className={`w-full h-7 rounded transition-all hover:scale-105 hover:shadow-lg group relative
|
||||||
|
${selectedColor === hex ? 'ring-2 ring-offset-2 ring-indigo-500' : ''}`}
|
||||||
|
style={{ backgroundColor: hex }}
|
||||||
|
title={`${colorName}-${shade}: ${hex}`}
|
||||||
|
>
|
||||||
|
<span className={`absolute left-2 text-xs font-medium opacity-0 group-hover:opacity-100 transition-opacity
|
||||||
|
${Number(shade) > 500 ? 'text-white' : 'text-black'}`}>
|
||||||
|
{shade}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -182,41 +182,225 @@ export function hsbToRgb(h: number, s: number, v: number): { r: number; g: numbe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to find the closest Tailwind color
|
// Function to find the closest Tailwind color using CIELAB color space
|
||||||
export function findClosestTailwindColor(hex: string): string {
|
export function findClosestTailwindColor(hex: string): { name: string; hex: string } {
|
||||||
// This is a simplified version - we'll expand this with actual Tailwind colors
|
const rgb1 = hexToRgb(hex)
|
||||||
const tailwindColors = {
|
if (!rgb1) return { name: 'Invalid color', hex }
|
||||||
'red-500': '#EF4444',
|
|
||||||
'blue-500': '#3B82F6',
|
|
||||||
'green-500': '#22C55E',
|
|
||||||
'yellow-500': '#EAB308',
|
|
||||||
'purple-500': '#A855F7',
|
|
||||||
'pink-500': '#EC4899',
|
|
||||||
'indigo-500': '#6366F1',
|
|
||||||
// Add more colors as needed
|
|
||||||
}
|
|
||||||
|
|
||||||
const targetRgb = hexToRgb(hex)
|
|
||||||
if (!targetRgb) return 'Invalid color'
|
|
||||||
|
|
||||||
let closestColor = ''
|
let closestColor = ''
|
||||||
|
let closestHex = ''
|
||||||
let minDistance = Infinity
|
let minDistance = Infinity
|
||||||
|
|
||||||
Object.entries(tailwindColors).forEach(([name, colorHex]) => {
|
// Convert RGB to Lab
|
||||||
const currentRgb = hexToRgb(colorHex)
|
const lab1 = rgbToLab(rgb1.r, rgb1.g, rgb1.b)
|
||||||
if (!currentRgb) return
|
|
||||||
|
|
||||||
const distance = Math.sqrt(
|
Object.entries(TAILWIND_COLORS).forEach(([colorName, shades]) => {
|
||||||
Math.pow(targetRgb.r - currentRgb.r, 2) +
|
Object.entries(shades).forEach(([shade, colorHex]) => {
|
||||||
Math.pow(targetRgb.g - currentRgb.g, 2) +
|
const rgb2 = hexToRgb(colorHex)
|
||||||
Math.pow(targetRgb.b - currentRgb.b, 2)
|
if (!rgb2) return
|
||||||
)
|
|
||||||
|
const lab2 = rgbToLab(rgb2.r, rgb2.g, rgb2.b)
|
||||||
|
const distance = deltaE(lab1, lab2)
|
||||||
|
|
||||||
if (distance < minDistance) {
|
if (distance < minDistance) {
|
||||||
minDistance = distance
|
minDistance = distance
|
||||||
closestColor = name
|
closestColor = `${colorName}-${shade}`
|
||||||
|
closestHex = colorHex
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
return closestColor
|
return { name: closestColor, hex: closestHex }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CIELAB color space conversion functions
|
||||||
|
function rgbToLab(r: number, g: number, b: number) {
|
||||||
|
let x, y, z
|
||||||
|
|
||||||
|
r = r / 255
|
||||||
|
g = g / 255
|
||||||
|
b = b / 255
|
||||||
|
|
||||||
|
r = r > 0.04045 ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92
|
||||||
|
g = g > 0.04045 ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92
|
||||||
|
b = b > 0.04045 ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92
|
||||||
|
|
||||||
|
x = (r * 0.4124 + g * 0.3576 + b * 0.1805) / 0.95047
|
||||||
|
y = (r * 0.2126 + g * 0.7152 + b * 0.0722) / 1.00000
|
||||||
|
z = (r * 0.0193 + g * 0.1192 + b * 0.9505) / 1.08883
|
||||||
|
|
||||||
|
x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + 16/116
|
||||||
|
y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + 16/116
|
||||||
|
z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + 16/116
|
||||||
|
|
||||||
|
return {
|
||||||
|
l: (116 * y) - 16,
|
||||||
|
a: 500 * (x - y),
|
||||||
|
b: 200 * (y - z)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate color difference using CIEDE2000
|
||||||
|
function deltaE(lab1: { l: number; a: number; b: number }, lab2: { l: number; a: number; b: number }) {
|
||||||
|
const deltaL = lab2.l - lab1.l
|
||||||
|
const deltaA = lab2.a - lab1.a
|
||||||
|
const deltaB = lab2.b - lab1.b
|
||||||
|
const c1 = Math.sqrt(lab1.a * lab1.a + lab1.b * lab1.b)
|
||||||
|
const c2 = Math.sqrt(lab2.a * lab2.a + lab2.b * lab2.b)
|
||||||
|
const deltaC = c2 - c1
|
||||||
|
let deltaH = deltaA * deltaA + deltaB * deltaB - deltaC * deltaC
|
||||||
|
deltaH = deltaH < 0 ? 0 : Math.sqrt(deltaH)
|
||||||
|
const sc = 1.0 + 0.045 * c1
|
||||||
|
const sh = 1.0 + 0.015 * c1
|
||||||
|
const deltaLKlsl = deltaL / (1.0)
|
||||||
|
const deltaCkcsc = deltaC / (sc)
|
||||||
|
const deltaHkhsh = deltaH / (sh)
|
||||||
|
return Math.sqrt(deltaLKlsl * deltaLKlsl + deltaCkcsc * deltaCkcsc + deltaHkhsh * deltaHkhsh)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export Tailwind colors for use in other components
|
||||||
|
export const TAILWIND_COLORS = {
|
||||||
|
slate: {
|
||||||
|
50: '#f8fafc',
|
||||||
|
100: '#f1f5f9',
|
||||||
|
200: '#e2e8f0',
|
||||||
|
300: '#cbd5e1',
|
||||||
|
400: '#94a3b8',
|
||||||
|
500: '#64748b',
|
||||||
|
600: '#475569',
|
||||||
|
700: '#334155',
|
||||||
|
800: '#1e293b',
|
||||||
|
900: '#0f172a',
|
||||||
|
950: '#020617',
|
||||||
|
},
|
||||||
|
gray: {
|
||||||
|
50: '#f9fafb',
|
||||||
|
100: '#f3f4f6',
|
||||||
|
200: '#e5e7eb',
|
||||||
|
300: '#d1d5db',
|
||||||
|
400: '#9ca3af',
|
||||||
|
500: '#6b7280',
|
||||||
|
600: '#4b5563',
|
||||||
|
700: '#374151',
|
||||||
|
800: '#1f2937',
|
||||||
|
900: '#111827',
|
||||||
|
950: '#030712',
|
||||||
|
},
|
||||||
|
zinc: {
|
||||||
|
50: '#fafafa',
|
||||||
|
100: '#f4f4f5',
|
||||||
|
200: '#e4e4e7',
|
||||||
|
300: '#d4d4d8',
|
||||||
|
400: '#a1a1aa',
|
||||||
|
500: '#71717a',
|
||||||
|
600: '#52525b',
|
||||||
|
700: '#3f3f46',
|
||||||
|
800: '#27272a',
|
||||||
|
900: '#18181b',
|
||||||
|
950: '#09090b',
|
||||||
|
},
|
||||||
|
red: {
|
||||||
|
50: '#fef2f2',
|
||||||
|
100: '#fee2e2',
|
||||||
|
200: '#fecaca',
|
||||||
|
300: '#fca5a5',
|
||||||
|
400: '#f87171',
|
||||||
|
500: '#ef4444',
|
||||||
|
600: '#dc2626',
|
||||||
|
700: '#b91c1c',
|
||||||
|
800: '#991b1b',
|
||||||
|
900: '#7f1d1d',
|
||||||
|
950: '#450a0a',
|
||||||
|
},
|
||||||
|
orange: {
|
||||||
|
50: '#fff7ed',
|
||||||
|
100: '#ffedd5',
|
||||||
|
200: '#fed7aa',
|
||||||
|
300: '#fdba74',
|
||||||
|
400: '#fb923c',
|
||||||
|
500: '#f97316',
|
||||||
|
600: '#ea580c',
|
||||||
|
700: '#c2410c',
|
||||||
|
800: '#9a3412',
|
||||||
|
900: '#7c2d12',
|
||||||
|
950: '#431407',
|
||||||
|
},
|
||||||
|
yellow: {
|
||||||
|
50: '#fefce8',
|
||||||
|
100: '#fef9c3',
|
||||||
|
200: '#fef08a',
|
||||||
|
300: '#fde047',
|
||||||
|
400: '#facc15',
|
||||||
|
500: '#eab308',
|
||||||
|
600: '#ca8a04',
|
||||||
|
700: '#a16207',
|
||||||
|
800: '#854d0e',
|
||||||
|
900: '#713f12',
|
||||||
|
950: '#422006',
|
||||||
|
},
|
||||||
|
green: {
|
||||||
|
50: '#f0fdf4',
|
||||||
|
100: '#dcfce7',
|
||||||
|
200: '#bbf7d0',
|
||||||
|
300: '#86efac',
|
||||||
|
400: '#4ade80',
|
||||||
|
500: '#22c55e',
|
||||||
|
600: '#16a34a',
|
||||||
|
700: '#15803d',
|
||||||
|
800: '#166534',
|
||||||
|
900: '#14532d',
|
||||||
|
950: '#052e16',
|
||||||
|
},
|
||||||
|
blue: {
|
||||||
|
50: '#eff6ff',
|
||||||
|
100: '#dbeafe',
|
||||||
|
200: '#bfdbfe',
|
||||||
|
300: '#93c5fd',
|
||||||
|
400: '#60a5fa',
|
||||||
|
500: '#3b82f6',
|
||||||
|
600: '#2563eb',
|
||||||
|
700: '#1d4ed8',
|
||||||
|
800: '#1e40af',
|
||||||
|
900: '#1e3a8a',
|
||||||
|
950: '#172554',
|
||||||
|
},
|
||||||
|
indigo: {
|
||||||
|
50: '#eef2ff',
|
||||||
|
100: '#e0e7ff',
|
||||||
|
200: '#c7d2fe',
|
||||||
|
300: '#a5b4fc',
|
||||||
|
400: '#818cf8',
|
||||||
|
500: '#6366f1',
|
||||||
|
600: '#4f46e5',
|
||||||
|
700: '#4338ca',
|
||||||
|
800: '#3730a3',
|
||||||
|
900: '#312e81',
|
||||||
|
950: '#1e1b4b',
|
||||||
|
},
|
||||||
|
purple: {
|
||||||
|
50: '#faf5ff',
|
||||||
|
100: '#f3e8ff',
|
||||||
|
200: '#e9d5ff',
|
||||||
|
300: '#d8b4fe',
|
||||||
|
400: '#c084fc',
|
||||||
|
500: '#a855f7',
|
||||||
|
600: '#9333ea',
|
||||||
|
700: '#7e22ce',
|
||||||
|
800: '#6b21a8',
|
||||||
|
900: '#581c87',
|
||||||
|
950: '#3b0764',
|
||||||
|
},
|
||||||
|
pink: {
|
||||||
|
50: '#fdf2f8',
|
||||||
|
100: '#fce7f3',
|
||||||
|
200: '#fbcfe8',
|
||||||
|
300: '#f9a8d4',
|
||||||
|
400: '#f472b6',
|
||||||
|
500: '#ec4899',
|
||||||
|
600: '#db2777',
|
||||||
|
700: '#be185d',
|
||||||
|
800: '#9d174d',
|
||||||
|
900: '#831843',
|
||||||
|
950: '#500724',
|
||||||
|
},
|
||||||
|
} as const
|
||||||
Reference in New Issue
Block a user