//
// Copyright 2016 Google Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

// stylelint-disable selector-class-pattern --
// Selector '.mdc-*' should only be used in this project.

@use 'sass:map';
@use 'sass:math';
@use '../animation/functions' as functions2;
@use '../density/functions' as density-functions;
@use '../density/variables' as density-variables;
@use '../feature-targeting/feature-targeting';
@use '../ripple/ripple';
@use '../ripple/ripple-theme';
@use '../theme/color-custom-properties';
@use '../theme/custom-properties';
@use '../theme/keys';
@use '../theme/theme';
@use '../theme/theme-color';
@use '../theme/shadow-dom';
@use '../touch-target/touch-target';
@use './checkbox-custom-properties';

$baseline-theme-color: secondary !default;
$mark-color: theme-color.prop-value(on-secondary) !default;
$border-color: rgba(theme-color.prop-value(on-surface), 0.54) !default;
$disabled-color: rgba(theme-color.prop-value(on-surface), 0.38) !default;

$ripple-size: 40px !default;
$icon-size: 18px !default;
$mark-stroke-size: math.div(2, 15) * $icon-size !default;
$border-width: 2px !default;
$transition-duration: 90ms !default;
$item-spacing: 4px !default;
$focus-indicator-opacity: map.get(
  ripple-theme.$dark-ink-opacities,
  focus
) !default;

$minimum-size: 28px !default;
$maximum-size: $ripple-size !default;
$density-scale: density-variables.$default-scale !default;
$density-config: (
  size: (
    minimum: $minimum-size,
    default: $ripple-size,
    maximum: $maximum-size,
  ),
) !default;

$ripple-target: '.mdc-checkbox__ripple';
$custom-property-prefix: 'checkbox';

// TODO(b/188417756): State layer (ripple) size token is missing including
//     `state-layer-size`.
// TODO(b/188529841): `selected-checkmark-color` and `disabled-selected-checkmark-color` does not exist in tokens.
$light-theme: (
  disabled-selected-checkmark-color: $mark-color,
  disabled-selected-icon-color: $disabled-color,
  disabled-selected-icon-opacity: null,
  disabled-unselected-icon-color: $disabled-color,
  disabled-unselected-icon-opacity: null,
  selected-checkmark-color: $mark-color,
  selected-focus-icon-color: $baseline-theme-color,
  selected-focus-state-layer-color: theme-color.$on-surface,
  selected-focus-state-layer-opacity: 0.12,
  selected-hover-icon-color: $baseline-theme-color,
  selected-hover-state-layer-color: $baseline-theme-color,
  selected-hover-state-layer-opacity:
    map.get(ripple-theme.$dark-ink-opacities, hover),
  selected-icon-color: $baseline-theme-color,
  selected-pressed-icon-color: $baseline-theme-color,
  selected-pressed-state-layer-color: theme-color.$on-surface,
  selected-pressed-state-layer-opacity:
    map.get(ripple-theme.$dark-ink-opacities, pressed),
  state-layer-size: $ripple-size,
  unselected-focus-icon-color: $baseline-theme-color,
  unselected-focus-state-layer-color: theme-color.$on-surface,
  unselected-focus-state-layer-opacity:
    map.get(ripple-theme.$dark-ink-opacities, focus),
  unselected-hover-icon-color: $baseline-theme-color,
  unselected-hover-state-layer-color: theme-color.$on-surface,
  unselected-hover-state-layer-opacity:
    map.get(ripple-theme.$dark-ink-opacities, hover),
  unselected-icon-color: $border-color,
  unselected-pressed-icon-color: $border-color,
  unselected-pressed-state-layer-color: theme-color.$on-surface,
  unselected-pressed-state-layer-opacity:
    map.get(ripple-theme.$dark-ink-opacities, pressed),
);

$forced-colors-theme: (
  disabled-selected-checkmark-color: ButtonFace,
  disabled-selected-icon-color: GrayText,
  disabled-selected-icon-opacity: 1,
  disabled-unselected-icon-color: GrayText,
  disabled-unselected-icon-opacity: 1,
  selected-checkmark-color: ButtonText,
);

@mixin theme($theme) {
  @include theme.validate-theme-keys($light-theme, $theme);
  @include keys.declare-custom-properties(
    $theme,
    $prefix: $custom-property-prefix
  );
}

