@allystudio/color-vision-simulator
Version:
Color vision deficiency simulator for accessibility testing and design
237 lines (234 loc) • 7.38 kB
JavaScript
// src/types.ts
var COLOR_VISION_PRESETS = {
PROTANOPIA: "protanopia",
DEUTERANOPIA: "deuteranopia",
TRITANOPIA: "tritanopia",
ACHROMATOPSIA: "achromatopsia",
NORMAL: "normal"
};
// src/simulator.ts
var COLOR_DEFICIENCY_FILTERS = {
[COLOR_VISION_PRESETS.PROTANOPIA]: `url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><filter id='protanopia'><feColorMatrix in='SourceGraphic' type='matrix' values='0.567, 0.433, 0, 0, 0, 0.558, 0.442, 0, 0, 0, 0, 0.242, 0.758, 0, 0, 0, 0, 0, 1, 0'/></filter></svg>#protanopia")`,
[COLOR_VISION_PRESETS.DEUTERANOPIA]: `url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><filter id='deuteranopia'><feColorMatrix in='SourceGraphic' type='matrix' values='0.625, 0.375, 0, 0, 0, 0.7, 0.3, 0, 0, 0, 0, 0.3, 0.7, 0, 0, 0, 0, 0, 1, 0'/></filter></svg>#deuteranopia")`,
[COLOR_VISION_PRESETS.TRITANOPIA]: `url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><filter id='tritanopia'><feColorMatrix in='SourceGraphic' type='matrix' values='0.95, 0.05, 0, 0, 0, 0, 0.433, 0.567, 0, 0, 0, 0.475, 0.525, 0, 0, 0, 0, 0, 1, 0'/></filter></svg>#tritanopia")`,
[COLOR_VISION_PRESETS.ACHROMATOPSIA]: "grayscale(100%)",
[COLOR_VISION_PRESETS.NORMAL]: "none"
};
function createColorVisionSimulator(options = {}) {
let state = {
isActive: false,
visionType: COLOR_VISION_PRESETS.PROTANOPIA
};
const config = {
overlayId: options.overlayId ?? "color-vision-simulator-overlay",
stylesId: options.stylesId ?? "color-vision-simulator-styles",
zIndex: options.zIndex ?? 2147483647,
useDirectFilter: options.useDirectFilter ?? true
};
const callbacks = /* @__PURE__ */ new Set();
function updateSimulation() {
let overlay = document.getElementById(config.overlayId);
let styleEl = document.getElementById(config.stylesId);
if (!state.isActive) {
if (overlay) overlay.remove();
if (styleEl) styleEl.remove();
document.documentElement.classList.remove("ally-direct-filter");
return;
}
if (!overlay) {
overlay = document.createElement("div");
overlay.id = config.overlayId;
document.body.appendChild(overlay);
}
if (!styleEl) {
styleEl = document.createElement("style");
styleEl.id = config.stylesId;
document.head.appendChild(styleEl);
}
const filter = COLOR_DEFICIENCY_FILTERS[state.visionType];
styleEl.textContent = `
#${config.overlayId} {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
pointer-events: none;
z-index: ${config.zIndex};
filter: ${filter};
}
/* Apply to the entire page if directly supported by the browser */
${config.useDirectFilter && state.visionType !== COLOR_VISION_PRESETS.NORMAL ? `
html.ally-direct-filter {
filter: ${filter};
}
html.ally-direct-filter #${config.overlayId} {
display: none;
}
` : ""}
`;
if (config.useDirectFilter && state.visionType !== COLOR_VISION_PRESETS.NORMAL) {
document.documentElement.classList.add("ally-direct-filter");
} else {
document.documentElement.classList.remove("ally-direct-filter");
}
}
function notifyStateChange() {
const stateEvent = {
isActive: state.isActive,
visionType: state.visionType
};
callbacks.forEach((callback) => {
try {
callback(stateEvent);
} catch (error) {
console.error("[ColorVisionSimulator] Error in state change callback:", error);
}
});
}
return {
/**
* Start the color vision deficiency simulation
*/
start() {
if (state.isActive) return;
state.isActive = true;
updateSimulation();
notifyStateChange();
},
/**
* Stop the color vision deficiency simulation
*/
stop() {
if (!state.isActive) return;
state.isActive = false;
updateSimulation();
notifyStateChange();
},
/**
* Toggle the simulation on/off
*/
toggle() {
if (state.isActive) {
this.stop();
} else {
this.start();
}
},
/**
* Set the type of color vision deficiency to simulate
*/
setVisionType(type) {
if (state.visionType === type) return;
state.visionType = type;
updateSimulation();
notifyStateChange();
},
/**
* Set both vision type and active state at once
*/
configure(visionType, isActive) {
const changed = state.visionType !== visionType || state.isActive !== isActive;
state.visionType = visionType;
state.isActive = isActive;
if (changed) {
updateSimulation();
notifyStateChange();
}
},
/**
* Get the current vision type being simulated
*/
getVisionType() {
return state.visionType;
},
/**
* Check if the simulator is currently active
*/
isActive() {
return state.isActive;
},
/**
* Get the current state of the simulator
*/
getState() {
return { ...state };
},
/**
* Subscribe to state changes
*/
onStateChange(callback) {
callbacks.add(callback);
return () => {
callbacks.delete(callback);
};
},
/**
* Clean up the simulator and remove all DOM elements
*/
destroy() {
this.stop();
callbacks.clear();
const overlay = document.getElementById(config.overlayId);
const styleEl = document.getElementById(config.stylesId);
if (overlay) overlay.remove();
if (styleEl) styleEl.remove();
document.documentElement.classList.remove("ally-direct-filter");
}
};
}
var singletonInstance = null;
function getSingletonSimulator(options) {
if (!singletonInstance) {
singletonInstance = createColorVisionSimulator(options);
}
return singletonInstance;
}
function destroySingletonSimulator() {
if (singletonInstance) {
singletonInstance.destroy();
singletonInstance = null;
}
}
// src/utils.ts
function formatColorVisionType(type) {
switch (type) {
case "protanopia":
return "Protanopia (Red-blind)";
case "deuteranopia":
return "Deuteranopia (Green-blind)";
case "tritanopia":
return "Tritanopia (Blue-blind)";
case "achromatopsia":
return "Achromatopsia (Total color blindness)";
case "normal":
return "Normal Vision";
default:
return "Unknown";
}
}
function getColorVisionDescription(type) {
switch (type) {
case "protanopia":
return "Difficulty distinguishing between red and green colors, with red appearing darker";
case "deuteranopia":
return "Difficulty distinguishing between red and green colors, most common form of color blindness";
case "tritanopia":
return "Difficulty distinguishing between blue and yellow colors, very rare condition";
case "achromatopsia":
return "Complete absence of color vision, seeing only in shades of gray";
case "normal":
return "Normal color vision with no deficiencies";
default:
return "Unknown color vision type";
}
}
export {
COLOR_VISION_PRESETS,
createColorVisionSimulator,
destroySingletonSimulator,
formatColorVisionType,
getColorVisionDescription,
getSingletonSimulator
};
//# sourceMappingURL=index.js.map