Skip to content

Instantly share code, notes, and snippets.

@alekseykulikov
Created September 30, 2022 09:54
Show Gist options
  • Save alekseykulikov/2da13fa95dc2dd892a074ff6345e74ed to your computer and use it in GitHub Desktop.
Save alekseykulikov/2da13fa95dc2dd892a074ff6345e74ed to your computer and use it in GitHub Desktop.
Bookmark version of [CT](https://github.com/csswizardry/ct) with some improvements
/**
* Credit: https://github.com/csswizardry/ct
*
* Main changes:
* - use as a bookmark (toggle on/off)
* - work with any CSP (no extrnal scripts/styles)
* - fix an issue with async scripts added using inline JS (impossible to detect with css)
* - improved styles detection with [rel='stylesheet'][href]
*
* Add JS bookmark -> javascript:(<code below>)
*/
/* prettier-ignore */
(function () {
let css = String.raw;
let ct = document.querySelector('style.ct');
if (ct) return ct.remove();
document.querySelectorAll('script').forEach(s => {
if (s.async && s.getAttribute('async') === null && s.getAttribute('defer') === null) {
s.setAttribute('async', '');
}
});
ct = document.createElement('style');
ct.className = 'ct';
ct.innerHTML = css`
/**
* It's slightly easier to remember topics than it is colours. Set up some
* custom properties for use later on.
*/
head {
--ct-is-problematic: solid;
--ct-is-affected: dashed;
--ct-notify: #0bce6b;
--ct-warn: #ffa400;
--ct-error: #ff4e42;
}
/**
* Show the <head> and set up the items we might be interested in.
*/
head,
head script,
head script:not([src])[async],
head script:not([src])[defer],
head style,
head [rel='stylesheet'][href],
head script ~ meta[http-equiv='content-security-policy'],
head > meta[charset]:not(:nth-child(-n + 5)) {
display: block;
}
head script,
head style,
head [rel='stylesheet'][href],
head title,
head script ~ meta[http-equiv='content-security-policy'],
head > meta[charset]:not(:nth-child(-n + 5)) {
margin: 2px;
padding: 2px;
border-width: 2px;
background-color: white;
color: #333;
}
head ::before,
head script,
head style {
font: 16px/1.5 monospace, monospace;
display: block;
}
head ::before {
font-weight: bold;
}
/**
* External Script and Style
*/
head script[src],
head link[rel='stylesheet'] {
border-style: var(--ct-is-problematic);
border-color: var(--ct-warn);
}
head script[src]::before {
content: '[Blocking Script – ' attr(src) ']';
}
head link[rel='stylesheet']::before {
content: '[Blocking Stylesheet – ' attr(href) ']';
}
/**
* Inline Script and Style.
*/
head style:not(:empty),
head script:not(:empty) {
max-height: 5em;
overflow: auto;
background-color: #ffd;
white-space: pre;
border-color: var(--ct-notify);
border-style: var(--ct-is-problematic);
}
head script:not(:empty)::before {
content: '[Inline Script] ';
}
head style:not(:empty)::before {
content: '[Inline Style] ';
}
/**
* Blocked Title.
*
* These selectors are generally more complex because the Key Selector ('title')
* depends on the specific conditions of preceding JS--we can't cast a wide net
* and narrow it down later as we can when targeting elements directly.
*/
head script[src]:not([async]):not([defer]):not([type='module']) ~ title,
head script:not(:empty) ~ title {
display: block;
border-style: var(--ct-is-affected);
border-color: var(--ct-error);
}
head script[src]:not([async]):not([defer]):not([type='module']) ~ title::before,
head script:not(:empty) ~ title::before {
content: '[<title> blocked by JS] ';
}
/**
* Blocked Scripts.
*
* These selectors are generally more complex because the Key Selector
* ('script') depends on the specific conditions of preceding CSS--we can't cast
* a wide net and narrow it down later as we can when targeting elements
* directly.
*/
head [rel='stylesheet']:not([media='print']):not(.ct) ~ script,
head style:not(:empty) ~ script {
border-style: var(--ct-is-affected);
border-color: var(--ct-warn);
}
head [rel='stylesheet']:not([media='print']):not(.ct) ~ script::before,
head style:not(:empty) ~ script::before {
content: '[JS blocked by CSS – ' attr(src) ']';
}
/**
* Using both 'async' and 'defer' is redundant (an anti-pattern, even). Let's
* flag that.
*/
head script[src][src][async][defer] {
display: block;
border-style: var(--ct-is-problematic);
border-color: var(--ct-warn);
}
head script[src][src][async][defer]::before {
content: '[async and defer is redundant: prefer defer – ' attr(src) ']';
}
/**
* Async and defer simply do not work on inline scripts. It won't do any harm,
* but it's useful to know about.
*/
head script:not([src])[async],
head script:not([src])[defer] {
border-style: var(--ct-is-problematic);
border-color: var(--ct-warn);
}
head script:not([src])[async]::before {
content: 'The async attribute is redundant on inline scripts';
}
head script:not([src])[defer]::before {
content: 'The defer attribute is redundant on inline scripts';
}
/**
* Third Party blocking resources.
*
* Expect false-positives here… it's a crude proxy at best.
*
* Selector-chaining (e.g. '[src][src]') is used to bump up specificity.
*/
head script[src][src][src^="//"],
head script[src][src][src^="http"],
head [rel="stylesheet"][href^="//"],
head [rel="stylesheet"][href^="http"] {
border-style: var(--ct-is-problematic);
border-color: var(--ct-error);
}
head script[src][src][src^="//"]::before,
head script[src][src][src^="http"]::before {
content: '[Third Party Blocking Script – ' attr(src) ']';
}
head [rel="stylesheet"][href^="//"]::before,
head [rel="stylesheet"][href^="http"]::before {
content: '[Third Party Blocking Stylesheet – ' attr(href) ']';
}
/**
* Mid-HEAD CSP disables the Preload Scanner
*/
head script ~ meta[http-equiv='content-security-policy'] {
border-style: var(--ct-is-problematic);
border-color: var(--ct-error);
}
head script ~ meta[http-equiv='content-security-policy']::before {
content: '[Meta CSP defined after JS]';
}
/**
* Charset should appear as early as possible
*/
head > meta[charset]:not(:nth-child(-n + 5)) {
border-style: var(--ct-is-problematic);
border-color: var(--ct-warn);
}
head > meta[charset]:not(:nth-child(-n + 5))::before {
content: '[Charset should appear as early as possible]';
}
/**
* Hide all irrelevant or non-matching scripts and styles (including ct.css).
*
* We're done!
*/
link[rel='stylesheet'][media='print'],
link[rel='stylesheet'].ct,
style.ct,
script[async],
script[defer],
script[type='module'] {
display: none;
}
`;
document.head.appendChild(ct);
})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment