Use CSS variables for sprite images

This commit is contained in:
Jordan Eldredge 2020-10-01 19:39:37 -07:00
parent 90bc65830c
commit b067ac5cb4
12 changed files with 729 additions and 414 deletions

File diff suppressed because one or more lines are too long

View file

@ -23,6 +23,36 @@
outline: 0;
}
#webamp [style*="--active-background"]:active {
background-image: var(--active-background);
}
#webamp [style*="--base-background"] {
background-image: var(--base-background);
}
#webamp [style*="--base-background"] {
background-image: var(--base-background);
}
/**
* Ideally we would collapse these next four down into two rules that each have
* multiple comma separated selectors. However, `::-moz-range-thumb`
* will break the rule in non-Firefox browsers, so we just make each
* selector it's own rule.
*/
#webamp [style*="--thumb-background"]::-webkit-slider-thumb {
background-image: var(--thumb-background);
}
#webamp [style*="--thumb-background"]::-moz-range-thumb {
background-image: var(--thumb-background);
}
#webamp [style*="--active-thumb-background"]:active::-webkit-slider-thumb {
background-image: var(--active-thumb-background);
}
#webamp [style*="--active-thumb-background"]:active::-moz-range-thumb {
background-image: var(--active-thumb-background);
}
/* Range input css reset */
#webamp input[type="range"] {
-webkit-appearance: none;

View file

@ -1,11 +1,22 @@
import React from "react";
import * as Actions from "../../actionCreators";
import { useActionCreator } from "../../hooks";
import { useActionCreator, useSprite } from "../../hooks";
const Eject = React.memo(() => {
const openMediaFileDialog = useActionCreator(Actions.openMediaFileDialog);
return <div id="eject" onClick={openMediaFileDialog} title="Open File(s)" />;
const spriteStyle = useSprite({
base: "MAIN_EJECT_BUTTON",
size: "MAIN_EJECT_BUTTON",
active: "MAIN_EJECT_BUTTON_ACTIVE",
});
return (
<div
style={{ ...spriteStyle, position: "absolute", top: 89, left: 136 }}
onClick={openMediaFileDialog}
title="Open File(s)"
/>
);
});
export default Eject;

View file

