Compare commits

...

3 Commits

Author SHA1 Message Date
Eric Bailey dee035cc17 Refactor button to allow style overrides 2024-12-06 14:22:17 -06:00
Eric Bailey 2a262ff18c Add contrast helper 2024-12-06 14:22:04 -06:00
Eric Bailey 4840ae0eb4 Allow for custom hues inside a theme provider 2024-12-06 14:20:57 -06:00
6 changed files with 308 additions and 272 deletions
+4
View File
@@ -148,6 +148,7 @@
"fast-text-encoding": "^1.0.6",
"history": "^5.3.0",
"hls.js": "^1.5.11",
"hsl-to-rgb-for-reals": "^1.1.1",
"js-sha256": "^0.9.0",
"jwt-decode": "^4.0.0",
"lande": "^1.0.10",
@@ -201,6 +202,7 @@
"tippy.js": "^6.3.7",
"tlds": "^1.234.0",
"tldts": "^6.1.46",
"wcag-contrast": "^3.0.0",
"zeego": "^1.6.2",
"zod": "^3.20.2"
},
@@ -219,6 +221,7 @@
"@react-native/typescript-config": "^0.74.1",
"@testing-library/jest-native": "^5.4.1",
"@testing-library/react-native": "^11.5.2",
"@types/hsl-to-rgb-for-reals": "^1.1.3",
"@types/jest": "^29.4.0",
"@types/lodash.chunk": "^4.2.7",
"@types/lodash.debounce": "^4.0.7",
@@ -228,6 +231,7 @@
"@types/react-dom": "^18.2.18",
"@types/react-responsive": "^8.0.5",
"@types/react-test-renderer": "^17.0.1",
"@types/wcag-contrast": "^3.0.3",
"@typescript-eslint/eslint-plugin": "^7.18.0",
"@typescript-eslint/parser": "^7.18.0",
"babel-jest": "^29.7.0",
+11 -6
View File
@@ -33,6 +33,7 @@ export type Alf = {
setFontScale: (fontScale: Exclude<Device['fontScale'], undefined>) => void
setFontFamily: (fontFamily: Device['fontFamily']) => void
}
setHue: (hue: number) => void
/**
* Feature flags or other gated options
*/
@@ -59,6 +60,7 @@ export const Context = React.createContext<Alf>({
setFontScale: () => {},
setFontFamily: () => {},
},
setHue: () => {},
flags: {},
})
@@ -94,20 +96,21 @@ export function ThemeProvider({
},
[setFontFamily],
)
const [hue, setHue] = React.useState(BLUE_HUE)
const themes = React.useMemo(() => {
return createThemes({
hues: {
primary: BLUE_HUE,
primary: hue,
negative: RED_HUE,
positive: GREEN_HUE,
},
})
}, [])
}, [hue])
const value = React.useMemo<Alf>(
() => ({
themes,
themeName: themeName,
themeName,
theme: themes[themeName],
fonts: {
scale: fontScale,
@@ -116,6 +119,7 @@ export function ThemeProvider({
setFontScale: setFontScaleAndPersist,
setFontFamily: setFontFamilyAndPersist,
},
setHue: setHue,
flags: {},
}),
[
@@ -126,6 +130,7 @@ export function ThemeProvider({
fontFamily,
setFontFamilyAndPersist,
fontScaleMultiplier,
setHue,
],
)
@@ -136,11 +141,11 @@ export function useAlf() {
return React.useContext(Context)
}
export function useTheme(theme?: ThemeName) {
export function useTheme(themeName?: ThemeName) {
const alf = useAlf()
return React.useMemo(() => {
return theme ? alf.themes[theme] : alf.theme
}, [theme, alf])
return themeName ? alf.themes[themeName] : alf.theme
}, [themeName, alf])
}
export function useBreakpoints() {
+1 -1
View File
@@ -60,7 +60,7 @@ export function createThemes({
dim: Theme
} {
const color = {
trueBlack: '#000000',
trueBlack: 'hsl(0, 0%, 0%)',
gray_0: `hsl(${hues.primary}, 20%, ${defaultScale[14]}%)`,
gray_25: `hsl(${hues.primary}, 20%, ${defaultScale[13]}%)`,
+35
View File
@@ -0,0 +1,35 @@
import {ColorValue} from 'react-native'
import hslToRgb from 'hsl-to-rgb-for-reals'
import {rgb} from 'wcag-contrast'
export function compareAndContrast(
compare: ColorValue | undefined,
contrast: ColorValue | undefined,
) {
let result = contrast
if (compare && contrast) {
const [, bgHue, bgSat, bgLuminance] =
compare.toString().match(/hsl\((\d+), (\d+)%, ([\d\.]+)%\)/) || []
const [, textHue, textSat, textLuminance] =
contrast.toString().match(/hsl\((\d+), (\d+)%, ([\d\.]+)%\)/) || []
const compareRgb = hslToRgb(
parseInt(bgHue),
parseInt(bgSat) / 100,
parseInt(bgLuminance) / 100,
)
const contrastRgb = hslToRgb(
parseInt(textHue),
parseInt(textSat) / 100,
parseInt(textLuminance) / 100,
)
const score = rgb(compareRgb, contrastRgb)
if (score < 3) {
result = `hsl(${textHue}, ${textSat}%, ${Math.abs(
parseInt(textLuminance) - 100,
)}%)`
}
}
return result
}
+223 -265
View File
@@ -15,6 +15,7 @@ import {
import {LinearGradient} from 'expo-linear-gradient'
import {atoms as a, flatten, select, tokens, useTheme} from '#/alf'
import {compareAndContrast} from '#/alf/util/contrast'
import {Props as SVGIconProps} from '#/components/icons/common'
import {Text} from '#/components/Typography'
@@ -120,7 +121,6 @@ export const Button = React.forwardRef<View, ButtonProps>(
},
ref,
) => {
const t = useTheme()
const [state, setState] = React.useState({
pressed: false,
hovered: false,
@@ -184,227 +184,6 @@ export const Button = React.forwardRef<View, ButtonProps>(
}))
}, [setState])
const {baseStyles, hoverStyles} = React.useMemo(() => {
const baseStyles: ViewStyle[] = []
const hoverStyles: ViewStyle[] = []
if (color === 'primary') {
if (variant === 'solid') {
if (!disabled) {
baseStyles.push({
backgroundColor: t.palette.primary_500,
})
hoverStyles.push({
backgroundColor: t.palette.primary_600,
})
} else {
baseStyles.push({
backgroundColor: select(t.name, {
light: t.palette.primary_700,
dim: t.palette.primary_300,
dark: t.palette.primary_300,
}),
})
}
} else if (variant === 'outline') {
baseStyles.push(a.border, t.atoms.bg, {
borderWidth: 1,
})
if (!disabled) {
baseStyles.push(a.border, {
borderColor: t.palette.primary_500,
})
hoverStyles.push(a.border, {
backgroundColor: t.palette.primary_50,
})
} else {
baseStyles.push(a.border, {
borderColor: t.palette.primary_200,
})
}
} else if (variant === 'ghost') {
if (!disabled) {
baseStyles.push(t.atoms.bg)
hoverStyles.push({
backgroundColor: t.palette.primary_100,
})
}
}
} else if (color === 'secondary') {
if (variant === 'solid') {
if (!disabled) {
baseStyles.push(t.atoms.bg_contrast_25)
hoverStyles.push(t.atoms.bg_contrast_50)
} else {
baseStyles.push(t.atoms.bg_contrast_100)
}
} else if (variant === 'outline') {
baseStyles.push(a.border, t.atoms.bg, {
borderWidth: 1,
})
if (!disabled) {
baseStyles.push(a.border, {
borderColor: t.palette.contrast_300,
})
hoverStyles.push(t.atoms.bg_contrast_50)
} else {
baseStyles.push(a.border, {
borderColor: t.palette.contrast_200,
})
}
} else if (variant === 'ghost') {
if (!disabled) {
baseStyles.push(t.atoms.bg)
hoverStyles.push({
backgroundColor: t.palette.contrast_25,
})
}
}
} else if (color === 'secondary_inverted') {
if (variant === 'solid') {
if (!disabled) {
baseStyles.push({
backgroundColor: t.palette.contrast_900,
})
hoverStyles.push({
backgroundColor: t.palette.contrast_950,
})
} else {
baseStyles.push({
backgroundColor: t.palette.contrast_600,
})
}
} else if (variant === 'outline') {
baseStyles.push(a.border, t.atoms.bg, {
borderWidth: 1,
})
if (!disabled) {
baseStyles.push(a.border, {
borderColor: t.palette.contrast_300,
})
hoverStyles.push(t.atoms.bg_contrast_50)
} else {
baseStyles.push(a.border, {
borderColor: t.palette.contrast_200,
})
}
} else if (variant === 'ghost') {
if (!disabled) {
baseStyles.push(t.atoms.bg)
hoverStyles.push({
backgroundColor: t.palette.contrast_25,
})
}
}
} else if (color === 'negative') {
if (variant === 'solid') {
if (!disabled) {
baseStyles.push({
backgroundColor: t.palette.negative_500,
})
hoverStyles.push({
backgroundColor: t.palette.negative_600,
})
} else {
baseStyles.push({
backgroundColor: select(t.name, {
light: t.palette.negative_700,
dim: t.palette.negative_300,
dark: t.palette.negative_300,
}),
})
}
} else if (variant === 'outline') {
baseStyles.push(a.border, t.atoms.bg, {
borderWidth: 1,
})
if (!disabled) {
baseStyles.push(a.border, {
borderColor: t.palette.negative_500,
})
hoverStyles.push(a.border, {
backgroundColor: t.palette.negative_50,
})
} else {
baseStyles.push(a.border, {
borderColor: t.palette.negative_200,
})
}
} else if (variant === 'ghost') {
if (!disabled) {
baseStyles.push(t.atoms.bg)
hoverStyles.push({
backgroundColor: t.palette.negative_100,
})
}
}
}
if (shape === 'default') {
if (size === 'large') {
baseStyles.push({
paddingVertical: 13,
paddingHorizontal: 20,
borderRadius: 8,
gap: 8,
})
} else if (size === 'small') {
baseStyles.push({
paddingVertical: 9,
paddingHorizontal: 12,
borderRadius: 6,
gap: 6,
})
} else if (size === 'tiny') {
baseStyles.push({
paddingVertical: 4,
paddingHorizontal: 8,
borderRadius: 4,
gap: 4,
})
}
} else if (shape === 'round' || shape === 'square') {
if (size === 'large') {
if (shape === 'round') {
baseStyles.push({height: 46, width: 46})
} else {
baseStyles.push({height: 44, width: 44})
}
} else if (size === 'small') {
if (shape === 'round') {
baseStyles.push({height: 34, width: 34})
} else {
baseStyles.push({height: 34, width: 34})
}
} else if (size === 'tiny') {
if (shape === 'round') {
baseStyles.push({height: 22, width: 22})
} else {
baseStyles.push({height: 21, width: 21})
}
}
if (shape === 'round') {
baseStyles.push(a.rounded_full)
} else if (shape === 'square') {
if (size === 'tiny') {
baseStyles.push(a.rounded_xs)
} else {
baseStyles.push(a.rounded_sm)
}
}
}
return {
baseStyles,
hoverStyles,
}
}, [t, variant, color, size, shape, disabled])
const {gradientColors, gradientHoverColors, gradientLocations} =
React.useMemo(() => {
const colors: string[] = []
@@ -436,17 +215,19 @@ export const Button = React.forwardRef<View, ButtonProps>(
gradientLocations: locations,
}
}, [variant, color])
const context = React.useMemo<ButtonContext>(
() => ({
...state,
variant,
color,
size,
shape,
disabled: disabled || false,
}),
[state, variant, color, size, disabled],
[state, variant, color, size, shape, disabled],
)
const {backgroundBase: baseStyles, textBase: hoverStyles} =
useButtonStyles(context)
const flattenedBaseStyles = flatten([baseStyles, style])
@@ -508,143 +289,319 @@ export const Button = React.forwardRef<View, ButtonProps>(
)
Button.displayName = 'Button'
export function useSharedButtonTextStyles() {
export function useButtonStyles({
color,
variant,
disabled,
size,
shape,
}: ButtonContext) {
const t = useTheme()
const {color, variant, disabled, size} = useButtonContext()
return React.useMemo(() => {
const baseStyles: TextStyle[] = []
const backgroundBase: ViewStyle[] = []
const backgroundHover: ViewStyle[] = []
const textBase: TextStyle[] = []
if (color === 'primary') {
if (variant === 'solid') {
if (!disabled) {
baseStyles.push({color: t.palette.white})
backgroundBase.push({
backgroundColor: t.palette.primary_500,
})
backgroundHover.push({
backgroundColor: t.palette.primary_600,
})
textBase.push({color: t.palette.white})
} else {
baseStyles.push({color: t.palette.white, opacity: 0.5})
backgroundBase.push({
backgroundColor: select(t.name, {
light: t.palette.primary_700,
dim: t.palette.primary_300,
dark: t.palette.primary_300,
}),
})
textBase.push({color: t.palette.white, opacity: 0.5})
}
} else if (variant === 'outline') {
backgroundBase.push(a.border, t.atoms.bg, {
borderWidth: 1,
})
if (!disabled) {
baseStyles.push({
backgroundBase.push(a.border, {
borderColor: t.palette.primary_500,
})
backgroundHover.push(a.border, {
backgroundColor: t.palette.primary_50,
})
textBase.push({
color: t.palette.primary_600,
})
} else {
baseStyles.push({color: t.palette.primary_600, opacity: 0.5})
backgroundBase.push(a.border, {
borderColor: t.palette.primary_200,
})
textBase.push({color: t.palette.primary_600, opacity: 0.5})
}
} else if (variant === 'ghost') {
if (!disabled) {
baseStyles.push({color: t.palette.primary_600})
backgroundBase.push(t.atoms.bg)
backgroundHover.push({
backgroundColor: t.palette.primary_100,
})
textBase.push({color: t.palette.primary_600})
} else {
baseStyles.push({color: t.palette.primary_600, opacity: 0.5})
textBase.push({color: t.palette.primary_600, opacity: 0.5})
}
}
} else if (color === 'secondary') {
if (variant === 'solid' || variant === 'gradient') {
if (variant === 'solid') {
if (!disabled) {
baseStyles.push({
backgroundBase.push(t.atoms.bg_contrast_25)
backgroundHover.push(t.atoms.bg_contrast_50)
textBase.push({
color: t.palette.contrast_700,
})
} else {
baseStyles.push({
backgroundBase.push(t.atoms.bg_contrast_100)
textBase.push({
color: t.palette.contrast_400,
})
}
} else if (variant === 'outline') {
backgroundBase.push(a.border, t.atoms.bg, {
borderWidth: 1,
})
if (!disabled) {
baseStyles.push({
backgroundBase.push(a.border, {
borderColor: t.palette.contrast_300,
})
backgroundHover.push(t.atoms.bg_contrast_50)
textBase.push({
color: t.palette.contrast_600,
})
} else {
baseStyles.push({
backgroundBase.push(a.border, {
borderColor: t.palette.contrast_200,
})
textBase.push({
color: t.palette.contrast_300,
})
}
} else if (variant === 'ghost') {
if (!disabled) {
baseStyles.push({
backgroundBase.push(t.atoms.bg)
backgroundHover.push({
backgroundColor: t.palette.contrast_25,
})
textBase.push({
color: t.palette.contrast_600,
})
} else {
baseStyles.push({
textBase.push({
color: t.palette.contrast_300,
})
}
}
} else if (color === 'secondary_inverted') {
if (variant === 'solid' || variant === 'gradient') {
if (variant === 'solid') {
if (!disabled) {
baseStyles.push({
backgroundBase.push({
backgroundColor: t.palette.contrast_900,
})
backgroundHover.push({
backgroundColor: t.palette.contrast_950,
})
textBase.push({
color: t.palette.contrast_100,
})
} else {
baseStyles.push({
backgroundBase.push({
backgroundColor: t.palette.contrast_600,
})
textBase.push({
color: t.palette.contrast_400,
})
}
} else if (variant === 'outline') {
backgroundBase.push(a.border, t.atoms.bg, {
borderWidth: 1,
})
if (!disabled) {
baseStyles.push({
backgroundBase.push(a.border, {
borderColor: t.palette.contrast_300,
})
backgroundHover.push(t.atoms.bg_contrast_50)
textBase.push({
color: t.palette.contrast_600,
})
} else {
baseStyles.push({
backgroundBase.push(a.border, {
borderColor: t.palette.contrast_200,
})
textBase.push({
color: t.palette.contrast_300,
})
}
} else if (variant === 'ghost') {
if (!disabled) {
baseStyles.push({
backgroundBase.push(t.atoms.bg)
backgroundHover.push({
backgroundColor: t.palette.contrast_25,
})
textBase.push({
color: t.palette.contrast_600,
})
} else {
baseStyles.push({
textBase.push({
color: t.palette.contrast_300,
})
}
}
} else if (color === 'negative') {
if (variant === 'solid' || variant === 'gradient') {
if (variant === 'solid') {
if (!disabled) {
baseStyles.push({color: t.palette.white})
backgroundBase.push({
backgroundColor: t.palette.negative_500,
})
backgroundHover.push({
backgroundColor: t.palette.negative_600,
})
textBase.push({color: t.palette.white})
} else {
baseStyles.push({color: t.palette.white, opacity: 0.5})
backgroundBase.push({
backgroundColor: select(t.name, {
light: t.palette.negative_700,
dim: t.palette.negative_300,
dark: t.palette.negative_300,
}),
})
textBase.push({color: t.palette.white, opacity: 0.5})
}
} else if (variant === 'outline') {
backgroundBase.push(a.border, t.atoms.bg, {
borderWidth: 1,
})
if (!disabled) {
baseStyles.push({color: t.palette.negative_400})
backgroundBase.push(a.border, {
borderColor: t.palette.negative_500,
})
backgroundHover.push(a.border, {
backgroundColor: t.palette.negative_50,
})
textBase.push({color: t.palette.negative_400})
} else {
baseStyles.push({color: t.palette.negative_400, opacity: 0.5})
backgroundBase.push(a.border, {
borderColor: t.palette.negative_200,
})
textBase.push({color: t.palette.negative_400, opacity: 0.5})
}
} else if (variant === 'ghost') {
if (!disabled) {
baseStyles.push({color: t.palette.negative_400})
backgroundBase.push(t.atoms.bg)
backgroundHover.push({
backgroundColor: t.palette.negative_100,
})
textBase.push({color: t.palette.negative_400})
} else {
baseStyles.push({color: t.palette.negative_400, opacity: 0.5})
textBase.push({color: t.palette.negative_400, opacity: 0.5})
}
}
} else {
if (!disabled) {
baseStyles.push({color: t.palette.white})
textBase.push({color: t.palette.white})
} else {
baseStyles.push({color: t.palette.white, opacity: 0.5})
textBase.push({color: t.palette.white, opacity: 0.5})
}
}
if (shape === 'default') {
if (size === 'large') {
backgroundBase.push({
paddingVertical: 13,
paddingHorizontal: 20,
borderRadius: 8,
gap: 8,
})
} else if (size === 'small') {
backgroundBase.push({
paddingVertical: 9,
paddingHorizontal: 12,
borderRadius: 6,
gap: 6,
})
} else if (size === 'tiny') {
backgroundBase.push({
paddingVertical: 4,
paddingHorizontal: 8,
borderRadius: 4,
gap: 4,
})
}
} else if (shape === 'round' || shape === 'square') {
if (size === 'large') {
if (shape === 'round') {
backgroundBase.push({height: 46, width: 46})
} else {
backgroundBase.push({height: 44, width: 44})
}
} else if (size === 'small') {
if (shape === 'round') {
backgroundBase.push({height: 34, width: 34})
} else {
backgroundBase.push({height: 34, width: 34})
}
} else if (size === 'tiny') {
if (shape === 'round') {
backgroundBase.push({height: 22, width: 22})
} else {
backgroundBase.push({height: 21, width: 21})
}
}
if (shape === 'round') {
backgroundBase.push(a.rounded_full)
} else if (shape === 'square') {
if (size === 'tiny') {
backgroundBase.push(a.rounded_xs)
} else {
backgroundBase.push(a.rounded_sm)
}
}
}
if (size === 'large') {
baseStyles.push(a.text_md, a.leading_tight)
textBase.push(a.text_md, a.leading_tight)
} else if (size === 'small') {
baseStyles.push(a.text_sm, a.leading_tight)
textBase.push(a.text_sm, a.leading_tight)
} else if (size === 'tiny') {
baseStyles.push(a.text_xs, a.leading_tight)
textBase.push(a.text_xs, a.leading_tight)
}
return StyleSheet.flatten(baseStyles)
}, [t, variant, color, size, disabled])
const styles = {
backgroundBase: StyleSheet.flatten(backgroundBase),
backgroundHover: StyleSheet.flatten(backgroundHover),
textBase: StyleSheet.flatten(textBase),
}
styles.textBase.color = compareAndContrast(
styles.backgroundBase.backgroundColor,
styles.textBase.color,
)
return styles
}, [t, variant, color, size, shape, disabled])
}
export function ButtonText({children, style, ...rest}: ButtonTextProps) {
const textStyles = useSharedButtonTextStyles()
const ctx = useButtonContext()
const {textBase} = useButtonStyles(ctx)
return (
<Text {...rest} style={[a.font_bold, a.text_center, textStyles, style]}>
<Text {...rest} style={[a.font_bold, a.text_center, textBase, style]}>
{children}
</Text>
)
@@ -660,7 +617,8 @@ export function ButtonIcon({
size?: SVGIconProps['size']
}) {
const {size: buttonSize, disabled} = useButtonContext()
const textStyles = useSharedButtonTextStyles()
const ctx = useButtonContext()
const {textBase} = useButtonStyles(ctx)
const {iconSize, iconContainerSize} = React.useMemo(() => {
/**
* Pre-set icon sizes for different button sizes
@@ -739,7 +697,7 @@ export function ButtonIcon({
width={iconSize}
style={[
{
color: textStyles.color,
color: textBase.color,
pointerEvents: 'none',
},
]}
+34
View File
@@ -6944,6 +6944,11 @@
resolved "https://registry.yarnpkg.com/@types/hammerjs/-/hammerjs-2.0.41.tgz#f6ecf57d1b12d2befcce00e928a6a097c22980aa"
integrity sha512-ewXv/ceBaJprikMcxCmWU1FKyMAQ2X7a9Gtmzw8fcg2kIePI1crERDM818W+XYrxqdBBOdlf2rm137bU+BltCA==
"@types/hsl-to-rgb-for-reals@^1.1.3":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@types/hsl-to-rgb-for-reals/-/hsl-to-rgb-for-reals-1.1.3.tgz#c42d18a4edb2e0fc24fcb3ae0123d4e64d301962"
integrity sha512-q8/VNwwpVhEJ3aXxZ2z0ce3P2IRZ60yS+rt9s8EQd9PFUPGcqYfK+1l6D1UpLcKrp5C2EBySCHJSGPNgIava4A==
"@types/html-minifier-terser@^6.0.0":
version "6.1.0"
resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35"
@@ -7204,6 +7209,11 @@
resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz#60be8d21baab8c305132eb9cb912ed497852aadc"
integrity sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==
"@types/wcag-contrast@^3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@types/wcag-contrast/-/wcag-contrast-3.0.3.tgz#0b0339ab210b859eeee555b3fef58d293216905c"
integrity sha512-oprevfwJSLfpQK4KaWsRKJuNoebV76+xhmbXiWJGy+FkS34LpCgCMNIwRXWTb8xmmSxUE2ycFOYE7uyRVRm3LA==
"@types/ws@^8.5.5":
version "8.5.5"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.5.tgz#af587964aa06682702ee6dcbc7be41a80e4b28eb"
@@ -10314,6 +10324,11 @@ eslint@^8.19.0:
strip-ansi "^6.0.1"
text-table "^0.2.0"
esm@^3.0.84:
version "3.2.25"
resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10"
integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==
espree@^9.6.0, espree@^9.6.1:
version "9.6.1"
resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f"
@@ -11752,6 +11767,11 @@ hpack.js@^2.1.6:
readable-stream "^2.0.1"
wbuf "^1.1.0"
hsl-to-rgb-for-reals@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/hsl-to-rgb-for-reals/-/hsl-to-rgb-for-reals-1.1.1.tgz#e1eb23f6b78016e3722431df68197e6dcdc016d9"
integrity sha512-LgOWAkrN0rFaQpfdWBQlv/VhkOxb5AsBjk6NQVx4yEzWS923T07X0M1Y0VNko2H52HeSpZrZNNMJ0aFqsdVzQg==
html-encoding-sniffer@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9"
@@ -16655,6 +16675,13 @@ relateurl@^0.2.7:
resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==
relative-luminance@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/relative-luminance/-/relative-luminance-2.0.1.tgz#2babddf3a5a59673227d6f02e0f68e13989e3d13"
integrity sha512-wFuITNthJilFPwkK7gNJcULxXBcfFZvZORsvdvxeOdO44wCeZnuQkf3nFFzOR/dpJNxYsdRZJLsepWbyKhnMww==
dependencies:
esm "^3.0.84"
remove-trailing-slash@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/remove-trailing-slash/-/remove-trailing-slash-0.1.1.tgz#be2285a59f39c74d1bce4f825950061915e3780d"
@@ -18747,6 +18774,13 @@ wbuf@^1.1.0, wbuf@^1.7.3:
dependencies:
minimalistic-assert "^1.0.0"
wcag-contrast@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/wcag-contrast/-/wcag-contrast-3.0.0.tgz#51c2df43f15162993006454b3362844205889efb"
integrity sha512-RWbpg/S7FOXDCwqC2oFhN/vh8dHzj0OS6dpyOSDHyQFSmqmR+lAUStV/ziTT1GzDqL9wol+nZQB4vCi5yEak+w==
dependencies:
relative-luminance "^2.0.0"
wcwidth@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"