/*!==========================================================================
#CT.CSS
========================================================================== */
/*!
* ct.css – Let’s take a look inside your
…
*
* © Harry Roberts 2021 – twitter.com/csswizardry
*/
/**
* 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 and set up the items we might be interested in.
*/
head,
head script,
head script[type]:not([type="text/javascript"]),
head script:not([src])[async],
head script:not([src])[defer],
head style, head [rel="stylesheet"],
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"],
head title,
head script ~ meta[http-equiv="content-security-policy"],
head > meta[charset]:not(:nth-child(-n+5)) {
margin: 5px;
padding: 5px;
border-width: 5px;
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[type]:not([type="text/javascript"]) ~ 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: "[ 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]"
}
/**
* Supplying a character encoding for scripts is invalid and may pervert the
* script from loading.
*/
head script[type*="charset"] {
border-style: var(--ct-is-problematic);
border-color: var(--ct-warn);
}
head script[type*="charset"]::before {
content: "[charset on scripts may prevent script from loading]"
}
head script[type="text/javascript"] {
border-style: var(--ct-is-problematic);
border-color: var(--ct-warn);
}
head script[type="text/javascript"]::before {
content: "[" attr(type) " is redundant: you might as well delete it]"
}
/**
* 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!
*/
head link[rel="stylesheet"][media="print"],
head link[rel="stylesheet"].ct, head style.ct,
head script[async], head script[defer], head script[type=module], head script[type]:not([type="text/javascript"]) {
display: none;
}