Refactor: Move blend mode menu styles to CSS file

Moved all blend mode menu styles from CanvasLayers.ts to a dedicated CSS file. Replaced inline styles with CSS classes and preserved all functionality.
This commit is contained in:
Dariusz L
2025-08-03 14:18:21 +02:00
parent 5b54ab28cb
commit c4318d4923
6 changed files with 389 additions and 146 deletions

View File

@@ -238,7 +238,8 @@ export class CanvasIO {
} }
catch (error) { catch (error) {
log.error(`Failed to send data for node ${nodeId}:`, error); log.error(`Failed to send data for node ${nodeId}:`, error);
throw new Error(`Failed to get confirmation from server for node ${nodeId}. The workflow might not have the latest canvas data.`); throw new Error(`Failed to get confirmation from server for node ${nodeId}. ` +
`Make sure that the nodeId: (${nodeId}) matches the "node_id" value in the node options. If they don't match, you may need to manually set the node_id to ${nodeId}.`);
} }
} }
async addInputToCanvas(inputImage, inputMask) { async addInputToCanvas(inputImage, inputMask) {

View File

@@ -3,6 +3,7 @@ import { createModuleLogger } from "./utils/LoggerUtils.js";
import { generateUUID, generateUniqueFileName, createCanvas } from "./utils/CommonUtils.js"; import { generateUUID, generateUniqueFileName, createCanvas } from "./utils/CommonUtils.js";
import { withErrorHandling, createValidationError } from "./ErrorHandler.js"; import { withErrorHandling, createValidationError } from "./ErrorHandler.js";
import { showErrorNotification } from "./utils/NotificationUtils.js"; import { showErrorNotification } from "./utils/NotificationUtils.js";
import { addStylesheet, getUrl } from "./utils/ResourceManager.js";
// @ts-ignore // @ts-ignore
import { app } from "../../scripts/app.js"; import { app } from "../../scripts/app.js";
// @ts-ignore // @ts-ignore
@@ -122,6 +123,8 @@ export class CanvasLayers {
this.isAdjustingOpacity = false; this.isAdjustingOpacity = false;
this.internalClipboard = []; this.internalClipboard = [];
this.clipboardPreference = 'system'; this.clipboardPreference = 'system';
// Load CSS for blend mode menu
addStylesheet(getUrl('./css/blend_mode_menu.css'));
} }
async copySelectedLayers() { async copySelectedLayers() {
if (this.canvas.canvasSelection.selectedLayers.length === 0) if (this.canvas.canvasSelection.selectedLayers.length === 0)
@@ -785,65 +788,14 @@ export class CanvasLayers {
const menu = document.createElement('div'); const menu = document.createElement('div');
this.blendMenuElement = menu; this.blendMenuElement = menu;
menu.id = 'blend-mode-menu'; menu.id = 'blend-mode-menu';
menu.style.cssText = `
position: absolute;
top: 0;
left: 0;
background: #2a2a2a;
border: 1px solid #3a3a3a;
border-radius: 4px;
z-index: 10000;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
min-width: 200px;
`;
const titleBar = document.createElement('div'); const titleBar = document.createElement('div');
titleBar.style.cssText = ` titleBar.className = 'blend-menu-title-bar';
background: #3a3a3a;
color: white;
padding: 8px 10px;
cursor: move;
user-select: none;
border-radius: 3px 3px 0 0;
font-size: 12px;
font-weight: bold;
border-bottom: 1px solid #4a4a4a;
display: flex;
justify-content: space-between;
align-items: center;
`;
const titleText = document.createElement('span'); const titleText = document.createElement('span');
titleText.textContent = `Blend Mode: ${selectedLayer.name}`; titleText.textContent = `Blend Mode: ${selectedLayer.name}`;
titleText.style.cssText = ` titleText.className = 'blend-menu-title-text';
flex: 1;
cursor: move;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
`;
const closeButton = document.createElement('button'); const closeButton = document.createElement('button');
closeButton.textContent = '×'; closeButton.textContent = '×';
closeButton.style.cssText = ` closeButton.className = 'blend-menu-close-button';
background: none;
border: none;
color: white;
font-size: 18px;
cursor: pointer;
padding: 0;
margin: 0;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 3px;
transition: background-color 0.2s;
`;
closeButton.onmouseover = () => {
closeButton.style.backgroundColor = '#4a4a4a';
};
closeButton.onmouseout = () => {
closeButton.style.backgroundColor = 'transparent';
};
closeButton.onclick = (e) => { closeButton.onclick = (e) => {
e.stopPropagation(); e.stopPropagation();
this.closeBlendModeMenu(); this.closeBlendModeMenu();
@@ -851,18 +803,19 @@ export class CanvasLayers {
titleBar.appendChild(titleText); titleBar.appendChild(titleText);
titleBar.appendChild(closeButton); titleBar.appendChild(closeButton);
const content = document.createElement('div'); const content = document.createElement('div');
content.style.cssText = `padding: 5px;`; content.className = 'blend-menu-content';
menu.appendChild(titleBar); menu.appendChild(titleBar);
menu.appendChild(content); menu.appendChild(content);
const blendAreaContainer = document.createElement('div'); const blendAreaContainer = document.createElement('div');
blendAreaContainer.style.cssText = `padding: 5px 10px; border-bottom: 1px solid #4a4a4a;`; blendAreaContainer.className = 'blend-area-container';
const blendAreaLabel = document.createElement('label'); const blendAreaLabel = document.createElement('label');
blendAreaLabel.textContent = 'Blend Area'; blendAreaLabel.textContent = 'Blend Area';
blendAreaLabel.style.color = 'white'; blendAreaLabel.className = 'blend-area-label';
const blendAreaSlider = document.createElement('input'); const blendAreaSlider = document.createElement('input');
blendAreaSlider.type = 'range'; blendAreaSlider.type = 'range';
blendAreaSlider.min = '0'; blendAreaSlider.min = '0';
blendAreaSlider.max = '100'; blendAreaSlider.max = '100';
blendAreaSlider.className = 'blend-area-slider';
blendAreaSlider.value = selectedLayer?.blendArea?.toString() ?? '0'; blendAreaSlider.value = selectedLayer?.blendArea?.toString() ?? '0';
blendAreaSlider.oninput = () => { blendAreaSlider.oninput = () => {
if (selectedLayer) { if (selectedLayer) {
@@ -912,20 +865,19 @@ export class CanvasLayers {
this.blendModes.forEach((mode) => { this.blendModes.forEach((mode) => {
const container = document.createElement('div'); const container = document.createElement('div');
container.className = 'blend-mode-container'; container.className = 'blend-mode-container';
container.style.cssText = `margin-bottom: 5px;`;
const option = document.createElement('div'); const option = document.createElement('div');
option.style.cssText = `padding: 5px 10px; color: white; cursor: pointer; transition: background-color 0.2s;`; option.className = 'blend-mode-option';
option.textContent = `${mode.label} (${mode.name})`; option.textContent = `${mode.label} (${mode.name})`;
const slider = document.createElement('input'); const slider = document.createElement('input');
slider.type = 'range'; slider.type = 'range';
slider.min = '0'; slider.min = '0';
slider.max = '100'; slider.max = '100';
slider.className = 'blend-opacity-slider';
const selectedLayer = this.canvas.canvasSelection.selectedLayers[0]; const selectedLayer = this.canvas.canvasSelection.selectedLayers[0];
slider.value = selectedLayer ? String(Math.round(selectedLayer.opacity * 100)) : '100'; slider.value = selectedLayer ? String(Math.round(selectedLayer.opacity * 100)) : '100';
slider.style.cssText = `width: 100%; margin: 5px 0; display: none;`;
if (selectedLayer && selectedLayer.blendMode === mode.name) { if (selectedLayer && selectedLayer.blendMode === mode.name) {
slider.style.display = 'block'; container.classList.add('active');
option.style.backgroundColor = '#3a3a3a'; option.classList.add('active');
} }
option.onclick = () => { option.onclick = () => {
// Re-check selected layer at the time of click // Re-check selected layer at the time of click
@@ -933,19 +885,17 @@ export class CanvasLayers {
if (!currentSelectedLayer) { if (!currentSelectedLayer) {
return; return;
} }
// Hide only the opacity sliders within other blend mode containers // Remove active class from all containers and options
content.querySelectorAll('.blend-mode-container').forEach(c => { content.querySelectorAll('.blend-mode-container').forEach(c => {
const opacitySlider = c.querySelector('input[type="range"]'); c.classList.remove('active');
if (opacitySlider) { const optionDiv = c.querySelector('.blend-mode-option');
opacitySlider.style.display = 'none';
}
const optionDiv = c.querySelector('div');
if (optionDiv) { if (optionDiv) {
optionDiv.style.backgroundColor = ''; optionDiv.classList.remove('active');
} }
}); });
slider.style.display = 'block'; // Add active class to current container and option
option.style.backgroundColor = '#3a3a3a'; container.classList.add('active');
option.classList.add('active');
currentSelectedLayer.blendMode = mode.name; currentSelectedLayer.blendMode = mode.name;
this.canvas.render(); this.canvas.render();
}; };

170
js/css/blend_mode_menu.css Normal file
View File

@@ -0,0 +1,170 @@
/* Blend Mode Menu Styles */
#blend-mode-menu {
position: absolute;
top: 0;
left: 0;
background: #2a2a2a;
border: 1px solid #3a3a3a;
border-radius: 4px;
z-index: 10000;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
min-width: 200px;
}
#blend-mode-menu .blend-menu-title-bar {
background: #3a3a3a;
color: white;
padding: 8px 10px;
cursor: move;
user-select: none;
border-radius: 3px 3px 0 0;
font-size: 12px;
font-weight: bold;
border-bottom: 1px solid #4a4a4a;
display: flex;
justify-content: space-between;
align-items: center;
}
#blend-mode-menu .blend-menu-title-text {
flex: 1;
cursor: move;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
#blend-mode-menu .blend-menu-close-button {
background: none;
border: none;
color: white;
font-size: 18px;
cursor: pointer;
padding: 0;
margin: 0;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 3px;
transition: background-color 0.2s;
}
#blend-mode-menu .blend-menu-close-button:hover {
background-color: #4a4a4a;
}
#blend-mode-menu .blend-menu-close-button:focus {
background-color: transparent;
}
#blend-mode-menu .blend-menu-content {
padding: 5px;
}
#blend-mode-menu .blend-area-container {
padding: 5px 10px;
border-bottom: 1px solid #4a4a4a;
}
#blend-mode-menu .blend-area-label {
color: white;
display: block;
margin-bottom: 5px;
font-size: 12px;
}
#blend-mode-menu .blend-area-slider {
width: 100%;
margin: 5px 0;
-webkit-appearance: none;
height: 4px;
background: #555;
border-radius: 2px;
outline: none;
}
#blend-mode-menu .blend-area-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 14px;
height: 14px;
background: #e0e0e0;
border-radius: 50%;
cursor: pointer;
border: 2px solid #555;
transition: background 0.2s;
}
#blend-mode-menu .blend-area-slider::-webkit-slider-thumb:hover {
background: #fff;
}
#blend-mode-menu .blend-area-slider::-moz-range-thumb {
width: 14px;
height: 14px;
background: #e0e0e0;
border-radius: 50%;
cursor: pointer;
border: 2px solid #555;
}
#blend-mode-menu .blend-mode-container {
margin-bottom: 5px;
}
#blend-mode-menu .blend-mode-option {
padding: 5px 10px;
color: white;
cursor: pointer;
transition: background-color 0.2s;
}
#blend-mode-menu .blend-mode-option:hover {
background-color: #3a3a3a;
}
#blend-mode-menu .blend-mode-option.active {
background-color: #3a3a3a;
}
#blend-mode-menu .blend-opacity-slider {
width: 100%;
margin: 5px 0;
display: none;
-webkit-appearance: none;
height: 4px;
background: #555;
border-radius: 2px;
outline: none;
}
#blend-mode-menu .blend-mode-container.active .blend-opacity-slider {
display: block;
}
#blend-mode-menu .blend-opacity-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 14px;
height: 14px;
background: #e0e0e0;
border-radius: 50%;
cursor: pointer;
border: 2px solid #555;
transition: background 0.2s;
}
#blend-mode-menu .blend-opacity-slider::-webkit-slider-thumb:hover {
background: #fff;
}
#blend-mode-menu .blend-opacity-slider::-moz-range-thumb {
width: 14px;
height: 14px;
background: #e0e0e0;
border-radius: 50%;
cursor: pointer;
border: 2px solid #555;
}

