_tab-theme.scss 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  1. //
  2. // Copyright 2021 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:meta';
  26. @use '@material/elevation/elevation-theme';
  27. @use '@material/ripple/ripple-theme';
  28. @use '@material/theme/keys';
  29. @use '@material/theme/state';
  30. @use '@material/theme/theme';
  31. @use '@material/theme/validate';
  32. @use '@material/theme/theme-color';
  33. @use '@material/typography/typography';
  34. @use '@material/tokens/resolvers';
  35. $primary-light-theme: (
  36. active-focus-state-layer-color: theme-color.$primary,
  37. active-focus-state-layer-opacity: 0.12,
  38. active-hover-state-layer-color: theme-color.$primary,
  39. active-hover-state-layer-opacity: 0.04,
  40. active-pressed-state-layer-color: theme-color.$primary,
  41. active-pressed-state-layer-opacity: 0.1,
  42. container-color: theme-color.$surface,
  43. container-elevation: elevation-theme.get-elevation(0),
  44. container-height: 48px,
  45. container-shape: 0,
  46. inactive-focus-state-layer-color: theme-color.$on-surface,
  47. inactive-focus-state-layer-opacity: 0.12,
  48. inactive-hover-state-layer-color: theme-color.$on-surface,
  49. inactive-hover-state-layer-opacity: 0.04,
  50. inactive-pressed-state-layer-color: theme-color.$on-surface,
  51. inactive-pressed-state-layer-opacity: 0.1,
  52. with-icon-active-focus-icon-color: theme-color.$primary,
  53. with-icon-active-hover-icon-color: theme-color.$primary,
  54. with-icon-active-icon-color: theme-color.$primary,
  55. with-icon-active-pressed-icon-color: theme-color.$primary,
  56. with-icon-and-label-text-container-height: 64px,
  57. with-icon-icon-size: 24px,
  58. with-icon-inactive-focus-icon-color: theme-color.$on-surface,
  59. with-icon-inactive-hover-icon-color: theme-color.$on-surface,
  60. with-icon-inactive-icon-color: theme-color.$on-surface,
  61. with-icon-inactive-pressed-icon-color: theme-color.$on-surface,
  62. with-label-text-active-focus-label-text-color: theme-color.$primary,
  63. with-label-text-active-hover-label-text-color: theme-color.$primary,
  64. with-label-text-active-label-text-color: theme-color.$primary,
  65. with-label-text-active-pressed-label-text-color: theme-color.$primary,
  66. with-label-text-inactive-focus-label-text-color: theme-color.$on-surface,
  67. with-label-text-inactive-hover-label-text-color: theme-color.$on-surface,
  68. with-label-text-inactive-label-text-color: theme-color.$on-surface,
  69. with-label-text-inactive-pressed-label-text-color: theme-color.$on-surface,
  70. with-label-text-label-text-font: typography.get-font(subhead2),
  71. with-label-text-label-text-size: typography.get-size(subhead2),
  72. with-label-text-label-text-letter-spacing: typography.get-tracking(subhead2),
  73. with-label-text-label-text-line-height: typography.get-line-height(subhead2),
  74. with-label-text-label-text-weight: typography.get-weight(subhead2),
  75. );
  76. $secondary-light-theme: (
  77. active-label-text-color: null,
  78. container-color: null,
  79. container-elevation: null,
  80. container-height: null,
  81. container-shadow-color: null,
  82. container-shape: null,
  83. focus-label-text-color: null,
  84. focus-state-layer-color: null,
  85. focus-state-layer-opacity: null,
  86. hover-label-text-color: null,
  87. hover-state-layer-color: null,
  88. hover-state-layer-opacity: null,
  89. inactive-label-text-color: null,
  90. label-text-font: null,
  91. label-text-size: null,
  92. label-text-letter-spacing: null,
  93. label-text-line-height: null,
  94. label-text-weight: null,
  95. pressed-label-text-color: null,
  96. pressed-state-layer-color: null,
  97. pressed-state-layer-opacity: null,
  98. with-icon-active-icon-color: null,
  99. with-icon-focus-icon-color: null,
  100. with-icon-hover-icon-color: null,
  101. with-icon-inactive-icon-color: null,
  102. with-icon-pressed-icon-color: null,
  103. with-icon-icon-size: null,
  104. );
  105. $_ripple-target: '.mdc-tab__ripple';
  106. $custom-property-prefix: 'tab';
  107. $custom-property-prefix-primary: 'primary-navigation-tab';
  108. $custom-property-prefix-secondary: 'secondary-navigation-tab';
  109. @mixin primary-navigation-tab-theme($theme, $resolvers: resolvers.$material) {
  110. $theme: validate.theme($primary-light-theme, $theme);
  111. $theme: typography.resolve-theme(
  112. $theme,
  113. map.get($resolvers, typography),
  114. with-label-text-label-text
  115. );
  116. @include keys.declare-custom-properties(
  117. $theme,
  118. $prefix: $custom-property-prefix-primary
  119. );
  120. }
  121. @mixin secondary-navigation-tab-theme($theme, $resolvers: resolvers.$material) {
  122. $theme: validate.theme($secondary-light-theme, $theme);
  123. $theme: typography.resolve-theme(
  124. $theme,
  125. map.get($resolvers, typography),
  126. label-text
  127. );
  128. @include keys.declare-custom-properties(
  129. $theme,
  130. $prefix: $custom-property-prefix-secondary
  131. );
  132. }
  133. @mixin primary-navigation-tab-theme-styles(
  134. $theme,
  135. $resolvers: resolvers.$material
  136. ) {
  137. // TODO(b/251881053): Use theme-styles mixin from validate module
  138. // when all customers are migrated
  139. @include theme.validate-theme-styles($primary-light-theme, $theme);
  140. $theme: typography.resolve-theme(
  141. $theme,
  142. map.get($resolvers, typography),
  143. with-label-text-label-text
  144. );
  145. $theme: keys.create-theme-properties(
  146. $theme,
  147. $prefix: $custom-property-prefix-primary
  148. );
  149. // TODO(b/191298796): Use independent state layer color mixins when available.
  150. @include _active-state-layer-color(
  151. map.get($theme, active-hover-state-layer-color) or
  152. map.get($theme, active-focus-state-layer-color) or
  153. map.get($theme, active-pressed-state-layer-color)
  154. );
  155. @include _active-state-layer-opacity(
  156. (
  157. hover: map.get($theme, active-hover-state-layer-opacity),
  158. focus: map.get($theme, active-focus-state-layer-opacity),
  159. pressed: map.get($theme, active-pressed-state-layer-opacity),
  160. )
  161. );
  162. // TODO(b/191298796): Use independent state layer color mixins when available.
  163. @include _inactive-state-layer-color(
  164. map.get($theme, inactive-hover-state-layer-color) or
  165. map.get($theme, inactive-focus-state-layer-color) or
  166. map.get($theme, inactive-pressed-state-layer-color)
  167. );
  168. @include _inactive-state-layer-opacity(
  169. (
  170. hover: map.get($theme, inactive-hover-state-layer-opacity),
  171. focus: map.get($theme, inactive-focus-state-layer-opacity),
  172. pressed: map.get($theme, inactive-pressed-state-layer-opacity),
  173. )
  174. );
  175. @include _container-color(map.get($theme, container-color));
  176. @include _container-elevation(map.get($theme, container-elevation));
  177. @include _container-height(map.get($theme, container-height));
  178. @include _container-shape(map.get($theme, container-shape));
  179. @include _with-icon-and-label-text-container-height(
  180. map.get($theme, with-icon-and-label-text-container-height)
  181. );
  182. @include _icon-size(map.get($theme, with-icon-icon-size));
  183. @include _active-icon-color(
  184. (
  185. default: map.get($theme, with-icon-active-icon-color),
  186. hover: map.get($theme, with-icon-active-hover-icon-color),
  187. focus: map.get($theme, with-icon-active-focus-icon-color),
  188. pressed: map.get($theme, with-icon-active-pressed-icon-color),
  189. )
  190. );
  191. @include _inactive-icon-color(
  192. (
  193. default: map.get($theme, with-icon-inactive-icon-color),
  194. hover: map.get($theme, with-icon-inactive-hover-icon-color),
  195. focus: map.get($theme, with-icon-inactive-focus-icon-color),
  196. pressed: map.get($theme, with-icon-inactive-pressed-icon-color),
  197. )
  198. );
  199. @include _active-label-text-color(
  200. (
  201. default: map.get($theme, with-label-text-active-label-text-color),
  202. hover: map.get($theme, with-label-text-active-hover-label-text-color),
  203. focus: map.get($theme, with-label-text-active-focus-label-text-color),
  204. pressed: map.get($theme, with-label-text-active-pressed-label-text-color),
  205. )
  206. );
  207. @include _inactive-label-text-color(
  208. (
  209. default: map.get($theme, with-label-text-inactive-label-text-color),
  210. hover: map.get($theme, with-label-text-inactive-hover-label-text-color),
  211. focus: map.get($theme, with-label-text-inactive-focus-label-text-color),
  212. pressed:
  213. map.get($theme, with-label-text-inactive-pressed-label-text-color),
  214. )
  215. );
  216. @include _label-text-font-family(
  217. map.get($theme, with-label-text-label-text-font)
  218. );
  219. @include _label-text-font-size(
  220. map.get($theme, with-label-text-label-text-size)
  221. );
  222. @include _label-text-letter-spacing(
  223. map.get($theme, with-label-text-label-text-letter-spacing)
  224. );
  225. @include _label-text-line-height(
  226. map.get($theme, with-label-text-label-text-line-height)
  227. );
  228. @include _label-text-weight(
  229. map.get($theme, with-label-text-label-text-weight)
  230. );
  231. }
  232. @mixin secondary-navigation-tab-theme-styles(
  233. $theme,
  234. $resolvers: resolvers.$material
  235. ) {
  236. // TODO(b/251881053): Use theme-styles mixin from validate module
  237. // when all customers are migrated
  238. @include theme.validate-theme-styles($secondary-light-theme, $theme);
  239. $theme: typography.resolve-theme(
  240. $theme,
  241. map.get($resolvers, typography),
  242. label-text
  243. );
  244. $theme: keys.create-theme-properties(
  245. $theme,
  246. $prefix: $custom-property-prefix-secondary
  247. );
  248. // TODO(b/191298796): Use independent state layer color mixins when available.
  249. @include _active-state-layer-color(
  250. map.get($theme, hover-state-layer-color) or
  251. map.get($theme, focus-state-layer-color) or
  252. map.get($theme, pressed-state-layer-color)
  253. );
  254. @include _active-state-layer-opacity(
  255. (
  256. hover: map.get($theme, hover-state-layer-opacity),
  257. focus: map.get($theme, focus-state-layer-opacity),
  258. pressed: map.get($theme, pressed-state-layer-opacity),
  259. )
  260. );
  261. // TODO(b/191298796): Use independent state layer color mixins when available.
  262. @include _inactive-state-layer-color(
  263. map.get($theme, hover-state-layer-color) or
  264. map.get($theme, focus-state-layer-color) or
  265. map.get($theme, pressed-state-layer-color)
  266. );
  267. @include _inactive-state-layer-opacity(
  268. (
  269. hover: map.get($theme, hover-state-layer-opacity),
  270. focus: map.get($theme, focus-state-layer-opacity),
  271. pressed: map.get($theme, pressed-state-layer-opacity),
  272. )
  273. );
  274. @include _container-color(map.get($theme, container-color));
  275. @include _container-elevation(map.get($theme, container-elevation));
  276. @include _container-height(map.get($theme, container-height));
  277. @include _container-shape(map.get($theme, container-shape));
  278. @include _icon-size(map.get($theme, with-icon-icon-size));
  279. @include _active-icon-color(
  280. (
  281. default: map.get($theme, with-icon-active-icon-color),
  282. hover: map.get($theme, with-icon-hover-icon-color),
  283. focus: map.get($theme, with-icon-focus-icon-color),
  284. pressed: map.get($theme, with-icon-pressed-icon-color),
  285. )
  286. );
  287. @include _inactive-icon-color(
  288. (
  289. default: map.get($theme, with-icon-inactive-icon-color),
  290. hover: map.get($theme, with-icon-hover-icon-color),
  291. focus: map.get($theme, with-icon-focus-icon-color),
  292. pressed: map.get($theme, with-icon-pressed-icon-color),
  293. )
  294. );
  295. @include _active-label-text-color(
  296. (
  297. default: map.get($theme, active-label-text-color),
  298. hover: map.get($theme, hover-label-text-color),
  299. focus: map.get($theme, focus-label-text-color),
  300. pressed: map.get($theme, pressed-label-text-color),
  301. )
  302. );
  303. @include _inactive-label-text-color(
  304. (
  305. default: map.get($theme, inactive-label-text-color),
  306. hover: map.get($theme, hover-label-text-color),
  307. focus: map.get($theme, focus-label-text-color),
  308. pressed: map.get($theme, pressed-label-text-color),
  309. )
  310. );
  311. @include _label-text-font-family(map.get($theme, label-text-font));
  312. @include _label-text-font-size(map.get($theme, label-text-size));
  313. @include _label-text-letter-spacing(
  314. map.get($theme, label-text-letter-spacing)
  315. );
  316. @include _label-text-line-height(map.get($theme, label-text-line-height));
  317. @include _label-text-weight(map.get($theme, label-text-weight));
  318. }
  319. @mixin _icon-color($color) {
  320. .mdc-tab__icon {
  321. @include theme.property(color, $color);
  322. fill: currentColor;
  323. }
  324. }
  325. @mixin _active-icon-color($color-or-map) {
  326. &:not(:disabled) {
  327. @include _set-active-icon-color(state.get-default-state($color-or-map));
  328. &:hover {
  329. @include _set-active-icon-color(state.get-hover-state($color-or-map));
  330. }
  331. &:focus {
  332. @include _set-active-icon-color(state.get-focus-state($color-or-map));
  333. }
  334. &:active {
  335. @include _set-active-icon-color(state.get-pressed-state($color-or-map));
  336. }
  337. }
  338. &:disabled {
  339. @include _set-active-icon-color(state.get-disabled-state($color-or-map));
  340. }
  341. }
  342. @mixin _set-active-icon-color($color) {
  343. @include _is-active() {
  344. @include _icon-color($color);
  345. }
  346. }
  347. @mixin _inactive-icon-color($color-or-map) {
  348. &:not(:disabled) {
  349. @include _set-inactive-icon-color(state.get-default-state($color-or-map));
  350. &:hover {
  351. @include _set-inactive-icon-color(state.get-hover-state($color-or-map));
  352. }
  353. &:focus {
  354. @include _set-inactive-icon-color(state.get-focus-state($color-or-map));
  355. }
  356. &:active {
  357. @include _set-inactive-icon-color(state.get-pressed-state($color-or-map));
  358. }
  359. }
  360. &:disabled {
  361. @include _set-inactive-icon-color(state.get-disabled-state($color-or-map));
  362. }
  363. }
  364. @mixin _set-inactive-icon-color($color) {
  365. @include _is-inactive() {
  366. @include _icon-color($color);
  367. }
  368. }
  369. @mixin _label-text-color($color) {
  370. .mdc-tab__text-label {
  371. @include theme.property(color, $color);
  372. }
  373. }
  374. @mixin _active-label-text-color($color-or-map) {
  375. &:not(:disabled) {
  376. @include _set-active-label-text-color(
  377. state.get-default-state($color-or-map)
  378. );
  379. &:hover {
  380. @include _set-active-label-text-color(
  381. state.get-hover-state($color-or-map)
  382. );
  383. }
  384. &:focus {
  385. @include _set-active-label-text-color(
  386. state.get-focus-state($color-or-map)
  387. );
  388. }
  389. &:active {
  390. @include _set-active-label-text-color(
  391. state.get-pressed-state($color-or-map)
  392. );
  393. }
  394. }
  395. &:disabled {
  396. @include _set-active-label-text-color(
  397. state.get-disabled-state($color-or-map)
  398. );
  399. }
  400. }
  401. @mixin _set-active-label-text-color($color) {
  402. @include _is-active() {
  403. @include _label-text-color($color);
  404. }
  405. }
  406. @mixin _inactive-label-text-color($color-or-map) {
  407. &:not(:disabled) {
  408. @include _set-inactive-label-text-color(
  409. state.get-default-state($color-or-map)
  410. );
  411. &:hover {
  412. @include _set-inactive-label-text-color(
  413. state.get-hover-state($color-or-map)
  414. );
  415. }
  416. &:focus {
  417. @include _set-inactive-label-text-color(
  418. state.get-focus-state($color-or-map)
  419. );
  420. }
  421. &:active {
  422. @include _set-inactive-label-text-color(
  423. state.get-pressed-state($color-or-map)
  424. );
  425. }
  426. }
  427. &:disabled {
  428. @include _set-inactive-label-text-color(
  429. state.get-disabled-state($color-or-map)
  430. );
  431. }
  432. }
  433. @mixin _set-inactive-label-text-color($color) {
  434. @include _is-inactive() {
  435. @include _label-text-color($color);
  436. }
  437. }
  438. @mixin _container-color($color) {
  439. @include theme.property(background-color, $color);
  440. }
  441. @mixin _active-state-layer-color($color) {
  442. @include _is-active() {
  443. @include _state-layer-color($color);
  444. }
  445. }
  446. @mixin _inactive-state-layer-color($color) {
  447. @include _is-inactive() {
  448. @include _state-layer-color($color);
  449. }
  450. }
  451. @mixin _active-state-layer-opacity($opacity-or-map) {
  452. @include _is-active() {
  453. @include _hover-state-layer-opacity(state.get-hover-state($opacity-or-map));
  454. @include _focus-state-layer-opacity(state.get-focus-state($opacity-or-map));
  455. @include _pressed-state-layer-opacity(
  456. state.get-pressed-state($opacity-or-map)
  457. );
  458. }
  459. }
  460. @mixin _inactive-state-layer-opacity($opacity-or-map) {
  461. @include _is-inactive() {
  462. @include _hover-state-layer-opacity(state.get-hover-state($opacity-or-map));
  463. @include _focus-state-layer-opacity(state.get-focus-state($opacity-or-map));
  464. @include _pressed-state-layer-opacity(
  465. state.get-pressed-state($opacity-or-map)
  466. );
  467. }
  468. }
  469. @mixin _hover-state-layer-opacity($opacity) {
  470. @include ripple-theme.states-hover-opacity(
  471. $opacity,
  472. $ripple-target: $_ripple-target
  473. );
  474. }
  475. @mixin _focus-state-layer-opacity($opacity) {
  476. @include ripple-theme.states-focus-opacity(
  477. $opacity,
  478. $ripple-target: $_ripple-target
  479. );
  480. }
  481. @mixin _pressed-state-layer-opacity($opacity) {
  482. @include ripple-theme.states-press-opacity(
  483. $opacity,
  484. $ripple-target: $_ripple-target
  485. );
  486. }
  487. // TODO(b/191298796): support focused & pressed key colors.
  488. @mixin _state-layer-color($color) {
  489. @include ripple-theme.states-base-color(
  490. $color,
  491. $ripple-target: $_ripple-target
  492. );
  493. }
  494. @mixin _focus-state-layer-opacity($opacity) {
  495. @include ripple-theme.states-focus-opacity(
  496. $opacity,
  497. $ripple-target: $_ripple-target
  498. );
  499. }
  500. @mixin _pressed-state-layer-opacity($opacity) {
  501. @include ripple-theme.states-press-opacity(
  502. $opacity,
  503. $ripple-target: $_ripple-target
  504. );
  505. }
  506. @mixin _container-elevation($elevation) {
  507. // TODO(b/188905911): Use elevation's theme() mixin.
  508. @if $elevation and map.get($elevation, box-shadow) {
  509. @include elevation-theme.shadow(map.get($elevation, box-shadow));
  510. }
  511. @if $elevation and map.get($elevation, overlay-opacity) {
  512. @include elevation-theme.overlay-opacity(
  513. map.get($elevation, overlay-opacity)
  514. );
  515. }
  516. }
  517. @mixin _container-shape($shape-radius) {
  518. @include theme.property(border-radius, $shape-radius);
  519. .mdc-tab__ripple {
  520. @include theme.property(border-radius, $shape-radius);
  521. }
  522. }
  523. @mixin _icon-size($size) {
  524. @include theme.property('width', $size);
  525. @include theme.property('height', $size);
  526. @include theme.property('font-size', $size);
  527. }
  528. @mixin _label-text-font-family($font-family) {
  529. .mdc-tab__text-label {
  530. @include theme.property('font-family', $font-family);
  531. }
  532. }
  533. @mixin _label-text-font-size($font-size) {
  534. .mdc-tab__text-label {
  535. @include theme.property('font-size', $font-size);
  536. }
  537. }
  538. @mixin _label-text-letter-spacing($letter-spacing) {
  539. .mdc-tab__text-label {
  540. @include theme.property('letter-spacing', $letter-spacing);
  541. }
  542. }
  543. @mixin _label-text-line-height($line-height) {
  544. .mdc-tab__text-label {
  545. @include theme.property('line-height', $line-height);
  546. }
  547. }
  548. @mixin _label-text-weight($weight) {
  549. .mdc-tab__text-label {
  550. @include theme.property('font-weight', $weight);
  551. }
  552. }
  553. @mixin _height($height) {
  554. @include theme.property(height, $height);
  555. }
  556. @mixin _container-height($height) {
  557. &:not(.mdc-tab--stacked) {
  558. @include _height($height);
  559. }
  560. }
  561. @mixin _with-icon-and-label-text-container-height($height) {
  562. &.mdc-tab--stacked {
  563. @include _height($height);
  564. }
  565. }
  566. @mixin _is-active() {
  567. &.mdc-tab--active {
  568. @content;
  569. }
  570. }
  571. @mixin _is-inactive() {
  572. &:not(.mdc-tab--active) {
  573. @content;
  574. }
  575. }