@mixin theme-styles($theme) {
  @include theme.validate-theme-keys($light-theme, $theme);
  $theme: keys.create-theme-properties(
    $theme,
    $prefix: $custom-property-prefix
  );

  @include disabled-container-colors(
    $unmarked-stroke-color: map.get($theme, disabled-unselected-icon-color),
    $marked-fill-color: map.get($theme, disabled-selected-icon-color)
  );

  @include ink-color(map.get($theme, selected-checkmark-color));
  @include disabled-ink-color(
    map.get($theme, disabled-selected-checkmark-color)
  );

  @include _icon-color(
    map.get($theme, unselected-icon-color),
    map.get($theme, selected-icon-color)
  );

  &:hover {
    @include _icon-color(
      map.get($theme, unselected-hover-icon-color),
      map.get($theme, selected-hover-icon-color)
    );
  }

  @include ripple-theme.focus() {
    @include _icon-color(
      map.get($theme, unselected-focus-icon-color),
      map.get($theme, selected-focus-icon-color)
    );
  }

  @include ripple-theme.active() {
    @include _icon-color(
      map.get($theme, unselected-pressed-icon-color),
      map.get($theme, selected-pressed-icon-color)
    );
  }

  @include ripple-color(
    $color: map.get($theme, unselected-hover-state-layer-color),
    $opacity-map: (
      hover: map.get($theme, unselected-hover-state-layer-opacity),
      focus: map.get($theme, unselected-focus-state-layer-opacity),
      press: map.get($theme, unselected-pressed-state-layer-opacity),
    )
  );

  @include focus-indicator-color(
    $color: map.get($theme, selected-hover-state-layer-color),
    $opacity-map: (
      hover: map.get($theme, selected-hover-state-layer-opacity),
      focus: map.get($theme, selected-focus-state-layer-opacity),
      press: map.get($theme, selected-pressed-state-layer-opacity),
    )
  );

  @include ripple-size(map.get($theme, state-layer-size));
  // Set touch target to ripple size.
  @include touch-target(
    map.get($theme, state-layer-size),
    map.get($theme, state-layer-size)
  );
}

$light-theme-deprecated: (
  density-scale: 0,
  checkmark-color: $mark-color,
  container-checked-color: $baseline-theme-color,
  container-checked-hover-color: null,
  container-disabled-color: $disabled-color,
  outline-color: $border-color,
  outline-hover-color: null,
  ripple-color: theme-color.$on-surface,
  ripple-opacity: ripple-theme.$dark-ink-opacities,
  ripple-checked-color: $baseline-theme-color,
  ripple-checked-opacity: ripple-theme.$dark-ink-opacities,
);

/// Sets theme to checkbox based on provided theme configuration.
/// Only emits theme related styles.
/// @param {Map} $theme - Theme configuration to use for theming checkbox.
@mixin theme-deprecated($theme, $query: feature-targeting.all()) {
  @include theme.validate-theme($light-theme-deprecated, $theme);

  $ripple-color: map.get($theme, ripple-color);
  $ripple-opacity: map.get($theme, ripple-opacity);
  @if $ripple-opacity == null {
    $ripple-opacity: ();
  }
  @if $ripple-color {
    @include ripple-color(
      $color: $ripple-color,
      $opacity-map: $ripple-opacity,
      $query: $query
    );
  }

  $ripple-checked-color: map.get($theme, ripple-checked-color);
  $ripple-checked-opacity: map.get($theme, ripple-checked-opacity);
  @if $ripple-checked-opacity == null {
    $ripple-checked-opacity: ();
  }
  @if $ripple-checked-color {
    @include focus-indicator-color(
      $color: $ripple-checked-color,
      $opacity-map: $ripple-checked-opacity,
      $query: $query
    );
  }

  $density-scale: map.get($theme, density-scale);
  @if $density-scale != null {
    @include density($density-scale: $density-scale, $query: $query);
  }

  $outline-color: map.get($theme, outline-color);
  $container-checked-color: map.get($theme, container-checked-color);
  @if (
    ($outline-color and not $container-checked-color) or
      (not $outline-color and $container-checked-color)
  ) {
    @error 'Both `outline-color` and `container-checked-color` keys should be provided.';
  }
  @if ($outline-color and $container-checked-color) {
    @include container-colors(
      $unmarked-stroke-color: $outline-color,
      $marked-stroke-color: $container-checked-color,
      $marked-fill-color: $container-checked-color,
      $query: $query
    );
  }

  $outline-hover-color: map.get($theme, outline-hover-color);
  $container-checked-hover-color: map.get(
    $theme,
    container-checked-hover-color
  );
  @if (
    ($outline-hover-color and not $container-checked-hover-color) or
      (not $outline-hover-color and $container-checked-hover-color)
  ) {
    @error 'Both `outline-hover-color` and `container-checked-hover-color` keys should be provided.';
  }
  @if ($outline-hover-color and $container-checked-hover-color) {
    @include ripple-theme.states-selector() {
      @include container-colors(
        $unmarked-stroke-color: $outline-hover-color,
        $marked-stroke-color: $container-checked-hover-color,
        $marked-fill-color: $container-checked-hover-color,
        $query: $query
      );
    }
  }

  $container-disabled-color: map.get($theme, container-disabled-color);
  @if $container-disabled-color {
    @include disabled-container-colors(
      $unmarked-stroke-color: $container-disabled-color,
      $marked-fill-color: $container-disabled-color,
      $query: $query
    );
  }

  $checkmark-color: map.get($theme, checkmark-color);
  @if $checkmark-color {
    @include ink-color($checkmark-color, $query: $query);
    @include disabled-ink-color($checkmark-color, $query: $query);
  }
}

