_checkbox-theme.scss 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836
  1. //
  2. // Copyright 2016 Google Inc.
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. // stylelint-disable selector-class-pattern --
  23. // Selector '.mdc-*' should only be used in this project.
  24. @use 'sass:map';
  25. @use 'sass:math';
  26. @use '@material/animation/functions' as functions2;
  27. @use '@material/density/functions' as density-functions;
  28. @use '@material/density/variables' as density-variables;
  29. @use '@material/feature-targeting/feature-targeting';
  30. @use '@material/ripple/ripple';
  31. @use '@material/ripple/ripple-theme';
  32. @use '@material/theme/color-custom-properties';
  33. @use '@material/theme/custom-properties';
  34. @use '@material/theme/keys';
  35. @use '@material/theme/theme';
  36. @use '@material/theme/theme-color';
  37. @use '@material/theme/shadow-dom';
  38. @use '@material/touch-target/touch-target';
  39. @use './checkbox-custom-properties';
  40. $baseline-theme-color: secondary !default;
  41. $mark-color: theme-color.prop-value(on-secondary) !default;
  42. $border-color: rgba(theme-color.prop-value(on-surface), 0.54) !default;
  43. $disabled-color: rgba(theme-color.prop-value(on-surface), 0.38) !default;
  44. $ripple-size: 40px !default;
  45. $icon-size: 18px !default;
  46. $mark-stroke-size: math.div(2, 15) * $icon-size !default;
  47. $border-width: 2px !default;
  48. $transition-duration: 90ms !default;
  49. $item-spacing: 4px !default;
  50. $focus-indicator-opacity: map.get(
  51. ripple-theme.$dark-ink-opacities,
  52. focus
  53. ) !default;
  54. $minimum-size: 28px !default;
  55. $maximum-size: $ripple-size !default;
  56. $density-scale: density-variables.$default-scale !default;
  57. $density-config: (
  58. size: (
  59. minimum: $minimum-size,
  60. default: $ripple-size,
  61. maximum: $maximum-size,
  62. ),
  63. ) !default;
  64. $ripple-target: '.mdc-checkbox__ripple';
  65. $custom-property-prefix: 'checkbox';
  66. // TODO(b/188417756): State layer (ripple) size token is missing including
  67. // `state-layer-size`.
  68. // TODO(b/188529841): `selected-checkmark-color` and `disabled-selected-checkmark-color` does not exist in tokens.
  69. $light-theme: (
  70. disabled-selected-checkmark-color: $mark-color,
  71. disabled-selected-icon-color: $disabled-color,
  72. disabled-selected-icon-opacity: null,
  73. disabled-unselected-icon-color: $disabled-color,
  74. disabled-unselected-icon-opacity: null,
  75. selected-checkmark-color: $mark-color,
  76. selected-focus-icon-color: $baseline-theme-color,
  77. selected-focus-state-layer-color: theme-color.$on-surface,
  78. selected-focus-state-layer-opacity: 0.12,
  79. selected-hover-icon-color: $baseline-theme-color,
  80. selected-hover-state-layer-color: $baseline-theme-color,
  81. selected-hover-state-layer-opacity:
  82. map.get(ripple-theme.$dark-ink-opacities, hover),
  83. selected-icon-color: $baseline-theme-color,
  84. selected-pressed-icon-color: $baseline-theme-color,
  85. selected-pressed-state-layer-color: theme-color.$on-surface,
  86. selected-pressed-state-layer-opacity:
  87. map.get(ripple-theme.$dark-ink-opacities, pressed),
  88. state-layer-size: $ripple-size,
  89. unselected-focus-icon-color: $baseline-theme-color,
  90. unselected-focus-state-layer-color: theme-color.$on-surface,
  91. unselected-focus-state-layer-opacity:
  92. map.get(ripple-theme.$dark-ink-opacities, focus),
  93. unselected-hover-icon-color: $baseline-theme-color,
  94. unselected-hover-state-layer-color: theme-color.$on-surface,
  95. unselected-hover-state-layer-opacity:
  96. map.get(ripple-theme.$dark-ink-opacities, hover),
  97. unselected-icon-color: $border-color,
  98. unselected-pressed-icon-color: $border-color,
  99. unselected-pressed-state-layer-color: theme-color.$on-surface,
  100. unselected-pressed-state-layer-opacity:
  101. map.get(ripple-theme.$dark-ink-opacities, pressed),
  102. );
  103. $forced-colors-theme: (
  104. disabled-selected-checkmark-color: ButtonFace,
  105. disabled-selected-icon-color: GrayText,
  106. disabled-selected-icon-opacity: 1,
  107. disabled-unselected-icon-color: GrayText,
  108. disabled-unselected-icon-opacity: 1,
  109. selected-checkmark-color: ButtonText,
  110. );
  111. @mixin theme($theme) {
  112. // TODO(b/251881053): Replace with `validate-theme`.
  113. @include theme.validate-theme-styles($light-theme, $theme);
  114. @include keys.declare-custom-properties(
  115. $theme,
  116. $prefix: $custom-property-prefix
  117. );
  118. }
  119. @mixin theme-styles($theme) {
  120. @include theme.validate-theme-styles($light-theme, $theme);
  121. $theme: keys.create-theme-properties(
  122. $theme,
  123. $prefix: $custom-property-prefix
  124. );
  125. @include disabled-container-colors(
  126. $unmarked-stroke-color: map.get($theme, disabled-unselected-icon-color),
  127. $marked-fill-color: map.get($theme, disabled-selected-icon-color)
  128. );
  129. @include ink-color(map.get($theme, selected-checkmark-color));
  130. @include disabled-ink-color(
  131. map.get($theme, disabled-selected-checkmark-color)
  132. );
  133. @include _icon-color(
  134. map.get($theme, unselected-icon-color),
  135. map.get($theme, selected-icon-color)
  136. );
  137. &:hover {
  138. @include _icon-color(
  139. map.get($theme, unselected-hover-icon-color),
  140. map.get($theme, selected-hover-icon-color)
  141. );
  142. }
  143. @include ripple-theme.focus() {
  144. @include _icon-color(
  145. map.get($theme, unselected-focus-icon-color),
  146. map.get($theme, selected-focus-icon-color)
  147. );
  148. }
  149. @include ripple-theme.active() {
  150. @include _icon-color(
  151. map.get($theme, unselected-pressed-icon-color),
  152. map.get($theme, selected-pressed-icon-color)
  153. );
  154. }
  155. @include ripple-color(
  156. $color: map.get($theme, unselected-hover-state-layer-color),
  157. $opacity-map: (
  158. hover: map.get($theme, unselected-hover-state-layer-opacity),
  159. focus: map.get($theme, unselected-focus-state-layer-opacity),
  160. press: map.get($theme, unselected-pressed-state-layer-opacity),
  161. )
  162. );
  163. @include focus-indicator-color(
  164. $color: map.get($theme, selected-hover-state-layer-color),
  165. $opacity-map: (
  166. hover: map.get($theme, selected-hover-state-layer-opacity),
  167. focus: map.get($theme, selected-focus-state-layer-opacity),
  168. press: map.get($theme, selected-pressed-state-layer-opacity),
  169. )
  170. );
  171. @include ripple-size(map.get($theme, state-layer-size));
  172. // Set touch target to ripple size.
  173. @include touch-target(
  174. map.get($theme, state-layer-size),
  175. map.get($theme, state-layer-size)
  176. );
  177. }
  178. $light-theme-deprecated: (
  179. density-scale: 0,
  180. checkmark-color: $mark-color,
  181. container-checked-color: $baseline-theme-color,
  182. container-checked-hover-color: null,
  183. container-disabled-color: $disabled-color,
  184. outline-color: $border-color,
  185. outline-hover-color: null,
  186. ripple-color: theme-color.$on-surface,
  187. ripple-opacity: ripple-theme.$dark-ink-opacities,
  188. ripple-checked-color: $baseline-theme-color,
  189. ripple-checked-opacity: ripple-theme.$dark-ink-opacities,
  190. );
  191. /// Sets theme to checkbox based on provided theme configuration.
  192. /// Only emits theme related styles.
  193. /// @param {Map} $theme - Theme configuration to use for theming checkbox.
  194. @mixin theme-deprecated($theme, $query: feature-targeting.all()) {
  195. @include theme.validate-theme($light-theme-deprecated, $theme);
  196. $ripple-color: map.get($theme, ripple-color);
  197. $ripple-opacity: map.get($theme, ripple-opacity);
  198. @if $ripple-opacity == null {
  199. $ripple-opacity: ();
  200. }
  201. @if $ripple-color {
  202. @include ripple-color(
  203. $color: $ripple-color,
  204. $opacity-map: $ripple-opacity,
  205. $query: $query
  206. );
  207. }
  208. $ripple-checked-color: map.get($theme, ripple-checked-color);
  209. $ripple-checked-opacity: map.get($theme, ripple-checked-opacity);
  210. @if $ripple-checked-opacity == null {
  211. $ripple-checked-opacity: ();
  212. }
  213. @if $ripple-checked-color {
  214. @include focus-indicator-color(
  215. $color: $ripple-checked-color,
  216. $opacity-map: $ripple-checked-opacity,
  217. $query: $query
  218. );
  219. }
  220. $density-scale: map.get($theme, density-scale);
  221. @if $density-scale != null {
  222. @include density($density-scale: $density-scale, $query: $query);
  223. }
  224. $outline-color: map.get($theme, outline-color);
  225. $container-checked-color: map.get($theme, container-checked-color);
  226. @if (
  227. ($outline-color and not $container-checked-color) or
  228. (not $outline-color and $container-checked-color)
  229. ) {
  230. @error 'Both `outline-color` and `container-checked-color` keys should be provided.';
  231. }
  232. @if ($outline-color and $container-checked-color) {
  233. @include container-colors(
  234. $unmarked-stroke-color: $outline-color,
  235. $marked-stroke-color: $container-checked-color,
  236. $marked-fill-color: $container-checked-color,
  237. $query: $query
  238. );
  239. }
  240. $outline-hover-color: map.get($theme, outline-hover-color);
  241. $container-checked-hover-color: map.get(
  242. $theme,
  243. container-checked-hover-color
  244. );
  245. @if (
  246. ($outline-hover-color and not $container-checked-hover-color) or
  247. (not $outline-hover-color and $container-checked-hover-color)
  248. ) {
  249. @error 'Both `outline-hover-color` and `container-checked-hover-color` keys should be provided.';
  250. }
  251. @if ($outline-hover-color and $container-checked-hover-color) {
  252. @include ripple-theme.states-selector() {
  253. @include container-colors(
  254. $unmarked-stroke-color: $outline-hover-color,
  255. $marked-stroke-color: $container-checked-hover-color,
  256. $marked-fill-color: $container-checked-hover-color,
  257. $query: $query
  258. );
  259. }
  260. }
  261. $container-disabled-color: map.get($theme, container-disabled-color);
  262. @if $container-disabled-color {
  263. @include disabled-container-colors(
  264. $unmarked-stroke-color: $container-disabled-color,
  265. $marked-fill-color: $container-disabled-color,
  266. $query: $query
  267. );
  268. }
  269. $checkmark-color: map.get($theme, checkmark-color);
  270. @if $checkmark-color {
  271. @include ink-color($checkmark-color, $query: $query);
  272. @include disabled-ink-color($checkmark-color, $query: $query);
  273. }
  274. }
  275. ///
  276. /// @param {Number | String} $density-scale - Density scale value for component.
  277. /// Supported density scale values `-3`, `-2`, `-1`, `0`.
  278. /// @return Returns ripple size of checkbox for given density scale.
  279. ///
  280. @function get-ripple-size($density-scale) {
  281. @return density-functions.prop-value(
  282. $density-config: $density-config,
  283. $density-scale: $density-scale,
  284. $property-name: size
  285. );
  286. }
  287. ///
  288. /// Sets density scale for checkbox.
  289. ///
  290. /// @param {Number | String} $density-scale - Density scale value for component. Supported density scale values
  291. /// `-3`, `-2`, `-1`, `0`.
  292. ///
  293. @mixin density($density-scale, $query: feature-targeting.all()) {
  294. $size: get-ripple-size($density-scale);
  295. @include ripple-size($size, $query: $query);
  296. @include touch-target($size, $ripple-size: $size, $query: $query);
  297. }
  298. /// Sets ripple size of checkbox and optionally set touch target size which can
  299. /// be more than the size of ripple.
  300. /// @param {Number} $ripple-size - Visual ripple size of checkbox.
  301. @mixin ripple-size($ripple-size, $query: feature-targeting.all()) {
  302. $feat-structure: feature-targeting.create-target($query, structure);
  303. @if $ripple-size and not custom-properties.is-custom-prop($ripple-size) {
  304. $ripple-size: custom-properties.create(
  305. checkbox-custom-properties.$ripple-size,
  306. $ripple-size
  307. );
  308. }
  309. $checkbox-padding: 'calc((_ripple-size - _icon-size) / 2)';
  310. $replace: (
  311. _ripple-size: $ripple-size,
  312. _icon-size: $icon-size,
  313. );
  314. @include feature-targeting.targets($feat-structure) {
  315. @include theme.property(padding, $checkbox-padding, $replace: $replace);
  316. }
  317. .mdc-checkbox__background {
  318. @include feature-targeting.targets($feat-structure) {
  319. @include theme.property(top, $checkbox-padding, $replace: $replace);
  320. @include theme.property(left, $checkbox-padding, $replace: $replace);
  321. }
  322. }
  323. }
  324. /// Sets the touch target size and appropriate margin to accommodate the
  325. /// touch target.
  326. /// @param {Number} $touch-target-size Size of touch target (Native input) in `px`.
  327. /// @param {Number} $ripple-size Size of ripple in `px`.
  328. @mixin touch-target(
  329. $touch-target-size,
  330. $ripple-size,
  331. $query: feature-targeting.all()
  332. ) {
  333. $feat-structure: feature-targeting.create-target($query, structure);
  334. @if $touch-target-size {
  335. @if not custom-properties.is-custom-prop($touch-target-size) {
  336. $touch-target-size: custom-properties.create(
  337. checkbox-custom-properties.$touch-target-size,
  338. $touch-target-size
  339. );
  340. }
  341. $margin: 'calc((_touch-target-size - _ripple-size) / 2)';
  342. $replace: (
  343. _touch-target-size: $touch-target-size,
  344. _ripple-size: $ripple-size,
  345. );
  346. @include feature-targeting.targets($feat-structure) {
  347. @include theme.property(margin, $margin, $replace: $replace);
  348. }
  349. $offset: 'calc((_ripple-size - _touch-target-size) / 2)';
  350. @include feature-targeting.targets($feat-structure) {
  351. .mdc-checkbox__native-control {
  352. @include theme.property(top, $offset, $replace: $replace);
  353. @include theme.property(right, $offset, $replace: $replace);
  354. @include theme.property(left, $offset, $replace: $replace);
  355. @include theme.property(width, $touch-target-size);
  356. @include theme.property(height, $touch-target-size);
  357. }
  358. }
  359. }
  360. }
  361. @mixin _icon-color($unselected-color, $selected-color) {
  362. @if $unselected-color and $selected-color {
  363. @include container-colors(
  364. $unmarked-stroke-color: $unselected-color,
  365. $marked-stroke-color: $selected-color,
  366. $marked-fill-color: $selected-color
  367. );
  368. } @else if $unselected-color or $selected-color {
  369. @error 'Both unselected and selected icon colors should be provided.';
  370. }
  371. }
  372. ///
  373. /// Sets stroke & fill colors for both marked and unmarked state of enabled checkbox.
  374. /// Set $generate-keyframes to false to prevent the mixin from generating @keyframes
  375. /// @param {Color} $unmarked-stroke-color - The desired stroke color for the unmarked state
  376. /// @param {Color} $unmarked-fill-color - The desired fill color for the unmarked state
  377. /// @param {Color} $marked-stroke-color - The desired stroke color for the marked state
  378. /// @param {Color} $marked-fill-color - The desired fill color for the marked state
  379. /// @param {Boolean} $generate-keyframes [true] - Whether animation keyframes should be generated
  380. ///
  381. @mixin container-colors(
  382. $unmarked-stroke-color: $border-color,
  383. $unmarked-fill-color: transparent,
  384. $marked-stroke-color: $baseline-theme-color,
  385. $marked-fill-color: $baseline-theme-color,
  386. $generate-keyframes: true,
  387. $query: feature-targeting.all()
  388. ) {
  389. $feat-animation: feature-targeting.create-target($query, animation);
  390. $feat-color: feature-targeting.create-target($query, color);
  391. // Unchecked colors
  392. @if (
  393. $unmarked-stroke-color and not
  394. custom-properties.is-custom-prop($unmarked-stroke-color)
  395. ) {
  396. $unmarked-stroke-color: custom-properties.create(
  397. checkbox-custom-properties.$unchecked-color,
  398. theme-color.prop-value($unmarked-stroke-color)
  399. );
  400. }
  401. @include if-unmarked-enabled_ {
  402. @include container-colors_(
  403. $unmarked-stroke-color,
  404. $unmarked-fill-color,
  405. $query: $query
  406. );
  407. }
  408. // Checked colors
  409. @if (
  410. $marked-stroke-color and not
  411. custom-properties.is-custom-prop($marked-stroke-color)
  412. ) {
  413. $marked-stroke-color: custom-properties.create(
  414. checkbox-custom-properties.$checked-color,
  415. custom-properties.create(
  416. color-custom-properties.$secondary,
  417. theme-color.prop-value($marked-stroke-color)
  418. )
  419. );
  420. }
  421. @if (
  422. $marked-fill-color and not
  423. custom-properties.is-custom-prop($marked-fill-color)
  424. ) {
  425. $marked-fill-color: custom-properties.create(
  426. checkbox-custom-properties.$checked-color,
  427. custom-properties.create(
  428. color-custom-properties.$secondary,
  429. theme-color.prop-value($marked-fill-color)
  430. )
  431. );
  432. }
  433. @include if-marked-enabled_ {
  434. @include container-colors_(
  435. $marked-stroke-color,
  436. $marked-fill-color,
  437. $query: $query
  438. );
  439. }
  440. @if $generate-keyframes and
  441. $unmarked-stroke-color and
  442. $marked-stroke-color and
  443. $unmarked-fill-color and
  444. $marked-fill-color
  445. {
  446. $uid: theme-color.color-hash($unmarked-stroke-color) +
  447. theme-color.color-hash($marked-stroke-color) +
  448. theme-color.color-hash($unmarked-fill-color) +
  449. theme-color.color-hash($marked-fill-color);
  450. $anim-selector: if(&, '&.mdc-checkbox--anim', '.mdc-checkbox--anim');
  451. @include feature-targeting.targets($feat-animation, $feat-color) {
  452. @include container-keyframes_(
  453. $from-stroke-color: $unmarked-stroke-color,
  454. $to-stroke-color: $marked-stroke-color,
  455. $from-fill-color: $unmarked-fill-color,
  456. $to-fill-color: $marked-fill-color,
  457. $uid: #{$uid}
  458. );
  459. }
  460. #{$anim-selector} {
  461. &-unchecked-checked,
  462. &-unchecked-indeterminate {
  463. .mdc-checkbox__native-control:enabled ~ .mdc-checkbox__background {
  464. @include feature-targeting.targets($feat-animation) {
  465. animation-name: mdc-checkbox-fade-in-background-#{$uid};
  466. }
  467. }
  468. }
  469. &-checked-unchecked,
  470. &-indeterminate-unchecked {
  471. .mdc-checkbox__native-control:enabled ~ .mdc-checkbox__background {
  472. @include feature-targeting.targets($feat-animation) {
  473. animation-name: mdc-checkbox-fade-out-background-#{$uid};
  474. }
  475. }
  476. }
  477. }
  478. }
  479. }
  480. ///
  481. /// Sets stroke & fill colors for both marked and unmarked state of disabled checkbox.
  482. /// @param {Color} $unmarked-stroke-color - The desired stroke color for the unmarked state
  483. /// @param {Color} $unmarked-fill-color - The desired fill color for the unmarked state
  484. /// @param {Color} $marked-stroke-color - The desired stroke color for the marked state
  485. /// @param {Color} $marked-fill-color - The desired fill color for the marked state
  486. ///
  487. @mixin disabled-container-colors(
  488. $unmarked-stroke-color: $disabled-color,
  489. $unmarked-fill-color: transparent,
  490. $marked-stroke-color: transparent,
  491. $marked-fill-color: $disabled-color,
  492. $query: feature-targeting.all()
  493. ) {
  494. @if (
  495. $unmarked-stroke-color and not
  496. custom-properties.is-custom-prop($unmarked-stroke-color)
  497. ) {
  498. $unmarked-stroke-color: custom-properties.create(
  499. checkbox-custom-properties.$disabled-color,
  500. theme-color.prop-value($unmarked-stroke-color)
  501. );
  502. }
  503. @if $unmarked-stroke-color == null {
  504. $unmarked-fill-color: null;
  505. }
  506. @include if-unmarked-disabled_ {
  507. @include container-colors_(
  508. $unmarked-stroke-color,
  509. $unmarked-fill-color,
  510. $query: $query
  511. );
  512. }
  513. @if (
  514. $marked-fill-color and not
  515. custom-properties.is-custom-prop($marked-fill-color)
  516. ) {
  517. $marked-fill-color: custom-properties.create(
  518. checkbox-custom-properties.$disabled-color,
  519. theme-color.prop-value($marked-fill-color)
  520. );
  521. }
  522. @if $marked-fill-color and
  523. custom-properties.get-fallback($marked-fill-color) ==
  524. GrayText
  525. {
  526. // Transparent appears white in HCM
  527. $marked-stroke-color: GrayText;
  528. }
  529. @if $marked-fill-color == null {
  530. $marked-stroke-color: null;
  531. }
  532. @include if-marked-disabled_ {
  533. @include container-colors_(
  534. $marked-stroke-color,
  535. $marked-fill-color,
  536. $query: $query
  537. );
  538. }
  539. }
  540. ///
  541. /// Sets the ink color of the checked and indeterminate icons for an enabled checkbox
  542. /// @param {Color} $color - The desired ink color in enabled state
  543. ///
  544. @mixin ink-color($color, $query: feature-targeting.all()) {
  545. @if ($color and not custom-properties.is-custom-prop($color)) {
  546. $color: custom-properties.create(
  547. checkbox-custom-properties.$ink-color,
  548. $color
  549. );
  550. }
  551. @include if-enabled_ {
  552. @include ink-color_($color, $query: $query);
  553. }
  554. }
  555. ///
  556. /// Sets the ink color of the checked and indeterminate icons for a disabled checkbox
  557. /// @param {Color} $color - The desired ink color in disabled state
  558. ///
  559. @mixin disabled-ink-color($color, $query: feature-targeting.all()) {
  560. @if ($color and not custom-properties.is-custom-prop($color)) {
  561. $color: custom-properties.create(
  562. checkbox-custom-properties.$ink-color,
  563. $color
  564. );
  565. }
  566. @include if-disabled_ {
  567. @include ink-color_($color, $query: $query);
  568. }
  569. }
  570. /// Sets ripple color when checkbox is not in checked state.
  571. @mixin ripple-color(
  572. $color,
  573. $opacity-map: null,
  574. $query: feature-targeting.all()
  575. ) {
  576. @include ripple-theme.states(
  577. $color: $color,
  578. $opacity-map: $opacity-map,
  579. $query: $query,
  580. $ripple-target: $ripple-target
  581. );
  582. }
  583. /// Sets focus indicator color when checkbox is in checked state.
  584. @mixin focus-indicator-color(
  585. $color,
  586. $opacity-map: null,
  587. $query: feature-targeting.all()
  588. ) {
  589. $feat-color: feature-targeting.create-target($query, color);
  590. &.mdc-checkbox--selected {
  591. @include ripple-theme.states(
  592. $color: $color,
  593. $opacity-map: $opacity-map,
  594. $query: $query,
  595. $ripple-target: $ripple-target
  596. );
  597. }
  598. &.mdc-ripple-upgraded--background-focused.mdc-checkbox--selected {
  599. @include ripple-theme.states-base-color(
  600. $color: $color,
  601. $query: $query,
  602. $ripple-target: $ripple-target
  603. );
  604. }
  605. }
  606. //
  607. // Private
  608. //
  609. ///
  610. /// Helps select the checkbox background only when its native control is in
  611. /// enabled state.
  612. /// @access private
  613. ///
  614. @mixin if-enabled_ {
  615. .mdc-checkbox__native-control:enabled ~ {
  616. @content;
  617. }
  618. }
  619. ///
  620. /// Helps select the checkbox background only when its native control is in
  621. /// disabled state.
  622. /// @access private
  623. ///
  624. @mixin if-disabled_ {
  625. .mdc-checkbox__native-control:disabled ~ {
  626. @content;
  627. }
  628. }
  629. ///
  630. /// Helps select the checkbox background only when its native control is in
  631. /// unmarked & enabled state.
  632. /// @access private
  633. ///
  634. @mixin if-unmarked-enabled_ {
  635. .mdc-checkbox__native-control:enabled:not(:checked):not(:indeterminate):not([data-indeterminate='true'])
  636. ~ {
  637. @content;
  638. }
  639. }
  640. ///
  641. /// Helps select the checkbox background only when its native control is in
  642. /// unmarked & disabled state.
  643. /// @access private
  644. ///
  645. @mixin if-unmarked-disabled_ {
  646. // Note: we must use `[disabled]` instead of `:disabled` below because Edge does not always recalculate the style
  647. // property when the `:disabled` pseudo-class is followed by a sibling combinator. See:
  648. // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/11295231/
  649. .mdc-checkbox__native-control[disabled]:not(:checked):not(:indeterminate):not([data-indeterminate='true'])
  650. ~ {
  651. @content;
  652. }
  653. }
  654. ///
  655. /// Helps select the checkbox background only when its native control is in
  656. /// marked & enabled state.
  657. /// @access private
  658. ///
  659. @mixin if-marked-enabled_ {
  660. .mdc-checkbox__native-control:enabled:checked,
  661. .mdc-checkbox__native-control:enabled:indeterminate,
  662. .mdc-checkbox__native-control[data-indeterminate='true']:enabled {
  663. ~ {
  664. @content;
  665. }
  666. }
  667. }
  668. ///
  669. /// Helps select the checkbox background only when its native control is in
  670. /// marked & disabled state.
  671. /// @access private
  672. ///
  673. @mixin if-marked-disabled_ {
  674. // Note: we must use `[disabled]` instead of `:disabled` below because Edge does not always recalculate the style
  675. // property when the `:disabled` pseudo-class is followed by a sibling combinator. See:
  676. // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/11295231/
  677. .mdc-checkbox__native-control[disabled]:checked,
  678. .mdc-checkbox__native-control[disabled]:indeterminate,
  679. .mdc-checkbox__native-control[data-indeterminate='true'][disabled] {
  680. ~ {
  681. @content;
  682. }
  683. }
  684. }
  685. ///
  686. /// Sets the stroke & fill colors for the checkbox.
  687. /// This mixin should be wrapped in a mixin that qualifies state such as
  688. /// `mdc-checkbox-if-unmarked-enabled_`.
  689. /// @access private
  690. ///
  691. @mixin container-colors_(
  692. $stroke-color,
  693. $fill-color,
  694. $query: feature-targeting.all()
  695. ) {
  696. $feat-color: feature-targeting.create-target($query, color);
  697. .mdc-checkbox__background {
  698. @include feature-targeting.targets($feat-color) {
  699. @include theme.property(border-color, $stroke-color);
  700. @include theme.property(background-color, $fill-color);
  701. }
  702. }
  703. }
  704. ///
  705. /// Sets the ink color of the checked and indeterminate icons for a checkbox.
  706. /// This mixin should be wrapped in a mixin that qualifies state such as
  707. /// `mdc-checkbox-if-unmarked_`.
  708. /// @access private
  709. ///
  710. @mixin ink-color_($color, $query: feature-targeting.all()) {
  711. $feat-color: feature-targeting.create-target($query, color);
  712. .mdc-checkbox__background {
  713. .mdc-checkbox__checkmark {
  714. @include feature-targeting.targets($feat-color) {
  715. @include theme.property(color, $color);
  716. }
  717. }
  718. .mdc-checkbox__mixedmark {
  719. @include feature-targeting.targets($feat-color) {
  720. @include theme.property(border-color, $color);
  721. }
  722. }
  723. }
  724. }
  725. @mixin container-keyframes_(
  726. $from-stroke-color,
  727. $to-stroke-color,
  728. $from-fill-color,
  729. $to-fill-color,
  730. $uid
  731. ) {
  732. @keyframes mdc-checkbox-fade-in-background-#{$uid} {
  733. 0% {
  734. @include theme.property(border-color, $from-stroke-color);
  735. @include theme.property(background-color, $from-fill-color);
  736. }
  737. 50% {
  738. @include theme.property(border-color, $to-stroke-color);
  739. @include theme.property(background-color, $to-fill-color);
  740. }
  741. }
  742. @keyframes mdc-checkbox-fade-out-background-#{$uid} {
  743. 0%,
  744. 80% {
  745. @include theme.property(border-color, $to-stroke-color);
  746. @include theme.property(background-color, $to-fill-color);
  747. }
  748. 100% {
  749. @include theme.property(border-color, $from-stroke-color);
  750. @include theme.property(background-color, $from-fill-color);
  751. }
  752. }
  753. }