_mixins.scss 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. //
  2. // Copyright 2017 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:math';
  25. @use '@material/elevation/mixins' as elevation-mixins;
  26. @use '@material/feature-targeting/feature-targeting';
  27. @use '@material/ripple/ripple';
  28. @use '@material/ripple/ripple-theme';
  29. @use '@material/rtl/rtl';
  30. @use '@material/shape/mixins' as shape-mixins;
  31. @use '@material/theme/theme';
  32. @use './variables';
  33. @use '@material/theme/theme-color';
  34. @use '@material/dom/mixins' as dom-mixins;
  35. //
  36. // Public
  37. //
  38. $ripple-target: '.mdc-card__ripple';
  39. @mixin core-styles($query: feature-targeting.all()) {
  40. @include without-ripple($query);
  41. @include ripple($query);
  42. }
  43. @mixin static-styles($query: feature-targeting.all()) {
  44. $feat-structure: feature-targeting.create-target($query, structure);
  45. $feat-color: feature-targeting.create-target($query, color);
  46. .mdc-card {
  47. @include feature-targeting.targets($feat-structure) {
  48. @include container-layout_;
  49. }
  50. // Transparent card border for high-contrast mode.
  51. &::after {
  52. @include dom-mixins.transparent-border($query: $query);
  53. @include feature-targeting.targets($feat-structure) {
  54. pointer-events: none;
  55. }
  56. }
  57. }
  58. .mdc-card--outlined {
  59. // Outlined card already displays border in high-contrast mode.
  60. // Overwriting styles set above to remove a duplicate border.
  61. &::after {
  62. @include feature-targeting.targets($feat-structure) {
  63. border: none;
  64. }
  65. }
  66. }
  67. .mdc-card__content {
  68. @include feature-targeting.targets($feat-structure) {
  69. border-radius: inherit;
  70. height: 100%;
  71. }
  72. }
  73. //
  74. // Media
  75. //
  76. .mdc-card__media {
  77. @include feature-targeting.targets($feat-structure) {
  78. position: relative; // Child element `__media-content` has `position: absolute`
  79. box-sizing: border-box;
  80. background-repeat: no-repeat;
  81. background-position: center;
  82. background-size: cover;
  83. }
  84. &::before {
  85. @include feature-targeting.targets($feat-structure) {
  86. display: block;
  87. content: '';
  88. }
  89. }
  90. }
  91. .mdc-card__media:first-child {
  92. @include feature-targeting.targets($feat-structure) {
  93. border-top-left-radius: inherit;
  94. border-top-right-radius: inherit;
  95. }
  96. }
  97. .mdc-card__media:last-child {
  98. @include feature-targeting.targets($feat-structure) {
  99. border-bottom-left-radius: inherit;
  100. border-bottom-right-radius: inherit;
  101. }
  102. }
  103. .mdc-card__media--square {
  104. @include media-aspect-ratio(1, 1, $query);
  105. }
  106. .mdc-card__media--16-9 {
  107. @include media-aspect-ratio(16, 9, $query);
  108. }
  109. .mdc-card__media-content {
  110. @include feature-targeting.targets($feat-structure) {
  111. position: absolute;
  112. top: 0;
  113. right: 0;
  114. bottom: 0;
  115. left: 0;
  116. box-sizing: border-box;
  117. }
  118. }
  119. //
  120. // Primary action
  121. //
  122. .mdc-card__primary-action {
  123. @include feature-targeting.targets($feat-structure) {
  124. @include container-layout_;
  125. position: relative; // Needed to prevent the ripple wash from overflowing the container in IE and Edge
  126. outline: none;
  127. color: inherit;
  128. text-decoration: none;
  129. cursor: pointer;
  130. overflow: hidden;
  131. }
  132. }
  133. .mdc-card__primary-action:first-child {
  134. @include feature-targeting.targets($feat-structure) {
  135. border-top-left-radius: inherit;
  136. border-top-right-radius: inherit;
  137. }
  138. }
  139. .mdc-card__primary-action:last-child {
  140. @include feature-targeting.targets($feat-structure) {
  141. border-bottom-left-radius: inherit;
  142. border-bottom-right-radius: inherit;
  143. }
  144. }
  145. //
  146. // Action row
  147. //
  148. .mdc-card__actions {
  149. @include feature-targeting.targets($feat-structure) {
  150. @include actions-layout_;
  151. min-height: 52px;
  152. padding: 8px;
  153. }
  154. }
  155. .mdc-card__actions--full-bleed {
  156. @include feature-targeting.targets($feat-structure) {
  157. padding: 0;
  158. }
  159. }
  160. .mdc-card__action-buttons,
  161. .mdc-card__action-icons {
  162. @include feature-targeting.targets($feat-structure) {
  163. @include actions-layout_;
  164. }
  165. }
  166. .mdc-card__action-icons {
  167. @include feature-targeting.targets($feat-color) {
  168. @include theme.property(color, variables.$action-icon-color);
  169. }
  170. @include feature-targeting.targets($feat-structure) {
  171. flex-grow: 1;
  172. justify-content: flex-end;
  173. }
  174. }
  175. .mdc-card__action-buttons + .mdc-card__action-icons {
  176. @include feature-targeting.targets($feat-structure) {
  177. @include rtl.reflexive-box(margin, left, 16px);
  178. }
  179. }
  180. //
  181. // Action items
  182. //
  183. .mdc-card__action {
  184. @include feature-targeting.targets($feat-structure) {
  185. @include actions-layout_(inline-flex);
  186. justify-content: center;
  187. cursor: pointer;
  188. user-select: none;
  189. }
  190. &:focus {
  191. @include feature-targeting.targets($feat-structure) {
  192. outline: none;
  193. }
  194. }
  195. }
  196. //
  197. // Action buttons
  198. //
  199. .mdc-card__action--button {
  200. @include feature-targeting.targets($feat-structure) {
  201. @include rtl.reflexive-box(margin, right, 8px);
  202. padding: 0 8px;
  203. }
  204. &:last-child {
  205. @include feature-targeting.targets($feat-structure) {
  206. @include rtl.reflexive-box(margin, right, 0);
  207. }
  208. }
  209. }
  210. .mdc-card__actions--full-bleed .mdc-card__action--button {
  211. @include feature-targeting.targets($feat-structure) {
  212. justify-content: space-between;
  213. width: 100%;
  214. height: auto;
  215. max-height: none;
  216. margin: 0;
  217. padding: 8px 16px;
  218. @include rtl.ignore-next-line();
  219. text-align: left;
  220. }
  221. @include rtl.rtl {
  222. @include feature-targeting.targets($feat-structure) {
  223. @include rtl.ignore-next-line();
  224. text-align: right;
  225. }
  226. }
  227. }
  228. //
  229. // Action icons
  230. //
  231. .mdc-card__action--icon {
  232. @include feature-targeting.targets($feat-structure) {
  233. // Icon buttons are taller than buttons, so we need to adjust their margins to prevent the action row from
  234. // expanding.
  235. margin: -6px 0;
  236. // Same padding as mdc-icon-button.
  237. padding: 12px;
  238. }
  239. }
  240. .mdc-card__action--icon:not(:disabled) {
  241. @include feature-targeting.targets($feat-color) {
  242. @include theme.property(color, variables.$action-icon-color);
  243. }
  244. }
  245. }
  246. // This API is intended for use by frameworks that may want to separate the ripple-related styles from the other
  247. // card styles. It is recommended that most users use `mdc-card-core-styles` instead.
  248. @mixin without-ripple($query: feature-targeting.all()) {
  249. // postcss-bem-linter: define card
  250. $feat-color: feature-targeting.create-target($query, color);
  251. $feat-structure: feature-targeting.create-target($query, structure);
  252. // prettier-ignore
  253. @include elevation-mixins.overlay-common($query); // COPYBARA_COMMENT_THIS_LINE
  254. .mdc-card {
  255. @include shape-radius(variables.$shape-radius, $query: $query);
  256. @include fill-color(surface, $query);
  257. @include elevation-mixins.overlay-surface-position($query: $query);
  258. @include elevation-mixins.overlay-dimensions(100%, $query: $query);
  259. @include elevation-mixins.elevation(1, $query: $query);
  260. // Transparent card border for high-contrast mode.
  261. &::after {
  262. @include shape-radius(variables.$shape-radius, $query: $query);
  263. }
  264. }
  265. .mdc-card--outlined {
  266. @include elevation-mixins.elevation(0, $query: $query);
  267. @include outline(variables.$outline-color, $query: $query);
  268. }
  269. @include static-styles($query: $query);
  270. // postcss-bem-linter: end
  271. }
  272. // This API is intended for use by frameworks that may want to separate the ripple-related styles from the other
  273. // card styles. It is recommended that most users use `mdc-card-core-styles` instead.
  274. @mixin ripple($query: feature-targeting.all()) {
  275. @include ripple.common($query); // COPYBARA_COMMENT_THIS_LINE
  276. $feat-structure: feature-targeting.create-target($query, structure);
  277. .mdc-card__primary-action {
  278. @include ripple.surface($query, $ripple-target: $ripple-target);
  279. @include ripple.radius-bounded(
  280. $query: $query,
  281. $ripple-target: $ripple-target
  282. );
  283. @include ripple-theme.states(
  284. $query: $query,
  285. $ripple-target: $ripple-target
  286. );
  287. #{$ripple-target} {
  288. @include feature-targeting.targets($feat-structure) {
  289. box-sizing: content-box;
  290. height: 100%;
  291. overflow: hidden;
  292. left: 0;
  293. pointer-events: none;
  294. position: absolute;
  295. top: 0;
  296. width: 100%;
  297. }
  298. }
  299. @include ripple-theme.focus {
  300. &::after {
  301. @include dom-mixins.transparent-border(
  302. $border-width: 5px,
  303. $border-style: double,
  304. $query: $query
  305. );
  306. }
  307. }
  308. }
  309. }
  310. @mixin fill-color($color, $query: feature-targeting.all()) {
  311. $feat-color: feature-targeting.create-target($query, color);
  312. @include feature-targeting.targets($feat-color) {
  313. @include theme.property(background-color, $color);
  314. }
  315. }
  316. @mixin outline(
  317. $color,
  318. $thickness: variables.$outline-width,
  319. $query: feature-targeting.all()
  320. ) {
  321. $feat-color: feature-targeting.create-target($query, color);
  322. $feat-structure: feature-targeting.create-target($query, structure);
  323. @include feature-targeting.targets($feat-structure) {
  324. @include theme.property(border-width, $thickness);
  325. border-style: solid;
  326. }
  327. @include feature-targeting.targets($feat-color) {
  328. @include theme.property(border-color, $color);
  329. }
  330. }
  331. @mixin shape-radius(
  332. $radius,
  333. $rtl-reflexive: false,
  334. $query: feature-targeting.all()
  335. ) {
  336. @include shape-mixins.radius($radius, $rtl-reflexive, $query: $query);
  337. }
  338. @mixin media-aspect-ratio($x, $y, $query: feature-targeting.all()) {
  339. $feat-structure: feature-targeting.create-target($query, structure);
  340. &::before {
  341. @include feature-targeting.targets($feat-structure) {
  342. // This clever trick brought to you by: http://www.mademyday.de/css-height-equals-width-with-pure-css.html
  343. margin-top: math.percentage(math.div($y, $x));
  344. }
  345. }
  346. }
  347. //
  348. // Private
  349. //
  350. @mixin container-layout_ {
  351. display: flex;
  352. flex-direction: column;
  353. box-sizing: border-box;
  354. }
  355. @mixin actions-layout_($display: flex) {
  356. display: $display;
  357. flex-direction: row;
  358. align-items: center;
  359. box-sizing: border-box;
  360. }