Last active
April 17, 2023 21:21
-
-
Save junosuarez/f0a50cf783f65aac729a1a5e9bb298db to your computer and use it in GitHub Desktop.
A workaround for "broken" text selection on some sites in Chrome
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Find all css vars used in ::selection styles - use this snippet in dev tools | |
new Set( | |
Array.from(document.styleSheets) | |
.flatMap(s => { try { return Array.from(s.rules) } catch (e) { return [] } }) | |
.filter(rule => rule.selectorText?.includes('::selection')) | |
.flatMap(rule => Array.from(rule.cssText.matchAll(RE_CSS_VAR)) | |
.map(match => match.groups.varName) | |
) | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This fixes a bug with text selection highlighting on some websites | |
// by suggesting CSS to add to user styles as a temporary fix. | |
// Review the code and run in your browser console. | |
// | |
// Details: https://bugs.chromium.org/p/chromium/issues/detail?id=1429546 | |
// Note, the chromium bug was closed because it's actually in-spec behavior | |
// But as a user, we don't want to suffer broken highlighting while various | |
// site, browser, and spec developers pass this buck into one another's bug | |
// queues. | |
// | |
// The problem is CSS variables are commonly declared in a way that | |
// is incompatible with custom styling for text selection, and Chromium browsers | |
// handle this by making the text selection invisible - which is unusable. | |
// | |
// This fixes the CSS variable scoping issue by raising :root-scoped variables | |
// to be "registered custom properties" which lets them work for text selection. | |
// | |
// Further reading: https://github.com/w3c/csswg-drafts/issues/6641 | |
// --------------------- | |
// This regex matches a unary css variable expression | |
// | |
// syntax references: | |
// - https://www.w3.org/TR/css-values-4/#dashed-idents | |
// - https://www.w3.org/TR/css-variables-1/#defining-variables | |
// - https://www.w3.org/TR/css-variables-1/#using-variables | |
const RE_CSS_VAR = /var\(\s*(?<varName>--[A-z][A-z0-9-]*)\s*\)/g | |
// Find all css vars used in ::selection styles | |
const usedVars = new Set( | |
Array.from(document.styleSheets) | |
.flatMap(s => { try { return Array.from(s.rules) } catch (e) { return [] } }) | |
.filter(rule => rule.selectorText?.includes('::selection')) | |
.flatMap(rule => Array.from(rule.cssText.matchAll(RE_CSS_VAR)) | |
.map(match => match.groups.varName) | |
) | |
) | |
if (usedVars.size === 0) { | |
console.log('No used CSS vars found, no action needed.') | |
} else { | |
console.log(`Add this to your user stylesheet for ${window.location.hostname} (eg with https://add0n.com/stylus.html ):`) | |
usedVars.forEach(varName => { | |
// 1. derefrence the var in :root scope | |
const value = window.getComputedStyle(document.documentElement).getPropertyValue(varName) | |
// 2. register it as a global CSS property | |
// (which is the scope that developers (rather than spec writers) | |
// intend when setting a css variable in :root) | |
// registered properties are available everywhere, including in pseudoselectors. | |
// note, they have some other important but subtle differences from regular (non-registered) custom properties - | |
// read more at https://drafts.css-houdini.org/css-properties-values-api/#behavior-of-custom-properties | |
console.log(` | |
@property ${varName} { | |
syntax: '*'; | |
inherits: false; | |
initial-value: ${value}; | |
}`) | |
}) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This script fixes a bug with text selection highlighting on some websites. | |
// Details: https://bugs.chromium.org/p/chromium/issues/detail?id=1429546 | |
// Note, the chromium bug was closed because it's actually in-spec behavior | |
// But as a user, we don't want to suffer broken highlighting while various | |
// site, browser, and spec developers pass this buck into one another's bug | |
// queues. | |
// | |
// The problem is CSS variables are commonly declared in a way that | |
// is incompatible with custom styling for text selection, and Chromium browsers | |
// handle this by making the text selection invisible - which is unusable. | |
// | |
// This fixes the CSS variable scoping issue by raising :root-scoped variables | |
// to be "registered custom properties" which lets them work for text selection. | |
// | |
// Further reading: https://github.com/w3c/csswg-drafts/issues/6641 | |
// This regex matches a unary css variable expression | |
// | |
// syntax references: | |
// - https://www.w3.org/TR/css-values-4/#dashed-idents | |
// - https://www.w3.org/TR/css-variables-1/#defining-variables | |
// - https://www.w3.org/TR/css-variables-1/#using-variables | |
const RE_CSS_VAR = /var\(\s*(?<varName>--[A-z][A-z0-9-]*)\s*\)/g | |
function fixScope(varName) { | |
// 1. derefrence the var in :root scope | |
const value = window.getComputedStyle(document.documentElement).getPropertyValue(varName) | |
// 2. register it as a global CSS property | |
// (which is the scope that developers (rather than spec writers) | |
// intend when setting a css variable in :root) | |
// registered properties are available everywhere, including in pseudoselectors. | |
// note, they have some other important but subtle differences from regular (non-registered) custom properties - | |
// read more at https://drafts.css-houdini.org/css-properties-values-api/#behavior-of-custom-properties | |
try { | |
window.CSS.registerProperty({ | |
name: varName, | |
inherits: false, // required config. browser defaluts to false, so we'll use that | |
initialValue: value | |
}) | |
} catch (e) { | |
// throws if property already registered - in that case there's nothing we can do but log and ignore it | |
console.error('Failed to register custom property', varName, e) | |
} | |
} | |
// Find all css vars used in ::selection styles and apply the fix | |
new Set( | |
Array.from(document.styleSheets) | |
.flatMap(s => Array.from(s.rules)) | |
.filter(rule => rule.selectorText?.includes('::selection')) | |
.flatMap(rule => Array.from(rule.cssText.matchAll(RE_CSS_VAR)) | |
.map(match => match.groups.varName) | |
) | |
) | |
.forEach(fixScope) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment