/*
 * Attempts to create universal defaults for various basic HTML elements.
 *
 * Browsers are inconsistent and these rules bring them in line and enhance
 * behaviour match to what novices intuit.
 *
 * Development guidelines:
 *   1. Keep selector specificity minimal to make overriding easier
 *     a. No ID references                        eg.  #button { ... }
 *     b. No class references                     eg.  .button { ... }
 *     c. No custom element/component references  eg.  custom-button { ... }
 *   2. Every ruleset must have accompanying commentary to justify its inclusion.
 *
 * ---
 * Authors: Robin Miller
 */

/* ============================================
 *                   GENERAL
 * ============================================ */

/**
 * Setting all basic container elements to flex + column because either flex or grid is more commonly
 * expected behaviour of container elements.
 *
 * Defaulting to scrollbars on containers prevents unexpectedly drawing over adjacent content.
 * Better to always be able to access content if the container is unexpectedly too small.
 */
html,
body,
header,
main,
nav,
footer,
article,
section,
div,
aside,
address,
picture,
figure,
dl,
form {
   display: flex;
   flex-direction: column;
}

/**
 * Chrome limits the HTML node to min height of viewport. Firefox does not.
 * This can cause odd behaviour when, eg. a linear-gradient background is set.
 * 
 * Sans-serif fonts are better for displays, serif is better for print.
 */
html {
   min-height: 100%;

   font-family: ui-sans-serif, system-ui, sans-serif;

   /* Prevent Safari from adjusting fonts */
   -moz-text-size-adjust: none;
   -webkit-text-size-adjust: none;
   text-size-adjust: none;
}

@media print {
   html {
      font-family: serif;
   }
}

/**
 * Allows transitions between absolute and auto
 */
@media (prefers-reduced-motion: no-preference) {
   html {
      interpolate-size: allow-keyword;
   }
}

/**
 * All elements should default to border-box instead of content-box because the
 * intuitive "size" of an object includes all its visible area. Since borders
 * are very commonly visible, many developers (especially new ones) expect
 * border-box behaviour instead of content-box.
 */
*, *::before, *::after {
   box-sizing: border-box;

   /* Prevents overflow caused by long words and URLs in narrow boxes. */
   overflow-wrap: break-word;
}

/**
 * Creating space from viewport edge should be done by the primary <header>, <footer>,
 * and <main> elements, instead of <body>.
 *
 * Then these core containers may fill the space but maintain legible spacing for their content.
 */
body > header,
main,
body > footer {
   padding: 1rem;
}

/* see ruleset above */
body {
   margin: 0;
   padding: 0;
}

/**
 * Headers should be closer to their contents than the previous section to indicate relationship
 */
h1,
h2,
h3,
h4,
h5,
h6 {
   margin-block-start: 1rem;
   margin-block-end: 0.5rem;

   /* Headers are short, balance looks best in narrow contexts */
   text-wrap: balance;
}

/**
 * Avoids awkward single-word lines (orphans)
 */
p, 
dl, 
ol, 
ul,
th,
td {
   text-wrap: pretty;
}

/**
 * With containers set to flex, text containers can drop their leading margins
 */
p,
pre,
dl,
ol,
ul {
   margin-block-start: 0.5em; /* flex containers do not merge margins, so using half standard values */
   margin-block-end: 0.5em;
}

/**
 * Definition terms should be highlighted as different from definitions
 */
dt {
   font-weight: bold;
}

/**
 * Separate interior definitions from each other
 */
dd + dt {
   margin-block-start: 0.25em;
}

/**
 * Flex layout can stretch Anchors and Labels to have a much larger clickable area than expected.
 * This resets that default behaviour to reduce the cases of accidental interaction.
 *
 * Note: Chrome does not respect align-items in fieldsets.
 */
a,
label {
   align-self: flex-start;
}

/**
 * Links should be visually distinct from surrounding text, and should draw the eye.
 * Additive-styling of underline on hover (extremely common design practice) implies that
 * bold font is the correct morphological distinguisher.
 * Restricting to :any-link (ie. :link and :visited) to only style links with an href attribute,
 * allowing links to be quazi-disabled when href is missing as per spec.
 */
a:any-link {
   font-weight: bold;
   text-decoration: underline rgba(0, 0, 0, 0);
}

/**
 * Show underline on hover to indicate interactivity.
 */
a:any-link:hover {
   text-decoration-color: inherit;
}

/**
 * Indicating there is hover title text increases discoverability.
 */
abbr[title],
dfn[title] {
   cursor: help;
}

