Skip to content

Commit

Permalink
chore: replace tinycolor with color2k (chakra-ui#7019)
Browse files Browse the repository at this point in the history
  • Loading branch information
anubra266 authored Nov 23, 2022
1 parent 9e3d91f commit c9f976d
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 30 deletions.
5 changes: 5 additions & 0 deletions .changeset/fifty-socks-arrive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@chakra-ui/theme-tools": patch
---

Replace tinycolor package with color2k for smaller bundle size
8 changes: 4 additions & 4 deletions packages/components/theme-tools/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,17 @@
},
"dependencies": {
"@chakra-ui/anatomy": "workspace:*",
"@ctrl/tinycolor": "^3.4.0"
"color2k": "^2.0.0"
},
"peerDependencies": {
"@chakra-ui/styled-system": ">=2.0.0"
},
"devDependencies": {
"dlv": "^1.1.3",
"@types/dlv": "^1.1.2",
"@chakra-ui/shared-utils": "workspace:*",
"@chakra-ui/styled-system": "workspace:*",
"clean-package": "2.1.1"
"@types/dlv": "^1.1.2",
"clean-package": "2.1.1",
"dlv": "^1.1.3"
},
"scripts": {
"build": "tsup src/index.ts --dts",
Expand Down
99 changes: 80 additions & 19 deletions packages/components/theme-tools/src/color.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import {
TinyColor,
readability,
isReadable,
random,
WCAG2Parms,
} from "@ctrl/tinycolor"
toHex,
parseToRgba,
transparentize as setTransparency,
mix,
darken as reduceLightness,
lighten as increaseLightness,
getContrast,
parseToHsla,
hsla,
getLuminance,
} from "color2k"

import get from "dlv"

type Dict = { [key: string]: any }
Expand All @@ -20,8 +26,19 @@ const isEmptyObject = (obj: any) => Object.keys(obj).length === 0
*/
export const getColor = (theme: Dict, color: string, fallback?: string) => {
const hex = get(theme, `colors.${color}`, color)
const { isValid } = new TinyColor(hex)
return isValid ? hex : fallback
try {
toHex(hex)
return hex
} catch {
// returning black to stay consistent with TinyColor behaviour so as to prevent breaking change
return fallback ?? "#000000"
}
}

const getBrightness = (color: string) => {
const [r, g, b] = parseToRgba(color)
// http://www.w3.org/TR/AERT#color-contrast
return (r * 299 + g * 587 + b * 114) / 1000
}

/**
Expand All @@ -32,7 +49,8 @@ export const getColor = (theme: Dict, color: string, fallback?: string) => {
*/
export const tone = (color: string) => (theme: Dict) => {
const hex = getColor(theme, color)
const isDark = new TinyColor(hex).isDark()
const brightness = getBrightness(hex)
const isDark = brightness < 128
return isDark ? "dark" : "light"
}

Expand Down Expand Up @@ -64,7 +82,7 @@ export const isLight = (color: string) => (theme: Dict) =>
export const transparentize =
(color: string, opacity: number) => (theme: Dict) => {
const raw = getColor(theme, color)
return new TinyColor(raw).setAlpha(opacity).toRgbString()
return setTransparency(raw, 1 - opacity)
}

/**
Expand All @@ -76,7 +94,7 @@ export const transparentize =
*/
export const whiten = (color: string, amount: number) => (theme: Dict) => {
const raw = getColor(theme, color)
return new TinyColor(raw).mix("#fff", amount).toHexString()
return toHex(mix(raw, "#fff", amount))
}

/**
Expand All @@ -88,7 +106,7 @@ export const whiten = (color: string, amount: number) => (theme: Dict) => {
*/
export const blacken = (color: string, amount: number) => (theme: Dict) => {
const raw = getColor(theme, color)
return new TinyColor(raw).mix("#000", amount).toHexString()
return toHex(mix(raw, "#000", amount))
}

/**
Expand All @@ -100,7 +118,7 @@ export const blacken = (color: string, amount: number) => (theme: Dict) => {
*/
export const darken = (color: string, amount: number) => (theme: Dict) => {
const raw = getColor(theme, color)
return new TinyColor(raw).darken(amount).toHexString()
return toHex(reduceLightness(raw, amount))
}

/**
Expand All @@ -110,8 +128,10 @@ export const darken = (color: string, amount: number) => (theme: Dict) => {
*
* @deprecated This will be removed in the next major release.
*/
export const lighten = (color: string, amount: number) => (theme: Dict) =>
new TinyColor(getColor(theme, color)).lighten(amount).toHexString()
export const lighten = (color: string, amount: number) => (theme: Dict) => {
const raw = getColor(theme, color)
toHex(increaseLightness(raw, amount))
}

/**
* Checks the contract ratio of between 2 colors,
Expand All @@ -123,7 +143,12 @@ export const lighten = (color: string, amount: number) => (theme: Dict) =>
* @deprecated This will be removed in the next major release.
*/
export const contrast = (fg: string, bg: string) => (theme: Dict) =>
readability(getColor(theme, bg), getColor(theme, fg))
getContrast(getColor(theme, bg), getColor(theme, fg))

interface WCAG2Parms {
level?: "AA" | "AAA"
size?: "large" | "small"
}

/**
* Checks if a color meets the Web Content Accessibility
Expand All @@ -139,12 +164,43 @@ export const isAccessible =
(textColor: string, bgColor: string, options?: WCAG2Parms) => (theme: Dict) =>
isReadable(getColor(theme, bgColor), getColor(theme, textColor), options)

export function isReadable(
color1: string,
color2: string,
wcag2: WCAG2Parms = { level: "AA", size: "small" },
): boolean {
const readabilityLevel = readability(color1, color2)
switch ((wcag2.level ?? "AA") + (wcag2.size ?? "small")) {
case "AAsmall":
case "AAAlarge":
return readabilityLevel >= 4.5
case "AAlarge":
return readabilityLevel >= 3
case "AAAsmall":
return readabilityLevel >= 7
default:
return false
}
}

export function readability(color1: string, color2: string): number {
return (
(Math.max(getLuminance(color1), getLuminance(color2)) + 0.05) /
(Math.min(getLuminance(color1), getLuminance(color2)) + 0.05)
)
}
/**
*
* @deprecated This will be removed in the next major release.
*/
export const complementary = (color: string) => (theme: Dict) =>
new TinyColor(getColor(theme, color)).complement().toHexString()
export const complementary = (color: string) => (theme: Dict) => {
const raw = getColor(theme, color)
const hsl = parseToHsla(raw)
const complementHsl: [number, number, number, number] = Object.assign(hsl, [
(hsl[0] + 180) % 360,
])
return toHex(hsla(...complementHsl))
}

export function generateStripe(
size = "1rem",
Expand Down Expand Up @@ -177,8 +233,13 @@ interface RandomColorOptions {
colors?: string[]
}

const randomHex = () =>
`#${Math.floor(Math.random() * 0xffffff)
.toString(16)
.padEnd(6, "0")}`

export function randomColor(opts?: RandomColorOptions) {
const fallback = random().toHexString()
const fallback = randomHex()

if (!opts || isEmptyObject(opts)) {
return fallback
Expand Down
13 changes: 6 additions & 7 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit c9f976d

Please sign in to comment.