///
/// @param {Number | String} $density-scale - Density scale value for component.
///     Supported density scale values `-3`, `-2`, `-1`, `0`.
/// @return Returns ripple size of checkbox for given density scale.
///
@function get-ripple-size($density-scale) {
  @return density-functions.prop-value(
    $density-config: $density-config,
    $density-scale: $density-scale,
    $property-name: size
  );
}

///
/// Sets density scale for checkbox.
///
/// @param {Number | String} $density-scale - Density scale value for component. Supported density scale values
///     `-3`, `-2`, `-1`, `0`.
///
@mixin density($density-scale, $query: feature-targeting.all()) {
  $size: get-ripple-size($density-scale);

  @include ripple-size($size, $query: $query);
  @include touch-target($size, $ripple-size: $size, $query: $query);
}

/// Sets ripple size of checkbox and optionally set touch target size which can
/// be more than the size of ripple.
/// @param {Number} $ripple-size - Visual ripple size of checkbox.
@mixin ripple-size($ripple-size, $query: feature-targeting.all()) {
  $feat-structure: feature-targeting.create-target($query, structure);

  @if $ripple-size and not custom-properties.is-custom-prop($ripple-size) {
    $ripple-size: custom-properties.create(
      checkbox-custom-properties.$ripple-size,
      $ripple-size
    );
  }
  $checkbox-padding: 'calc((_ripple-size - _icon-size) / 2)';
  $replace: (
    _ripple-size: $ripple-size,
    _icon-size: $icon-size,
  );

  @include feature-targeting.targets($feat-structure) {
    @include theme.property(padding, $checkbox-padding, $replace: $replace);
  }

  .mdc-checkbox__background {
    @include feature-targeting.targets($feat-structure) {
      @include theme.property(top, $checkbox-padding, $replace: $replace);
      @include theme.property(left, $checkbox-padding, $replace: $replace);
    }
  }
}

/// Sets the touch target size and appropriate margin to accommodate the
/// touch target.
/// @param {Number} $touch-target-size Size of touch target (Native input) in `px`.
/// @param {Number} $ripple-size Size of ripple in `px`.
@mixin touch-target(
  $touch-target-size,
  $ripple-size,
  $query: feature-targeting.all()
) {
  $feat-structure: feature-targeting.create-target($query, structure);

  @if $touch-target-size {
    @if not custom-properties.is-custom-prop($touch-target-size) {
      $touch-target-size: custom-properties.create(
        checkbox-custom-properties.$touch-target-size,
        $touch-target-size
      );
    }

    $margin: 'calc((_touch-target-size - _ripple-size) / 2)';
    $replace: (
      _touch-target-size: $touch-target-size,
      _ripple-size: $ripple-size,
    );

    @include feature-targeting.targets($feat-structure) {
      @include theme.property(margin, $margin, $replace: $replace);
    }

    $offset: 'calc((_ripple-size - _touch-target-size) / 2)';

    @include feature-targeting.targets($feat-structure) {
      .mdc-checkbox__native-control {
        @include theme.property(top, $offset, $replace: $replace);
        @include theme.property(right, $offset, $replace: $replace);
        @include theme.property(left, $offset, $replace: $replace);
        @include theme.property(width, $touch-target-size);
        @include theme.property(height, $touch-target-size);
      }
    }
  }
}

