/* Copyright (c) Fortanix, Inc.
|* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of
|* the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */

@use 'sass:meta';

@mixin styles {
  @layer lib, custom; // Sublayers within `@layer reset` for more fine-grained control
  
  @layer lib {
    // Note: any top-level `&` in the CSS will be transpiled to `:scope`. Sass assumes that the CSS we load is
    // top-level CSS, where `&` does indeed mean `:scope`. So any `:root`/`html` selectors will need to be done in Sass.
    // See `global.scss` for these `:root` styles.
    @include meta.load-css('../lib/reset.css');
  }
  
  // Custom reset overrides
  // References:
  // - [1] https://www.youtube.com/watch?v=cCAtD_BAHNw ("Under the radar CSS features for your CSS reset")
  @layer custom {
    //* { min-inline-size: 0; } // Reset flexbox item min-width? https://github.com/elad2412/the-new-css-reset/issues/45
    
    // :focus-visible {
    //   /* outline: calc(bk.$size-1 / 2) solid color-contrast(var(--bk-page-color-background) vs white, black); */
    //   /*
    //   Note:
    //     - The following should be overridable by components (e.g. `outline-offset` might not always look good)
    //     - Focusable elements should never use `outline` for any other purpose than focus outline
    //     - Use `px` rather than `rem` because these should not scale dynamically
    //   */
    //   /* outline: 3px solid var(--bk-focus-outline-color); */
    //   /* outline-offset: 3px; */
    //   /* border-radius: 3px; */
    // }
    
    @media (prefers-reduced-motion: no-preference) {
      // Source: [1]
      :has(:target) {
        scroll-behavior: smooth;
        scroll-padding-top: 1.5lh;
      }
    }
    
    [hidden] {
      // The `reset.css` lib does this already but without the `!important`, which means it will get overridden easily
      // if a `display` is set in a higher layer. There should be no reason to override `display: none` on `[hidden]`.
      // stylelint-disable-next-line declaration-no-important
      display: none !important;
    }
    
    p {
      overflow-wrap: break-word; // Prevent long words causing overflow
    }
    
    button {
      cursor: pointer;
      user-select: none;
      
      &:disabled {
        cursor: not-allowed;
      }
    }
    
    label {
      cursor: pointer;
      user-select: none;
    }
    
    /*
    input, select, textarea {
      -webkit-appearance: revert;
      appearance: revert;
    }
    */
    
    /*
    By default, `<fieldset>` elements have some peculiar default browser styling. In particular, `<legend>` within a
    `<fieldset>` is styled on top of the border. Unsetting this default styling is not super trivial, see references:
    - https://github.com/whatwg/html/issues/3912
    - https://thatemil.com/blog/2015/01/03/reset-your-fieldset/ (2015, most of this is outdated/no longer necessary)
    - https://stackoverflow.com/a/20705034/233884 (solution with only `float`, seems to work fine in modern browsers)
    */
    fieldset {
      legend {
        float: inline-start;
        inline-size: 100%;
        
        // Clear the `float`
        + * {
          clear: both;
        }
      }
    }
    
    // Note: we assume that all styling for dialog (including `:modal` and `:backdrop`) has been hard reset.
    // `the-new-css-reset` has an exception for `dialog:modal`, but we don't want this exception, so here we unset all.
    dialog, dialog:modal, dialog::backdrop {
      all: unset;
      display: revert;
      box-sizing: border-box;
    }
    
    // Re-apply a few sensible defaults for dialogs. For reference, see the default user agent styling rules here:
    // https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/html/resources/html.css
    dialog {
      // Issue: if the parent has `user-select: none` (e.g. button), then the dialog will inherit this by default.
      // In the future, if `contain: user-select` is implemented, we could use that instead.
      &::backdrop {
        user-select: none; // Without this, clicking on the backdrop selects the dialog text in Firefox
      }
      // Note: doing this on the parent `dialog` doesn't seem to work in Chrome, the `::backdrop` takes precedence
      > * {
        user-select: text;
      }
      
      // Do not position absolutely by default (make that an opt in for modal dialogs only)
      position: static;
      
      // Use a variable (`--display`) to allow higher level styling to override the visible display type (e.g. to use
      // `flex` instead of `block`), without having to reimplement all the `display: none` logic.
      display: var(--display, block);
      
      &:not([popover]) {
        &:not([open]) { // Once supported, use `:open` for this instead of `[open]`
          display: none;
        }
      }
      
      // Take note of this Chromium issue: https://issues.chromium.org/issues/388538944
      // In summary, `<dialog open popover>` is buggy in Chromium when setting `open` manually or using `show()` rather
      // than `showModal()`. If we want to use a `<dialog>` as a popover, make sure to use `showModal()` only.
      
      &[popover] {
        &:not(:popover-open) {
          display: none;
        }
      }
    }
    /*
    Note: the `:modal` pseudo-class applies when the modal is in the top layer *and* currently open. That means that it
    stops applying during an exit transition, which will break the styling. The upcoming `:open` pseudo-class doesn't
    resolve this either. Chrome internally has an `:-internal-dialog-in-top-layer` pseudo-class for this, but it's not
    exposed.
    
    We can work around this by applying the modal styling on `dialog` regardless of `:modal`. However, that breaks
    regular (non-modal) styling. It is essentially impossible to generically style dialog elements without knowing
    whether it's going to be used as a modal or not, if we also want exit transitions.
    
    This reset assumes you do *not* apply an exit transition, by default. If you do want an exit transition, you can
    set `aria-model="true"` to opt in. Or, you can manually apply the `position: fixed` (etc.) styling on the base
    `dialog` yourself.
    
    - https://github.com/w3c/csswg-drafts/issues/9912 - "User-agent styles for top layer transitions"
    - https://github.com/whatwg/html/pull/9387#issuecomment-1599722425
    - https://issues.chromium.org/issues/40270744 - `:-internal-dialog-in-top-layer`
    */
    dialog[aria-modal='true'], dialog:modal {
      position: fixed;
      inset: 0;
      
      margin: auto; // In the future, could use `place-self: center` instead
      inline-size: fit-content;
      block-size: fit-content;
      
      &::backdrop {
        position: fixed;
        inset: 0;
        background: rgb(0 0 0 / 20%);
      }
    }
  }
}
