Skip to content

Instantly share code, notes, and snippets.

@junosuarez
Last active April 17, 2023 21:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save junosuarez/f0a50cf783f65aac729a1a5e9bb298db to your computer and use it in GitHub Desktop.
Save junosuarez/f0a50cf783f65aac729a1a5e9bb298db to your computer and use it in GitHub Desktop.
A workaround for "broken" text selection on some sites in Chrome
// 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 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 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