@ -2,7 +2,7 @@ import React from "react";
import Balance from "../Balance";
import * as Selectors from "../../selectors";
import { useTypedSelector } from "../../hooks";
import { useSprite, useTypedSelector } from "../../hooks";
export const offsetFromBalance = (balance: number): number => {
const percent = Math.abs(balance) / 100;
@ -13,10 +13,23 @@ export const offsetFromBalance = (balance: number): number => {
const MainBalance = React.memo(() => {
const balance = useTypedSelector(Selectors.getBalance);
const spriteStyle = useSprite({
base: "MAIN_BALANCE_BACKGROUND",
thumb: "MAIN_BALANCE_THUMB",
activeThumb: "MAIN_BALANCE_THUMB_ACTIVE",
});
return (
<Balance
id="balance"
style={{ backgroundPosition: `0 -${offsetFromBalance(balance)}px` }}
style={{
...spriteStyle,
left: 177,
top: 57,
height: 13,
width: 38,
position: "absolute",
backgroundPosition: `0 -${offsetFromBalance(balance)}px`,
}}
/>
);
});

View file

@ -15,7 +15,7 @@ const MainVolume = React.memo(() => {
};
return (
<div id="volume" style={style}>
<Volume />
<Volume style={{ background: "none" }} />
</div>
);
});

View file

@ -33,7 +33,7 @@ import * as Selectors from "../../selectors";
import "../../../css/main-window.css";
import { FilePicker } from "../../types";
import FocusTarget from "../FocusTarget";
import { useActionCreator, useTypedSelector } from "../../hooks";
import { useActionCreator, useSprite, useTypedSelector } from "../../hooks";
interface Props {
analyser: AnalyserNode;
@ -52,6 +52,7 @@ const MainWindow = React.memo(({ analyser, filePickers }: Props) => {
const doubled = useTypedSelector(Selectors.getDoubled);
const llama = useTypedSelector(Selectors.getLlamaMode);
const working = useTypedSelector(Selectors.getWorking);
const style = useSprite({ base: "MAIN_WINDOW_BACKGROUND" });
const className = classnames({
window: true,
@ -74,6 +75,7 @@ const MainWindow = React.memo(({ analyser, filePickers }: Props) => {
return (
<DropTarget
style={style}
id="main-window"
windowId={WINDOWS.MAIN}
className={className}

View file

@ -9,6 +9,7 @@ import { SkinImages } from "../types";
import { createSelector } from "reselect";
import Css from "./Css";
import ClipPaths from "./ClipPaths";
import { imageVarName, cursorVarName } from "../utils";
const CSS_PREFIX = "#webamp";
@ -34,28 +35,37 @@ const FALLBACKS: { [key: string]: string } = {
MAIN_BALANCE_THUMB_ACTIVE: "MAIN_VOLUME_THUMB_SELECTED",
};
const getCssRules = createSelector(
const getCssVars = createSelector(
Selectors.getSkinImages,
Selectors.getSkinCursors,
Selectors.getSkinLetterWidths,
Selectors.getSkinRegion,
(skinImages, skinCursors, skinGenLetterWidths, skinRegion): string | null => {
(skinImages, skinCursors): string | null => {
if (!skinImages || !skinCursors) {
return null;
}
const cssRules = [];
Object.keys(imageSelectors).forEach((imageName) => {
const imageUrl =
skinImages[imageName] || skinImages[FALLBACKS[imageName]];
if (imageUrl) {
imageSelectors[imageName].forEach((selector) => {
cssRules.push(
`${CSS_PREFIX} ${selector} {background-image: url(${imageUrl})}`
);
});
}
});
const variableRules = [
...Object.entries(skinImages).map(([imageName, value]) => [
imageVarName(imageName),
value || skinImages[FALLBACKS[imageName]],
]),
...Object.entries(skinCursors).map(([imageName, value]) => [
cursorVarName(imageName),
value,
]),
];
const rules = variableRules
.filter(([, image]) => image != null)
.map(([name, image]) => `${name}: url(${image})`)
.join(";\n ");
return `${CSS_PREFIX} {\n ${rules}\n}`;
}
);
const getLetterWidths = createSelector(
Selectors.getSkinLetterWidths,
(skinGenLetterWidths) => {
const cssRules: string[] = [];
if (skinGenLetterWidths != null) {
LETTERS.forEach((letter) => {
const width = skinGenLetterWidths[`GEN_TEXT_${letter}`];
@ -69,21 +79,43 @@ const getCssRules = createSelector(
);
});
}
return cssRules.join("\n");
}
);
const getCssRules = createSelector(
Selectors.getSkinImages,
Selectors.getSkinRegion,
(skinImages, skinRegion): string => {
const cssRules = [];
// DEPRECATED: All of these should be replaced with calls to `useSprite` in
// the component that actually renders the element.
Object.entries(imageSelectors).forEach(([imageName, selectors]) => {
selectors.forEach((selector) => {
// Ideally we would collapse this down into a single rule that has
// multipel comma separated selectors. However, `::-moz-range-thumb`
// will break the rule in non-Firefox browsers, so we just make each
// selector it's own rule.
cssRules.push(
`${CSS_PREFIX} ${selector} {\n background-image: var(${imageVarName(
imageName
)});\n}`
);
});
});
Object.keys(cursorSelectors).forEach((cursorName) => {
const cursorUrl = skinCursors[cursorName];
if (cursorUrl) {
cursorSelectors[cursorName].forEach((selector) => {
cssRules.push(
`${
// TODO: Fix this hack
// Maybe our CSS name spacing should be based on some other class/id
// than the one we use for defining the main div.
// That way it could be shared by both the player and the context menu.
selector.startsWith("#webamp-context-menu") ? "" : CSS_PREFIX
} ${selector} {cursor: url(${cursorUrl}), auto}`
);
});
}
cursorSelectors[cursorName].forEach((selector) => {
cssRules.push(
`${
// TODO: Fix this hack
// Maybe our CSS name spacing should be based on some other class/id
// than the one we use for defining the main div.
// That way it could be shared by both the player and the context menu.
selector.startsWith("#webamp-context-menu") ? "" : CSS_PREFIX
} ${selector} {cursor: var(${cursorVarName(cursorName)}), auto}`
);
});
});
if (numExIsUsed(skinImages)) {
@ -119,13 +151,15 @@ const getClipPaths = createSelector(Selectors.getSkinRegion, (skinRegion) => {
export default function Skin() {
const cssRules = useTypedSelector(getCssRules);
const cssVars = useTypedSelector(getCssVars);
const letterWidths = useTypedSelector(getLetterWidths);
const clipPaths = useTypedSelector(getClipPaths);
if (cssRules == null) {
return null;
}
return (
<>
<Css id="webamp-skin">{cssRules}</Css>
<Css id="webamp-style">{cssRules}</Css>
{cssVars != null && (
<Css id="webamp-skin">{`${cssVars} ${letterWidths}`}</Css>
)}
<ClipPaths>{clipPaths}</ClipPaths>
</>
);

View file

@ -5,6 +5,7 @@ import {
useLayoutEffect,
useRef,
} from "react";
import { spritesByName, SpriteName } from "./skinSprites";
import { useDispatch, useSelector } from "react-redux";
import * as Utils from "./utils";
import { Action, Thunk, AppState } from "./types";
@ -171,3 +172,34 @@ export function useActionCreator<T extends (...args: any[]) => Action | Thunk>(
export function useTypedDispatch(): (action: Action | Thunk) => void {
return useDispatch();
}
type SpriteConfig = {
base?: SpriteName;
active?: SpriteName;
thumb?: SpriteName;
activeThumb?: SpriteName;
size?: SpriteName;
};
export function useSprite(options: SpriteConfig): React.CSSProperties {
const style: React.CSSProperties = {};
(["base", "active", "thumb", "activeThumb"] as const).forEach((name) => {
const spriteName = options[name];
if (spriteName != null) {
// Ideally we could use `{backgroundImage: "var(varName)"}` for `base`.
// Sadly, that overrides any `:active` styles. So instead we just use
// this CSS variable hack for all sprite backgrounds.
// @ts-ignore
style[`--${name}-background`] = `var(${Utils.imageVarName(spriteName)})`;
}
});
if (options.size != null) {
const sprite = spritesByName[options.size];
if (sprite == null) {
throw new Error(`Could not find sprite style for "${options.size}"`);
}
style.height = sprite.height;
style.width = sprite.width;
}
return style;
}

View file

@ -25,9 +25,6 @@ export const imageSelectors: Selectors = {
MAIN_STOP_BUTTON_ACTIVE: [".actions #stop:active"],
MAIN_NEXT_BUTTON: [".actions #next"],
MAIN_NEXT_BUTTON_ACTIVE: [".actions #next:active"],
MAIN_EJECT_BUTTON: ["#eject"],
MAIN_EJECT_BUTTON_ACTIVE: ["#eject:active"],
MAIN_WINDOW_BACKGROUND: ["#main-window"],
MAIN_STEREO: [".media-info #stereo", ".stop .media-info #stereo.selected"],
MAIN_STEREO_SELECTED: [".media-info #stereo.selected"],
MAIN_MONO: [".media-info #mono", ".stop .media-info #mono.selected"],

View file

@ -1,6 +1,215 @@
import { UTF8_ELLIPSIS } from "./constants";
export type SpriteName = string;
export type SpriteName =
| "MAIN_BALANCE_BACKGROUND"
| "MAIN_BALANCE_THUMB"
| "MAIN_BALANCE_THUMB_ACTIVE"
| "MAIN_PREVIOUS_BUTTON"
| "MAIN_PREVIOUS_BUTTON_ACTIVE"
| "MAIN_PLAY_BUTTON"
| "MAIN_PLAY_BUTTON_ACTIVE"
| "MAIN_PAUSE_BUTTON"
| "MAIN_PAUSE_BUTTON_ACTIVE"
| "MAIN_STOP_BUTTON"
| "MAIN_STOP_BUTTON_ACTIVE"
| "MAIN_NEXT_BUTTON"
| "MAIN_NEXT_BUTTON_ACTIVE"
| "MAIN_EJECT_BUTTON"
| "MAIN_EJECT_BUTTON_ACTIVE"
| "MAIN_WINDOW_BACKGROUND"
| "MAIN_STEREO"
| "MAIN_STEREO_SELECTED"
| "MAIN_MONO"
| "MAIN_MONO_SELECTED"
| "NO_MINUS_SIGN"
| "MINUS_SIGN"
| "DIGIT_0"
| "DIGIT_1"
| "DIGIT_2"
| "DIGIT_3"
| "DIGIT_4"
| "DIGIT_5"
| "DIGIT_6"
| "DIGIT_7"
| "DIGIT_8"
| "DIGIT_9"
| "NO_MINUS_SIGN_EX"
| "MINUS_SIGN_EX"
| "DIGIT_0_EX"
| "DIGIT_1_EX"
| "DIGIT_2_EX"
| "DIGIT_3_EX"
| "DIGIT_4_EX"
| "DIGIT_5_EX"
| "DIGIT_6_EX"
| "DIGIT_7_EX"
| "DIGIT_8_EX"
| "DIGIT_9_EX"
| "MAIN_PLAYING_INDICATOR"
| "MAIN_PAUSED_INDICATOR"
| "MAIN_STOPPED_INDICATOR"
| "MAIN_NOT_WORKING_INDICATOR"
| "MAIN_WORKING_INDICATOR"
| "PLAYLIST_TOP_TILE"
| "PLAYLIST_TOP_LEFT_CORNER"
| "PLAYLIST_TITLE_BAR"
| "PLAYLIST_TOP_RIGHT_CORNER"
| "PLAYLIST_TOP_TILE_SELECTED"
| "PLAYLIST_TOP_LEFT_SELECTED"
| "PLAYLIST_TITLE_BAR_SELECTED"
| "PLAYLIST_TOP_RIGHT_CORNER_SELECTED"
| "PLAYLIST_LEFT_TILE"
| "PLAYLIST_RIGHT_TILE"
| "PLAYLIST_BOTTOM_TILE"
| "PLAYLIST_BOTTOM_LEFT_CORNER"
| "PLAYLIST_BOTTOM_RIGHT_CORNER"
| "PLAYLIST_VISUALIZER_BACKGROUND"
| "PLAYLIST_SHADE_BACKGROUND"
| "PLAYLIST_SHADE_BACKGROUND_LEFT"
| "PLAYLIST_SHADE_BACKGROUND_RIGHT"
| "PLAYLIST_SHADE_BACKGROUND_RIGHT_SELECTED"
| "PLAYLIST_SCROLL_HANDLE_SELECTED"
| "PLAYLIST_SCROLL_HANDLE"
| "PLAYLIST_ADD_URL"
| "PLAYLIST_ADD_URL_SELECTED"
| "PLAYLIST_ADD_DIR"
| "PLAYLIST_ADD_DIR_SELECTED"
| "PLAYLIST_ADD_FILE"
| "PLAYLIST_ADD_FILE_SELECTED"
| "PLAYLIST_REMOVE_ALL"
| "PLAYLIST_REMOVE_ALL_SELECTED"
| "PLAYLIST_CROP"
| "PLAYLIST_CROP_SELECTED"
| "PLAYLIST_REMOVE_SELECTED"
| "PLAYLIST_REMOVE_SELECTED_SELECTED"
| "PLAYLIST_REMOVE_MISC"
| "PLAYLIST_REMOVE_MISC_SELECTED"
| "PLAYLIST_INVERT_SELECTION"
| "PLAYLIST_INVERT_SELECTION_SELECTED"
| "PLAYLIST_SELECT_ZERO"
| "PLAYLIST_SELECT_ZERO_SELECTED"
| "PLAYLIST_SELECT_ALL"
| "PLAYLIST_SELECT_ALL_SELECTED"
| "PLAYLIST_SORT_LIST"
| "PLAYLIST_SORT_LIST_SELECTED"
| "PLAYLIST_FILE_INFO"
| "PLAYLIST_FILE_INFO_SELECTED"
| "PLAYLIST_MISC_OPTIONS"
| "PLAYLIST_MISC_OPTIONS_SELECTED"
| "PLAYLIST_NEW_LIST"
| "PLAYLIST_NEW_LIST_SELECTED"
| "PLAYLIST_SAVE_LIST"
| "PLAYLIST_SAVE_LIST_SELECTED"
| "PLAYLIST_LOAD_LIST"
| "PLAYLIST_LOAD_LIST_SELECTED"
| "PLAYLIST_ADD_MENU_BAR"
| "PLAYLIST_REMOVE_MENU_BAR"
| "PLAYLIST_SELECT_MENU_BAR"
| "PLAYLIST_MISC_MENU_BAR"
| "PLAYLIST_LIST_BAR"
| "PLAYLIST_CLOSE_SELECTED"
| "PLAYLIST_COLLAPSE_SELECTED"
| "PLAYLIST_EXPAND_SELECTED"
| "EQ_SHADE_BACKGROUND_SELECTED"
| "EQ_SHADE_BACKGROUND"
| "EQ_SHADE_VOLUME_SLIDER_LEFT"
| "EQ_SHADE_VOLUME_SLIDER_CENTER"
| "EQ_SHADE_VOLUME_SLIDER_RIGHT"
| "EQ_SHADE_BALANCE_SLIDER_LEFT"
| "EQ_SHADE_BALANCE_SLIDER_CENTER"
| "EQ_SHADE_BALANCE_SLIDER_RIGHT"
| "EQ_MAXIMIZE_BUTTON_ACTIVE"
| "EQ_MINIMIZE_BUTTON_ACTIVE"
| "EQ_CLOSE_BUTTON"
| "EQ_CLOSE_BUTTON_ACTIVE"
| "EQ_WINDOW_BACKGROUND"
| "EQ_TITLE_BAR"
| "EQ_TITLE_BAR_SELECTED"
| "EQ_SLIDER_BACKGROUND"
| "EQ_SLIDER_THUMB"
| "EQ_SLIDER_THUMB_SELECTED"
| "EQ_ON_BUTTON"
| "EQ_ON_BUTTON_DEPRESSED"
| "EQ_ON_BUTTON_SELECTED"
| "EQ_ON_BUTTON_SELECTED_DEPRESSED"
| "EQ_AUTO_BUTTON"
| "EQ_AUTO_BUTTON_DEPRESSED"
| "EQ_AUTO_BUTTON_SELECTED"
| "EQ_AUTO_BUTTON_SELECTED_DEPRESSED"
| "EQ_GRAPH_BACKGROUND"
| "EQ_GRAPH_LINE_COLORS"
| "EQ_PRESETS_BUTTON"
| "EQ_PRESETS_BUTTON_SELECTED"
| "EQ_PREAMP_LINE"
| "MAIN_POSITION_SLIDER_BACKGROUND"
| "MAIN_POSITION_SLIDER_THUMB"
| "MAIN_POSITION_SLIDER_THUMB_SELECTED"
| "MAIN_SHUFFLE_BUTTON"
| "MAIN_SHUFFLE_BUTTON_DEPRESSED"
| "MAIN_SHUFFLE_BUTTON_SELECTED"
| "MAIN_SHUFFLE_BUTTON_SELECTED_DEPRESSED"
| "MAIN_REPEAT_BUTTON"
| "MAIN_REPEAT_BUTTON_DEPRESSED"
| "MAIN_REPEAT_BUTTON_SELECTED"
| "MAIN_REPEAT_BUTTON_SELECTED_DEPRESSED"
| "MAIN_EQ_BUTTON"
| "MAIN_EQ_BUTTON_SELECTED"
| "MAIN_EQ_BUTTON_DEPRESSED"
| "MAIN_EQ_BUTTON_DEPRESSED_SELECTED"
| "MAIN_PLAYLIST_BUTTON"
| "MAIN_PLAYLIST_BUTTON_SELECTED"
| "MAIN_PLAYLIST_BUTTON_DEPRESSED"
| "MAIN_PLAYLIST_BUTTON_DEPRESSED_SELECTED"
| "MAIN_TITLE_BAR"
| "MAIN_TITLE_BAR_SELECTED"
| "MAIN_EASTER_EGG_TITLE_BAR"
| "MAIN_EASTER_EGG_TITLE_BAR_SELECTED"
| "MAIN_OPTIONS_BUTTON"
| "MAIN_OPTIONS_BUTTON_DEPRESSED"
| "MAIN_MINIMIZE_BUTTON"
| "MAIN_MINIMIZE_BUTTON_DEPRESSED"
| "MAIN_SHADE_BUTTON"
| "MAIN_SHADE_BUTTON_DEPRESSED"
| "MAIN_CLOSE_BUTTON"
| "MAIN_CLOSE_BUTTON_DEPRESSED"
| "MAIN_CLUTTER_BAR_BACKGROUND"
| "MAIN_CLUTTER_BAR_BACKGROUND_DISABLED"
| "MAIN_CLUTTER_BAR_BUTTON_O_SELECTED"
| "MAIN_CLUTTER_BAR_BUTTON_A_SELECTED"
| "MAIN_CLUTTER_BAR_BUTTON_I_SELECTED"
| "MAIN_CLUTTER_BAR_BUTTON_D_SELECTED"
| "MAIN_CLUTTER_BAR_BUTTON_V_SELECTED"
| "MAIN_SHADE_BACKGROUND"
| "MAIN_SHADE_BACKGROUND_SELECTED"
| "MAIN_SHADE_BUTTON_SELECTED"
| "MAIN_SHADE_BUTTON_SELECTED_DEPRESSED"
| "MAIN_SHADE_POSITION_BACKGROUND"
| "MAIN_SHADE_POSITION_THUMB"
| "MAIN_SHADE_POSITION_THUMB_LEFT"
| "MAIN_SHADE_POSITION_THUMB_RIGHT"
| "MAIN_VOLUME_BACKGROUND"
| "MAIN_VOLUME_THUMB"
| "MAIN_VOLUME_THUMB_SELECTED"
| "GEN_TOP_LEFT_SELECTED"
| "GEN_TOP_LEFT_END_SELECTED"
| "GEN_TOP_CENTER_FILL_SELECTED"
| "GEN_TOP_RIGHT_END_SELECTED"
| "GEN_TOP_LEFT_RIGHT_FILL_SELECTED"
| "GEN_TOP_RIGHT_SELECTED"
| "GEN_TOP_LEFT"
| "GEN_TOP_LEFT_END"
| "GEN_TOP_CENTER_FILL"
| "GEN_TOP_RIGHT_END"
| "GEN_TOP_LEFT_RIGHT_FILL"
| "GEN_TOP_RIGHT"
| "GEN_BOTTOM_LEFT"
| "GEN_BOTTOM_RIGHT"
| "GEN_BOTTOM_FILL"
| "GEN_MIDDLE_LEFT"
| "GEN_MIDDLE_LEFT_BOTTOM"
| "GEN_MIDDLE_RIGHT"
| "GEN_MIDDLE_RIGHT_BOTTOM"
| "GEN_CLOSE_SELECTED";
export interface Sprite {
name: SpriteName;
@ -83,7 +292,8 @@ export const FONT_LOOKUP: { [letter: string]: [number, number] } = {
"}": [1, 23],
};
export const imageConstFromChar = (char: string) =>
export const imageConstFromChar = (char: string): SpriteName =>
// @ts-ignore
`CHARACTER_${char.charCodeAt(0)}`;
const CHAR_X = 5;
@ -128,7 +338,14 @@ const sprites: SpriteMap = {
{ name: "MAIN_PAUSE_BUTTON_ACTIVE", x: 46, y: 18, width: 23, height: 18 },
{ name: "MAIN_STOP_BUTTON", x: 69, y: 0, width: 23, height: 18 },
{ name: "MAIN_STOP_BUTTON_ACTIVE", x: 69, y: 18, width: 23, height: 18 },
{ name: "MAIN_NEXT_BUTTON", x: 92, y: 0, width: 23, height: 18 },
{
name: "MAIN_NEXT_BUTTON",
x: 92,
y: 0,
// The actual button is 22. What gives here.
width: 22,
height: 18,
},
{ name: "MAIN_NEXT_BUTTON_ACTIVE", x: 92, y: 18, width: 22, height: 18 },
{ name: "MAIN_EJECT_BUTTON", x: 114, y: 0, width: 22, height: 16 },
{ name: "MAIN_EJECT_BUTTON_ACTIVE", x: 114, y: 16, width: 22, height: 16 },
@ -824,3 +1041,11 @@ const sprites: SpriteMap = {
};
export default sprites;
export const spritesByName: { [name: string]: Sprite } = {};
Object.values(sprites).forEach((s) => {
s.forEach((sprite) => {
spritesByName[sprite.name] = sprite;
});
});

View file

@ -403,3 +403,11 @@ export function weakMapMemoize<T extends object, R>(
return cache.get(value);
};
}
export function imageVarName(imageName: string): string {
return `--webamp-${imageName}`;
}
export function cursorVarName(imageName: string): string {
return `--webamp-cursor-${imageName}`;
}

View file

@ -4,7 +4,8 @@ const imagemin = require("imagemin");
const imageminOptipng = require("imagemin-optipng");
const DATA_URL_REGEX = new RegExp(/url\((data:image\/png;base64,.+)\)/gi);
const DATA_URL_PROPS_REGEX = /^(background(?:-image)?)|(content)|(cursor)/;
// The `--` is so that we consider CSS variables.
const DATA_URL_PROPS_REGEX = /^(background(?:-image)?)|(content)|(cursor)|(--)/;
async function optimizeDataUri(dataUri) {
const buffer = dataUriToBuffer(dataUri);