@mixin _icon-color($unselected-color, $selected-color) {
  @if $unselected-color and $selected-color {
    @include container-colors(
      $unmarked-stroke-color: $unselected-color,
      $marked-stroke-color: $selected-color,
      $marked-fill-color: $selected-color
    );
  } @else if $unselected-color or $selected-color {
    @error 'Both unselected and selected icon colors should be provided.';
  }
}

///
/// Sets stroke & fill colors for both marked and unmarked state of enabled checkbox.
/// Set $generate-keyframes to false to prevent the mixin from generating @keyframes
/// @param {Color} $unmarked-stroke-color - The desired stroke color for the unmarked state
/// @param {Color} $unmarked-fill-color - The desired fill color for the unmarked state
/// @param {Color} $marked-stroke-color - The desired stroke color for the marked state
/// @param {Color} $marked-fill-color - The desired fill color for the marked state
/// @param {Boolean} $generate-keyframes [true] - Whether animation keyframes should be generated
///
@mixin container-colors(
  $unmarked-stroke-color: $border-color,
  $unmarked-fill-color: transparent,
  $marked-stroke-color: $baseline-theme-color,
  $marked-fill-color: $baseline-theme-color,
  $generate-keyframes: true,
  $query: feature-targeting.all()
) {
  $feat-animation: feature-targeting.create-target($query, animation);
  $feat-color: feature-targeting.create-target($query, color);

  // Unchecked colors
  @if (
    $unmarked-stroke-color and not
      custom-properties.is-custom-prop($unmarked-stroke-color)
  ) {
    $unmarked-stroke-color: custom-properties.create(
      checkbox-custom-properties.$unchecked-color,
      theme-color.prop-value($unmarked-stroke-color)
    );
  }

  @include if-unmarked-enabled_ {
    @include container-colors_(
      $unmarked-stroke-color,
      $unmarked-fill-color,
      $query: $query
    );
  }

  // Checked colors
  @if (
    $marked-stroke-color and not
      custom-properties.is-custom-prop($marked-stroke-color)
  ) {
    $marked-stroke-color: custom-properties.create(
      checkbox-custom-properties.$checked-color,
      custom-properties.create(
        color-custom-properties.$secondary,
        theme-color.prop-value($marked-stroke-color)
      )
    );
  }
  @if (
    $marked-fill-color and not
      custom-properties.is-custom-prop($marked-fill-color)
  ) {
    $marked-fill-color: custom-properties.create(
      checkbox-custom-properties.$checked-color,
      custom-properties.create(
        color-custom-properties.$secondary,
        theme-color.prop-value($marked-fill-color)
      )
    );
  }

  @include if-marked-enabled_ {
    @include container-colors_(
      $marked-stroke-color,
      $marked-fill-color,
      $query: $query
    );
  }

  @if $generate-keyframes and
    $unmarked-stroke-color and
    $marked-stroke-color and
    $unmarked-fill-color and
    $marked-fill-color
  {
    $uid: theme-color.color-hash($unmarked-stroke-color) +
      theme-color.color-hash($marked-stroke-color) +
      theme-color.color-hash($unmarked-fill-color) +
      theme-color.color-hash($marked-fill-color);

    $anim-selector: if(&, '&.mdc-checkbox--anim', '.mdc-checkbox--anim');

    @include feature-targeting.targets($feat-animation, $feat-color) {
      @include container-keyframes_(
        $from-stroke-color: $unmarked-stroke-color,
        $to-stroke-color: $marked-stroke-color,
        $from-fill-color: $unmarked-fill-color,
        $to-fill-color: $marked-fill-color,
        $uid: #{$uid}
      );
    }

    #{$anim-selector} {
      &-unchecked-checked,
      &-unchecked-indeterminate {
        .mdc-checkbox__native-control:enabled ~ .mdc-checkbox__background {
          @include feature-targeting.targets($feat-animation) {
            animation-name: mdc-checkbox-fade-in-background-#{$uid};
          }
        }
      }

      &-checked-unchecked,
      &-indeterminate-unchecked {
        .mdc-checkbox__native-control:enabled ~ .mdc-checkbox__background {
          @include feature-targeting.targets($feat-animation) {
            animation-name: mdc-checkbox-fade-out-background-#{$uid};
          }
        }
      }
    }
  }
}

