/*!==========================================================================
   #CT.CSS
   ========================================================================== */

/*!
 * ct.css – Let’s take a look inside your <head>…
 *
 * © 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 <head> 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: "[<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]"
  }





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