/* Figures are almost always centered on the page between paragraphs */
figure {
   margin-left: auto;
   margin-right: auto;

   max-height: fit-content;
   max-width: fit-content;
}

/* Distinguishes caption from normal body text */
figcaption {
   text-align: center;
}

/**
 * Alt-text should appear different than standard text to indicate it is metadata
 */
img {
   font-style: italic;
}

/**
 * It is more common to expect media to be taller than a line (with exceptions like icons), so use block.
 * Max-width prevents image overflow when pixel count is large.
 */ 
img, picture, svg, video, canvas, audio, iframe, embed, object {
   display: block;
   max-width: 100%;
}

/* Better than the default of 300px */
audio {
   width: 100%;
}

/**
 * Audio without `[controls]` remains hidden by default
 */
audio:not([controls]) {
   display: none; 
}

/**
 * remove underline for <u> as it no longer should be used to underline text.
 * It now means "Unarticulated Annotation", which *may* be indicated by underline, (eg. spellcheck)
 * but could also indicate highlight, etc.
 *
 * Better to disable the old behaviour to discourage incorrect use.
 */
u {
   text-decoration: none;
}

/**
 * Template nodes should never be shown, by spec.
 * Source elements don't have anything to display.
 * Explicitly hidden just in case the browser does not know about templates.
 */
template, source {
   display: none;
}

/**
 * Removing user-select because summary is a clickable element that confusingly highlights on doubleclick
 */
details > summary {
   user-select: none;
}

/**
 * When an hr (thematic break) is a child of a flex layout container, it can disappear
 * in some default browser styles
 */
hr {
   /* providing any vertical margin will allow it to render in flex;
    * choosing this amount as a reasonable default */
   margin: 2rem 0;

   /* browser defaults often make it an inset 1px box, for some reason.
    * Using a single border is a simpler basic line. */
   border: none;
   border-top: 1px solid currentColor;
   color: inherit;
}

/* Key-press instructions should be obvious as such. */
kbd {
   display: inline-block;

   box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
   0 2px 0 0 rgba(255, 255, 255, 0.7) inset;

   border: 1px solid #b4b4b4;
   border-radius: 0.2em;

   background-color: #eee;
   padding: 0.12em 0.25em;

   color: #333;
   font-size: 0.85em;
   line-height: 1;
   white-space: nowrap;
   text-transform: capitalize;
}

/* Much more commonly want to ignore leading whitespace, because the tag itself will be indented */
pre {
   text-wrap-mode: wrap;
   white-space-collapse: preserve-breaks;
}

/* code lines inside a pre block can retain their interior whitespace */
pre code {
   white-space: pre-wrap;
}

/* ============================================
 *                INPUTS & FORMS
 * ============================================ */

form {
   /* shrink to a consistent size; contents will default to full width */
   width: fit-content;
}

fieldset {
   /* Space for fieldsets from other form elements increases legibility */
   margin: 0.5rem 0;

   /* space for the fieldset top and bottom border for visibility */
   padding-top: 0.5em; /* most contents in a fieldset have their own top margin */
   padding-bottom: 1em;
}

/**
 * Labels should create space from other form elements to indicate relationship of the label
 * and the input.
 *
 * Recommendation: place label text and input inside the same label tag to gain the expected behaviour of
 * selecting the input when the label is clicked.
 * Otherwise an ID on the input is required and the label must have a "for" attribute.
 */
label {
   /* better control over internal positioning */
   display: flex;

   /* Visual separation from other form elements by default for legibility & accessibility */
   margin: 0.5rem 0;

   /* checkboxes for example only make sense horizontally, so that limits the general rule here */
   flex-direction: row;

   /* flexibility for squished up situations */
   flex-wrap: wrap;

   /* Align labels and widget along central axis for clean line */
   align-items: center;

   /* avoid stretching to container width by default */
   width: fit-content;

   /* space label from input */
   gap: 0.5em;
}

label span:has(+:required):after,
fieldset[aria-required] legend:after {
   content: '*';
}

/**
 * Users can get confused when the placeholder text doesn't disappear on click, thinking
 * that it is inputted text they cannot delete.
 * Both Firefox and Chrome keep the text.
 */

/* NOTE: these are done in separate blocks because of the behaviour where
 * if any listed pseudo selector is invalid, the entire rule is ignored.
 *
 * For Chrome 6-57 & Opera 15-44 & Safari 5-10.1 ...
 */
input:focus::placeholder::-webkit-input-placeholder,
textarea:focus::placeholder::-webkit-input-placeholder {
   color: transparent;
}

