fc-dioxus 0.1.2

Typed, accessible Dioxus components over future-css
Documentation
/*!
 * future-css -- https://github.com/estith/future-css
 * A styling system for multi-language, multi-platform systems
 *
 * Regenerate with: npm run build
 * This file is generated; edit the sources in src/ instead.
 */
/*!
 * future-css -- checkbox, radio, and switch
 *
 * Checkboxes and radios are the native controls, tinted with `accent-color`
 * rather than rebuilt. A rebuilt checkbox has to reimplement the indeterminate
 * state, forced-colours rendering, zoom, and touch hit-testing; the native one
 * already does all four, and `accent-color` themes it from the same token as
 * every other accent surface.
 *
 *   <label class="fc-check">
 *     <input class="fc-check__input" type="checkbox" name="terms">
 *     <span class="fc-check__label">I accept the terms</span>
 *     <span class="fc-check__hint">You can withdraw at any time.</span>
 *   </label>
 *
 * The switch is the one control drawn by hand, because no native switch exists.
 * It is still a real checkbox -- `<input type="checkbox" role="switch">` -- so it
 * submits, resets, and toggles with Space like any other.
 */

@layer fc.components {
  /*
   * The whole label is the hit target, so the target size is met by the row,
   * not by the 1.125rem box.
   */
  .fc-check {
    display: grid;
    grid-template-columns: auto 1fr;
    column-gap: var(--fc-space-2);
    row-gap: var(--fc-space-0-5);
    align-items: start;
    min-block-size: var(--fc-size-target-min);
    padding-block: var(--fc-space-1);
    cursor: pointer;
    color: var(--fc-color-text-default);
  }

  .fc-check__input {
    /* Vertically centres the box on the first line of the label, however it wraps. */
    margin-block-start: calc((1lh - 1.125rem) / 2);
    margin-inline: 0;
    inline-size: 1.125rem;
    block-size: 1.125rem;
    accent-color: var(--fc-color-accent-bg);
    cursor: inherit;
  }

  .fc-check__label {
    font-size: var(--fc-font-size-sm);
    line-height: var(--fc-line-height-normal);
  }

  .fc-check__hint {
    grid-column: 2;
    font-size: var(--fc-font-size-xs);
    color: var(--fc-color-text-muted);
  }

  .fc-check:has(:disabled) {
    cursor: not-allowed;
    color: var(--fc-color-disabled-fg);
  }

  .fc-check:has([aria-invalid="true"], :user-invalid) .fc-check__label {
    color: var(--fc-color-danger-text);
  }

  /* A group of checks: a fieldset whose legend is the question being asked. */
  .fc-check-group {
    display: flex;
    flex-direction: column;
    gap: var(--fc-space-1);
  }

  .fc-check-group__legend {
    margin-block-end: var(--fc-space-1);
    font-size: var(--fc-font-size-sm);
    font-weight: var(--fc-font-weight-medium);
  }

  .fc-check-group--inline {
    flex-direction: row;
    flex-wrap: wrap;
    column-gap: var(--fc-space-5);
  }

  /* ---- Switch ---------------------------------------------------------- */

  /*
   * `--fc-switch-travel` is the distance the thumb moves. It is derived from the
   * track and thumb sizes so a size modifier only has to restate those three.
   * `--fc-switch-stretch` widens the thumb while pressed; the checked position
   * subtracts it, so the thumb grows toward the middle from either end instead
   * of running off the track.
   *
   * The off track is filled with `border-strong` rather than outlined: the fill
   * is what meets the 3:1 boundary contrast, and a light thumb sits on it. A
   * grey thumb on a grey outline -- the old drawing -- met the ratio but read
   * as disabled.
   */
  .fc-switch {
    --fc-switch-w: 2.5rem;
    --fc-switch-h: 1.375rem;
    --fc-switch-thumb: 1rem;
    --fc-switch-stretch: 0rem;
    --fc-switch-pad: calc((var(--fc-switch-h) - var(--fc-switch-thumb)) / 2 - var(--fc-border-width-thin));
    --fc-switch-travel: calc(
      var(--fc-switch-w) - var(--fc-switch-thumb) - 2 * var(--fc-switch-pad) - 2 * var(--fc-border-width-thin)
    );

    appearance: none;
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    inline-size: var(--fc-switch-w);
    block-size: var(--fc-switch-h);
    margin: 0;
    padding-inline: var(--fc-switch-pad);
    vertical-align: middle;

    background-color: var(--fc-color-border-strong);
    border: var(--fc-border-width-thin) solid var(--fc-color-border-strong);
    border-radius: var(--fc-radius-full);
    cursor: pointer;

    transition:
      background-color var(--fc-duration-normal) var(--fc-easing-standard),
      border-color var(--fc-duration-normal) var(--fc-easing-standard);
  }

  .fc-switch::before {
    content: "";
    inline-size: calc(var(--fc-switch-thumb) + var(--fc-switch-stretch));
    block-size: var(--fc-switch-thumb);
    border-radius: var(--fc-radius-full);
    background-color: var(--fc-color-bg-thumb);
    box-shadow: var(--fc-shadow-raised);
    /*
     * `translate` is physical and has no logical form; the RTL branch below
     * reverses it, the same treatment the progress sweep needed. The spring
     * curve gives the thumb a little weight as it lands.
     */
    transition:
      translate var(--fc-duration-slow) var(--fc-easing-spring),
      inline-size var(--fc-duration-fast) var(--fc-easing-standard),
      background-color var(--fc-duration-normal) var(--fc-easing-standard);
  }

  .fc-switch:checked {
    background-color: var(--fc-color-accent-bg);
    border-color: var(--fc-color-accent-border);
  }

  @media (hover: hover) {
    .fc-switch:hover {
      background-color: var(--fc-color-border-interactive);
      border-color: var(--fc-color-border-interactive);
    }

    .fc-switch:checked:hover {
      background-color: var(--fc-color-accent-bg-hover);
      border-color: var(--fc-color-accent-bg-hover);
    }
  }

  .fc-switch:checked::before {
    background-color: var(--fc-color-text-on-accent);
    translate: calc(var(--fc-switch-travel) - var(--fc-switch-stretch)) 0;
  }

  .fc-switch:checked:dir(rtl)::before {
    translate: calc((var(--fc-switch-travel) - var(--fc-switch-stretch)) * -1) 0;
  }

  /* Pressing stretches the thumb, so the control feels like it is being pushed. */
  .fc-switch:active:not(:disabled) {
    --fc-switch-stretch: calc(var(--fc-switch-thumb) / 4);
  }

  .fc-switch:disabled {
    cursor: not-allowed;
    opacity: 1;
  }

  .fc-switch--sm {
    --fc-switch-w: 2rem;
    --fc-switch-h: 1.125rem;
    --fc-switch-thumb: 0.75rem;
  }

  .fc-switch--lg {
    --fc-switch-w: 3rem;
    --fc-switch-h: 1.625rem;
    --fc-switch-thumb: 1.25rem;
  }

  /*
   * A switch row: label first, switch at the inline end, which is the settings
   * pattern people already know. Laid out by flex so it mirrors in RTL with no
   * extra rule.
   */
  .fc-switch-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--fc-space-4);
    min-block-size: var(--fc-size-target-comfortable);
    cursor: pointer;
  }

  .fc-switch-row__text {
    display: flex;
    flex-direction: column;
    gap: var(--fc-space-0-5);
    min-inline-size: 0;
  }

  .fc-switch-row__label {
    font-size: var(--fc-font-size-sm);
    font-weight: var(--fc-font-weight-medium);
  }

  .fc-switch-row__hint {
    font-size: var(--fc-font-size-xs);
    color: var(--fc-color-text-muted);
  }

  .fc-switch-row:has(:disabled) {
    cursor: not-allowed;
    color: var(--fc-color-disabled-fg);
  }

  /* ---- Forced colors --------------------------------------------------- */

  /*
   * The track and thumb are backgrounds, which forced colours discard. The thumb
   * is restated as a system colour and the checked state as Highlight, so on/off
   * is still legible -- and position, not colour, remains the primary cue.
   */
  @media (forced-colors: active) {
    .fc-switch {
      border-color: ButtonText;
      background-color: ButtonFace;
    }

    .fc-switch::before {
      background-color: ButtonText;
    }

    .fc-switch:checked {
      background-color: Highlight;
      border-color: Highlight;
    }

    .fc-switch:checked::before {
      background-color: HighlightText;
    }

    .fc-switch:disabled {
      border-color: GrayText;
    }

    .fc-switch:disabled::before {
      background-color: GrayText;
    }
  }
}