/*!========================================================================== #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; }