/* ... Firefox 19-51 ... */
input:focus::placeholder::-moz-placeholder,
textarea:focus::placeholder::-moz-placeholder {
   color: transparent;
}

/* ... Edge 12-18 ... */
input:focus::placeholder:-ms-input-placeholder,
textarea:focus::placeholder:-ms-input-placeholder {
   color: transparent;
}

/* ... and the official pseudo-element */
input:focus::placeholder,
textarea:focus::placeholder {
   color: transparent;
}

/**
 * Text areas are x-y resizable by default, but interfaces are normally designed to expand downward, not sideways.
 *
 * Defaulting to only-vertical reduces breaking behaviour.
 */
textarea {
   resize: vertical;
}

/**
 * Chrome has essentially no padding on form fields, which decreases legibility.
 *
 * Firefox has over-designed fields (gradient, sizing), which the border-radius causes to reset to plain white.
 *
 * Fields should be at least slightly rounded-edged, for UI psychology.
 */
input,
textarea,
select {
   /* Default border to override Firefox's huge default borders and standardize */
   border: 1px inset grey;
   border-radius: 0.25rem;

   /* Visual separation from input content for legibility */
   padding: 0.25rem 0.5rem;

   /* Chrome does not apply input font family consistently */
   font-family: ui-sans-serif, system-ui, sans-serif;

   /* Firefox does not apply a consistent input font size */
   font-size: 1rem;
}

/**
 * Clickable things should indicate their clickiness via the cursor and a hover highlight
 */
button:enabled:hover,
input[type="reset"]:enabled:hover,
input[type="button"]:enabled:hover,
input[type="submit"]:enabled:hover,
input[type="radio"]:enabled:hover,
label:hover input[type="radio"]:enabled,
label:hover input[type="radio"]:enabled + span,
input[type="checkbox"]:enabled:hover,
label:hover input[type="checkbox"]:enabled,
label:hover input[type="checkbox"]:enabled + span,
select:enabled:hover,
select:enabled:focus-within, /* as of at least Firefox 91, filter rule closes the dropdown. Including focus-within fixes it  */
summary:hover {
   /* highlighting on hover provides instant feedback */
   filter: brightness(105%);
   cursor: pointer;
}

/*
 * Same clickable indicator but without highlight to prevent double highlight
 */
label:has(input[type="radio"]:enabled):hover,
label:has(input[type="checkbox"]:enabled):hover {
   cursor: pointer;
}

/**
 * Firefox uses OS focus indication, which may only be colour based (bad for colourblind)
 * Chrome uses Outlines to indicate focus.
 *
 * This standardizes and forces morphological visual focus indication.
 */
input:focus,
textarea:focus,
button:focus,
select:focus,
input[type="reset"]:focus,
input[type="button"]:focus,
input[type="submit"]:focus {
   /* remove Chrome's focus highlight outline */
   outline: none;

   /* use internal shading to indicate focus */
   box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.8) inset
}

/**
 * General button style
 */
button,
input[type="button"],
input[type="reset"],
input[type="submit"] {
   /* Center button labelling */
   display: inline-flex;
   align-items: center;
   justify-content: center;

   /**
    * Minimum size based on accessibility recommendations:
    * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button
    */
   min-width: 44px;
   min-height: 44px;

   /* basic raised appearance indicates it is separate from page content */
   box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
   border: none;
   border-radius: 0.25rem;

   /* visual separation of text from button edge and interior contents from each other for legibility. */
   padding: 0.5rem 1rem;
   gap: 0.5rem;

   background-color: buttonFace;
   color: buttonText;

   /* Some browsers have a custom smaller fontsize for inputs */
   font-size: 1rem;
}

/* draw button as depressed to indicate active interaction / movement */
button:enabled:active:hover,
input[type="button"]:enabled:active:hover,
input[type="reset"]:enabled:active:hover,
input[type="submit"]:enabled:active:hover {
   box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.4) inset;
}

/**
 * disabled elements get reduced contrast to draw less attention, and
 * a little transparency to further blend & imply a reduced tangibility
 */
*:disabled,
input[type="radio"]:disabled + span:before,
input[type="checkbox"]:disabled + span:before,
input[type="radio"]:disabled + span:after,
input[type="checkbox"]:disabled + span:after {
   opacity: 0.5;
   filter: saturate(0.5) contrast(0.8);
}

input[type="checkbox"],
input[type="radio"] {
   /* chrome adds margins which break alignment in flex centering */
   margin: 0;
}