///
/// Sets stroke & fill colors for both marked and unmarked state of disabled checkbox.
/// @param {Color} $unmarked-stroke-color - The desired stroke color for the unmarked state
/// @param {Color} $unmarked-fill-color - The desired fill color for the unmarked state
/// @param {Color} $marked-stroke-color - The desired stroke color for the marked state
/// @param {Color} $marked-fill-color - The desired fill color for the marked state
///
@mixin disabled-container-colors(
  $unmarked-stroke-color: $disabled-color,
  $unmarked-fill-color: transparent,
  $marked-stroke-color: transparent,
  $marked-fill-color: $disabled-color,
  $query: feature-targeting.all()
) {
  @if (
    $unmarked-stroke-color and not
      custom-properties.is-custom-prop($unmarked-stroke-color)
  ) {
    $unmarked-stroke-color: custom-properties.create(
      checkbox-custom-properties.$disabled-color,
      theme-color.prop-value($unmarked-stroke-color)
    );
  }

  @if $unmarked-stroke-color == null {
    $unmarked-fill-color: null;
  }

  @include if-unmarked-disabled_ {
    @include container-colors_(
      $unmarked-stroke-color,
      $unmarked-fill-color,
      $query: $query
    );
  }

  @if (
    $marked-fill-color and not
      custom-properties.is-custom-prop($marked-fill-color)
  ) {
    $marked-fill-color: custom-properties.create(
      checkbox-custom-properties.$disabled-color,
      theme-color.prop-value($marked-fill-color)
    );
  }

  @if $marked-fill-color and
    custom-properties.get-fallback($marked-fill-color) ==
    GrayText
  {
    // Transparent appears white in HCM
    $marked-stroke-color: GrayText;
  }

  @if $marked-fill-color == null {
    $marked-stroke-color: null;
  }

  @include if-marked-disabled_ {
    @include container-colors_(
      $marked-stroke-color,
      $marked-fill-color,
      $query: $query
    );
  }
}

///
/// Sets the ink color of the checked and indeterminate icons for an enabled checkbox
/// @param {Color} $color - The desired ink color in enabled state
///
@mixin ink-color($color, $query: feature-targeting.all()) {
  @if ($color and not custom-properties.is-custom-prop($color)) {
    $color: custom-properties.create(
      checkbox-custom-properties.$ink-color,
      $color
    );
  }

  @include if-enabled_ {
    @include ink-color_($color, $query: $query);
  }
}

///
/// Sets the ink color of the checked and indeterminate icons for a disabled checkbox
/// @param {Color} $color - The desired ink color in disabled state
///
@mixin disabled-ink-color($color, $query: feature-targeting.all()) {
  @if ($color and not custom-properties.is-custom-prop($color)) {
    $color: custom-properties.create(
      checkbox-custom-properties.$ink-color,
      $color
    );
  }

  @include if-disabled_ {
    @include ink-color_($color, $query: $query);
  }
}

/// Sets ripple color when checkbox is not in checked state.
@mixin ripple-color(
  $color,
  $opacity-map: null,
  $query: feature-targeting.all()
) {
  @include ripple-theme.states(
    $color: $color,
    $opacity-map: $opacity-map,
    $query: $query,
    $ripple-target: $ripple-target
  );
}

/// Sets focus indicator color when checkbox is in checked state.
@mixin focus-indicator-color(
  $color,
  $opacity-map: null,
  $query: feature-targeting.all()
) {
  $feat-color: feature-targeting.create-target($query, color);

  &.mdc-checkbox--selected {
    @include ripple-theme.states(
      $color: $color,
      $opacity-map: $opacity-map,
      $query: $query,
      $ripple-target: $ripple-target
    );
  }

  &.mdc-ripple-upgraded--background-focused.mdc-checkbox--selected {
    @include ripple-theme.states-base-color(
      $color: $color,
      $query: $query,
      $ripple-target: $ripple-target
    );
  }
}

//
// Private
//