View File

@@ -269,7 +269,10 @@ export class CanvasIO {
log.error(`Failed to send data for node ${nodeId}:`, error); log.error(`Failed to send data for node ${nodeId}:`, error);
throw new Error(`Failed to get confirmation from server for node ${nodeId}. The workflow might not have the latest canvas data.`); throw new Error(
`Failed to get confirmation from server for node ${nodeId}. ` +
`Make sure that the nodeId: (${nodeId}) matches the "node_id" value in the node options. If they don't match, you may need to manually set the node_id to ${nodeId}.`
);
} }
} }

View File

@@ -3,6 +3,7 @@ import {createModuleLogger} from "./utils/LoggerUtils.js";
import {generateUUID, generateUniqueFileName, createCanvas} from "./utils/CommonUtils.js"; import {generateUUID, generateUniqueFileName, createCanvas} from "./utils/CommonUtils.js";
import {withErrorHandling, createValidationError} from "./ErrorHandler.js"; import {withErrorHandling, createValidationError} from "./ErrorHandler.js";
import {showErrorNotification, showSuccessNotification} from "./utils/NotificationUtils.js"; import {showErrorNotification, showSuccessNotification} from "./utils/NotificationUtils.js";
import { addStylesheet, getUrl } from "./utils/ResourceManager.js";
// @ts-ignore // @ts-ignore
import {app} from "../../scripts/app.js"; import {app} from "../../scripts/app.js";
// @ts-ignore // @ts-ignore
@@ -57,6 +58,9 @@ export class CanvasLayers {
this.isAdjustingOpacity = false; this.isAdjustingOpacity = false;
this.internalClipboard = []; this.internalClipboard = [];
this.clipboardPreference = 'system'; this.clipboardPreference = 'system';
// Load CSS for blend mode menu
addStylesheet(getUrl('./css/blend_mode_menu.css'));
} }
async copySelectedLayers(): Promise<void> { async copySelectedLayers(): Promise<void> {
@@ -916,70 +920,17 @@ export class CanvasLayers {
const menu = document.createElement('div'); const menu = document.createElement('div');
this.blendMenuElement = menu; this.blendMenuElement = menu;
menu.id = 'blend-mode-menu'; menu.id = 'blend-mode-menu';
menu.style.cssText = `
position: absolute;
top: 0;
left: 0;
background: #2a2a2a;
border: 1px solid #3a3a3a;
border-radius: 4px;
z-index: 10000;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
min-width: 200px;
`;
const titleBar = document.createElement('div'); const titleBar = document.createElement('div');
titleBar.style.cssText = ` titleBar.className = 'blend-menu-title-bar';
background: #3a3a3a;
color: white;
padding: 8px 10px;
cursor: move;
user-select: none;
border-radius: 3px 3px 0 0;
font-size: 12px;
font-weight: bold;
border-bottom: 1px solid #4a4a4a;
display: flex;
justify-content: space-between;
align-items: center;
`;
const titleText = document.createElement('span'); const titleText = document.createElement('span');
titleText.textContent = `Blend Mode: ${selectedLayer.name}`; titleText.textContent = `Blend Mode: ${selectedLayer.name}`;
titleText.style.cssText = ` titleText.className = 'blend-menu-title-text';
flex: 1;
cursor: move;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
`;
const closeButton = document.createElement('button'); const closeButton = document.createElement('button');
closeButton.textContent = '×'; closeButton.textContent = '×';
closeButton.style.cssText = ` closeButton.className = 'blend-menu-close-button';
background: none;
border: none;
color: white;
font-size: 18px;
cursor: pointer;
padding: 0;
margin: 0;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 3px;
transition: background-color 0.2s;
`;
closeButton.onmouseover = () => {
closeButton.style.backgroundColor = '#4a4a4a';
};
closeButton.onmouseout = () => {
closeButton.style.backgroundColor = 'transparent';
};
closeButton.onclick = (e: MouseEvent) => { closeButton.onclick = (e: MouseEvent) => {
e.stopPropagation(); e.stopPropagation();
@@ -990,22 +941,23 @@ export class CanvasLayers {
titleBar.appendChild(closeButton); titleBar.appendChild(closeButton);
const content = document.createElement('div'); const content = document.createElement('div');
content.style.cssText = `padding: 5px;`; content.className = 'blend-menu-content';
menu.appendChild(titleBar); menu.appendChild(titleBar);
menu.appendChild(content); menu.appendChild(content);
const blendAreaContainer = document.createElement('div'); const blendAreaContainer = document.createElement('div');
blendAreaContainer.style.cssText = `padding: 5px 10px; border-bottom: 1px solid #4a4a4a;`; blendAreaContainer.className = 'blend-area-container';
const blendAreaLabel = document.createElement('label'); const blendAreaLabel = document.createElement('label');
blendAreaLabel.textContent = 'Blend Area'; blendAreaLabel.textContent = 'Blend Area';
blendAreaLabel.style.color = 'white'; blendAreaLabel.className = 'blend-area-label';
const blendAreaSlider = document.createElement('input'); const blendAreaSlider = document.createElement('input');
blendAreaSlider.type = 'range'; blendAreaSlider.type = 'range';
blendAreaSlider.min = '0'; blendAreaSlider.min = '0';
blendAreaSlider.max = '100'; blendAreaSlider.max = '100';
blendAreaSlider.className = 'blend-area-slider';
blendAreaSlider.value = selectedLayer?.blendArea?.toString() ?? '0'; blendAreaSlider.value = selectedLayer?.blendArea?.toString() ?? '0';
@@ -1064,23 +1016,22 @@ export class CanvasLayers {
this.blendModes.forEach((mode: BlendMode) => { this.blendModes.forEach((mode: BlendMode) => {
const container = document.createElement('div'); const container = document.createElement('div');
container.className = 'blend-mode-container'; container.className = 'blend-mode-container';
container.style.cssText = `margin-bottom: 5px;`;
const option = document.createElement('div'); const option = document.createElement('div');
option.style.cssText = `padding: 5px 10px; color: white; cursor: pointer; transition: background-color 0.2s;`; option.className = 'blend-mode-option';
option.textContent = `${mode.label} (${mode.name})`; option.textContent = `${mode.label} (${mode.name})`;
const slider = document.createElement('input'); const slider = document.createElement('input');
slider.type = 'range'; slider.type = 'range';
slider.min = '0'; slider.min = '0';
slider.max = '100'; slider.max = '100';
slider.className = 'blend-opacity-slider';
const selectedLayer = this.canvas.canvasSelection.selectedLayers[0]; const selectedLayer = this.canvas.canvasSelection.selectedLayers[0];
slider.value = selectedLayer ? String(Math.round(selectedLayer.opacity * 100)) : '100'; slider.value = selectedLayer ? String(Math.round(selectedLayer.opacity * 100)) : '100';
slider.style.cssText = `width: 100%; margin: 5px 0; display: none;`;
if (selectedLayer && selectedLayer.blendMode === mode.name) { if (selectedLayer && selectedLayer.blendMode === mode.name) {
slider.style.display = 'block'; container.classList.add('active');
option.style.backgroundColor = '#3a3a3a'; option.classList.add('active');
} }
option.onclick = () => { option.onclick = () => {
@@ -1090,20 +1041,18 @@ export class CanvasLayers {
return; return;
} }
// Hide only the opacity sliders within other blend mode containers // Remove active class from all containers and options
content.querySelectorAll<HTMLDivElement>('.blend-mode-container').forEach(c => { content.querySelectorAll<HTMLDivElement>('.blend-mode-container').forEach(c => {
const opacitySlider = c.querySelector<HTMLInputElement>('input[type="range"]'); c.classList.remove('active');
if (opacitySlider) { const optionDiv = c.querySelector<HTMLDivElement>('.blend-mode-option');
opacitySlider.style.display = 'none';
}
const optionDiv = c.querySelector<HTMLDivElement>('div');
if (optionDiv) { if (optionDiv) {
optionDiv.style.backgroundColor = ''; optionDiv.classList.remove('active');
} }
}); });
slider.style.display = 'block'; // Add active class to current container and option
option.style.backgroundColor = '#3a3a3a'; container.classList.add('active');
option.classList.add('active');
currentSelectedLayer.blendMode = mode.name; currentSelectedLayer.blendMode = mode.name;
this.canvas.render(); this.canvas.render();

170
src/css/blend_mode_menu.css Normal file
View File

@@ -0,0 +1,170 @@
/* Blend Mode Menu Styles */
#blend-mode-menu {
position: absolute;
top: 0;
left: 0;
background: #2a2a2a;
border: 1px solid #3a3a3a;
border-radius: 4px;
z-index: 10000;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
min-width: 200px;
}
#blend-mode-menu .blend-menu-title-bar {
background: #3a3a3a;
color: white;
padding: 8px 10px;
cursor: move;
user-select: none;
border-radius: 3px 3px 0 0;
font-size: 12px;
font-weight: bold;
border-bottom: 1px solid #4a4a4a;
display: flex;
justify-content: space-between;
align-items: center;
}
#blend-mode-menu .blend-menu-title-text {
flex: 1;
cursor: move;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
#blend-mode-menu .blend-menu-close-button {
background: none;
border: none;
color: white;
font-size: 18px;
cursor: pointer;
padding: 0;
margin: 0;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 3px;
transition: background-color 0.2s;
}
#blend-mode-menu .blend-menu-close-button:hover {
background-color: #4a4a4a;
}
#blend-mode-menu .blend-menu-close-button:focus {
background-color: transparent;
}
#blend-mode-menu .blend-menu-content {
padding: 5px;
}
#blend-mode-menu .blend-area-container {
padding: 5px 10px;
border-bottom: 1px solid #4a4a4a;
}
#blend-mode-menu .blend-area-label {
color: white;
display: block;
margin-bottom: 5px;
font-size: 12px;
}
#blend-mode-menu .blend-area-slider {
width: 100%;
margin: 5px 0;
-webkit-appearance: none;
height: 4px;
background: #555;
border-radius: 2px;
outline: none;
}
#blend-mode-menu .blend-area-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 14px;
height: 14px;
background: #e0e0e0;
border-radius: 50%;
cursor: pointer;
border: 2px solid #555;
transition: background 0.2s;
}
#blend-mode-menu .blend-area-slider::-webkit-slider-thumb:hover {
background: #fff;
}
#blend-mode-menu .blend-area-slider::-moz-range-thumb {
width: 14px;
height: 14px;
background: #e0e0e0;
border-radius: 50%;
cursor: pointer;
border: 2px solid #555;
}
#blend-mode-menu .blend-mode-container {
margin-bottom: 5px;
}
#blend-mode-menu .blend-mode-option {
padding: 5px 10px;
color: white;
cursor: pointer;
transition: background-color 0.2s;
}
#blend-mode-menu .blend-mode-option:hover {
background-color: #3a3a3a;
}
#blend-mode-menu .blend-mode-option.active {
background-color: #3a3a3a;
}
#blend-mode-menu .blend-opacity-slider {
width: 100%;
margin: 5px 0;
display: none;
-webkit-appearance: none;
height: 4px;
background: #555;
border-radius: 2px;
outline: none;
}
#blend-mode-menu .blend-mode-container.active .blend-opacity-slider {
display: block;
}
#blend-mode-menu .blend-opacity-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 14px;
height: 14px;
background: #e0e0e0;
border-radius: 50%;
cursor: pointer;
border: 2px solid #555;
transition: background 0.2s;
}
#blend-mode-menu .blend-opacity-slider::-webkit-slider-thumb:hover {
background: #fff;
}
#blend-mode-menu .blend-opacity-slider::-moz-range-thumb {
width: 14px;
height: 14px;
background: #e0e0e0;
border-radius: 50%;
cursor: pointer;
border: 2px solid #555;
}