UNPKG

@allystudio/color-vision-simulator

Version:

Color vision deficiency simulator for accessibility testing and design

237 lines (234 loc) 7.38 kB
// 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