///
/// Helps select the checkbox background only when its native control is in
/// enabled state.
/// @access private
///
@mixin if-enabled_ {
  .mdc-checkbox__native-control:enabled ~ {
    @content;
  }
}

///
/// Helps select the checkbox background only when its native control is in
/// disabled state.
/// @access private
///
@mixin if-disabled_ {
  .mdc-checkbox__native-control:disabled ~ {
    @content;
  }
}

///
/// Helps select the checkbox background only when its native control is in
/// unmarked & enabled state.
/// @access private
///
@mixin if-unmarked-enabled_ {
  .mdc-checkbox__native-control:enabled:not(:checked):not(:indeterminate):not([data-indeterminate='true'])
    ~ {
    @content;
  }
}

///
/// Helps select the checkbox background only when its native control is in
/// unmarked & disabled state.
/// @access private
///
@mixin if-unmarked-disabled_ {
  // Note: we must use `[disabled]` instead of `:disabled` below because Edge does not always recalculate the style
  // property when the `:disabled` pseudo-class is followed by a sibling combinator. See:
  // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/11295231/
  .mdc-checkbox__native-control[disabled]:not(:checked):not(:indeterminate):not([data-indeterminate='true'])
    ~ {
    @content;
  }
}

///
/// Helps select the checkbox background only when its native control is in
/// marked & enabled state.
/// @access private
///
@mixin if-marked-enabled_ {
  .mdc-checkbox__native-control:enabled:checked,
  .mdc-checkbox__native-control:enabled:indeterminate,
  .mdc-checkbox__native-control[data-indeterminate='true']:enabled {
    ~ {
      @content;
    }
  }
}

///
/// Helps select the checkbox background only when its native control is in
/// marked & disabled state.
/// @access private
///
@mixin if-marked-disabled_ {
  // Note: we must use `[disabled]` instead of `:disabled` below because Edge does not always recalculate the style
  // property when the `:disabled` pseudo-class is followed by a sibling combinator. See:
  // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/11295231/
  .mdc-checkbox__native-control[disabled]:checked,
  .mdc-checkbox__native-control[disabled]:indeterminate,
  .mdc-checkbox__native-control[data-indeterminate='true'][disabled] {
    ~ {
      @content;
    }
  }
}

///
/// Sets the stroke & fill colors for the checkbox.
/// This mixin should be wrapped in a mixin that qualifies state such as
/// `mdc-checkbox-if-unmarked-enabled_`.
/// @access private
///
@mixin container-colors_(
  $stroke-color,
  $fill-color,
  $query: feature-targeting.all()
) {
  $feat-color: feature-targeting.create-target($query, color);

  .mdc-checkbox__background {
    @include feature-targeting.targets($feat-color) {
      @include theme.property(border-color, $stroke-color);
      @include theme.property(background-color, $fill-color);
    }
  }
}

///
/// Sets the ink color of the checked and indeterminate icons for a checkbox.
/// This mixin should be wrapped in a mixin that qualifies state such as
/// `mdc-checkbox-if-unmarked_`.
/// @access private
///
@mixin ink-color_($color, $query: feature-targeting.all()) {
  $feat-color: feature-targeting.create-target($query, color);

  .mdc-checkbox__background {
    .mdc-checkbox__checkmark {
      @include feature-targeting.targets($feat-color) {
        @include theme.property(color, $color);
      }
    }

    .mdc-checkbox__mixedmark {
      @include feature-targeting.targets($feat-color) {
        @include theme.property(border-color, $color);
      }
    }
  }
}

@mixin container-keyframes_(
  $from-stroke-color,
  $to-stroke-color,
  $from-fill-color,
  $to-fill-color,
  $uid
) {
  @keyframes mdc-checkbox-fade-in-background-#{$uid} {
    0% {
      @include theme.property(border-color, $from-stroke-color);
      @include theme.property(background-color, $from-fill-color);
    }

    50% {
      @include theme.property(border-color, $to-stroke-color);
      @include theme.property(background-color, $to-fill-color);
    }
  }

  @keyframes mdc-checkbox-fade-out-background-#{$uid} {
    0%,
    80% {
      @include theme.property(border-color, $to-stroke-color);
      @include theme.property(background-color, $to-fill-color);
    }

    100% {
      @include theme.property(border-color, $from-stroke-color);
      @include theme.property(background-color, $from-fill-color);
    }
  }
}
