// Copyright 2016 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only // Fonts @mixin localized-fonts { /* Japanese */ &:lang(ja) { font-family: Inter, 'SF Pro', 'SF Pro JP', 'BIZ UDGothic', 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', メイリオ, Meiryo, 'MS Pゴシック', 'Helvetica Neue', Helvetica, Arial, sans-serif; } /* Farsi (Persian) */ &:lang(fa) { font-family: 'Vazirmatn', -apple-system, system-ui, BlinkMacSystemFont, 'Segoe UI', Tahoma, 'Noto Sans Arabic', Helvetica, Arial, sans-serif; } /* Urdu */ &:lang(ur) { font-family: 'Noto Nastaliq Urdu', Gulzar, 'Jameel Noori Nastaleeq', 'Faiz Lahori Nastaleeq', 'Urdu Typesetting', Helvetica, Arial, sans-serif; } } @mixin font-family { font-family: $inter; @include localized-fonts; } @mixin time-fonts { font-family: Hatsuishi, $inter; @include localized-fonts; } @mixin font-title-1 { font-weight: 600; font-size: 26px; line-height: 32px; letter-spacing: -0.56px; } @mixin font-title-2 { font-weight: 600; font-size: 20px; line-height: 26px; letter-spacing: -0.34px; } @mixin font-title-medium { font-weight: 600; font-size: 18px; line-height: 25px; letter-spacing: -0.25px; } @mixin font-body-1 { font-size: 14px; line-height: 20px; letter-spacing: -0.08px; } @mixin font-body-1-bold { @include font-body-1; font-weight: 600; } @mixin font-body-1-italic { @include font-body-1; font-style: italic; } @mixin font-body-1-bold-italic { @include font-body-1; font-weight: 600; font-style: italic; } @mixin font-body-2 { font-size: 13px; line-height: 18px; letter-spacing: -0.03px; } @mixin font-body-2-bold { @include font-body-2; font-weight: 600; } @mixin font-body-2-medium { @include font-body-2; font-weight: 500; } @mixin font-body-2-italic { @include font-body-2; font-style: italic; } @mixin font-body-2-bold-italic { @include font-body-2; font-weight: 600; font-style: italic; } @mixin font-subtitle { font-size: 12px; line-height: 16px; letter-spacing: 0; } @mixin font-subtitle-bold { @include font-subtitle; font-weight: 600; } @mixin font-caption { font-size: 11px; line-height: 14px; letter-spacing: 0.06px; } @mixin font-caption-bold { @include font-caption; font-weight: 600; } @mixin font-caption-bold-italic { @include font-caption; font-weight: 600; font-style: italic; } // Themes @mixin light-theme() { @content; } @mixin explicit-light-theme() { .light-theme & { @content; } } @mixin dark-theme() { .dark-theme & { @content; } } @mixin any-theme { &, .dark-theme & { @content; } } // Utilities @mixin rounded-corners() { // This ensures the borders are completely rounded. (A value like 100% would make it an ellipse.) border-radius: 9999px; } @mixin smooth-scroll() { scroll-behavior: smooth; @media (prefers-reduced-motion) { scroll-behavior: auto; } } // NOTE: As of this writing, this mixin only works in the main window, because this class // is only applied there. @mixin only-when-page-is-visible { .page-is-visible & { @content; } } // Search results loading @mixin search-results-loading-pulsating-background { animation: search-results-loading-pulsating-background-animation 2s infinite; @media (prefers-reduced-motion) { animation: none; } @include light-theme { background: $color-gray-05; } @include dark-theme { background: $color-gray-65; } } @keyframes search-results-loading-pulsating-background-animation { 0% { opacity: 1; } 50% { opacity: 0.55; } 100% { opacity: 1; } } @mixin search-results-loading-box($width) { width: $width; height: 12px; border-radius: 4px; @include search-results-loading-pulsating-background; } // Icons @function str-replace($string, $search, $replace: '') { $index: str-index($string, $search); @if $index { @return ( str-slice($string, 1, $index - 1) + $replace + str-replace( str-slice($string, $index + str-length($search)), $search, $replace ) ); } @return $string; } $rtl-icon-map: ( 'chevron-left-16.svg': 'chevron-right-16.svg', 'chevron-right-16.svg': 'chevron-left-16.svg', 'chevron-left-20.svg': 'chevron-right-20.svg', 'chevron-right-20.svg': 'chevron-left-20.svg', 'chevron-left-24.svg': 'chevron-right-24.svg', 'chevron-right-24.svg': 'chevron-left-24.svg', 'arrow-left-32.svg': 'arrow-right-32.svg', 'arrow-right-32.svg': 'arrow-left-32.svg', // v3 icons 'chevron-left.svg': 'chevron-right.svg', 'chevron-right.svg': 'chevron-left.svg', 'chevron-shallow-left.svg': 'chevron-shallow-right.svg', 'chevron-shallow-right.svg': 'chevron-shallow-left.svg', 'chevron-left-compact-bold.svg': 'chevron-right-compact-bold.svg', 'chevron-right-compact-bold.svg': 'chevron-left-compact-bold.svg', 'chevron-right-bold.svg': 'chevron-left-bold.svg', 'arrow-left.svg': 'arrow-right.svg', 'arrow-right.svg': 'arrow-left.svg', // Ignored cases: 'phone-right-outline-24.svg': '', 'phone-right-solid-24.svg': '', ); @function get-rtl-svg($svg) { @each $ltr, $rtl in $rtl-icon-map { @if str-index($svg, $ltr) { @if $rtl == '' { @return $ltr; } @return str-replace($svg, $ltr, $rtl); } } @if str-index($svg, 'left') or str-index($svg, 'right') { @error "Missing RTL icon for #{$svg}"; } @return false; } @mixin color-svg($svg, $color, $stretch: true, $mask-origin: null) { -webkit-mask: url($svg) no-repeat center; @if $stretch { -webkit-mask-size: 100%; } @if $mask-origin { -webkit-mask-origin: $mask-origin; } background-color: $color; @media (forced-colors: active) { background-color: WindowText; } $rtl-svg: get-rtl-svg($svg); @if $rtl-svg { :dir(rtl) & { -webkit-mask: url($rtl-svg) no-repeat center; } } } // Keyboard @mixin keyboard-mode() { .keyboard-mode & { @content; } } @mixin mouse-mode() { .mouse-mode & { @content; } } @mixin dark-keyboard-mode() { .dark-theme.keyboard-mode & { @content; } } @mixin dark-mouse-mode() { .dark-theme.mouse-mode & { @content; } } // Other @mixin popper-shadow() { box-shadow: 0px 8px 20px rgba(0, 0, 0, 0.3), 0px 0px 8px rgba(0, 0, 0, 0.05); @media (forced-colors: active) { border: 1px solid WindowText; } } @mixin button-reset { background: none; color: inherit; border: none; padding: 0; margin: 0; font: inherit; cursor: pointer; outline: inherit; text-align: inherit; @media (forced-colors: active) { border: 1px solid WindowText; } } @mixin staged-attachment-close-button { @include button-reset; position: absolute; top: 4px; inset-inline-end: 4px; width: 16px; height: 16px; z-index: $z-index-above-base; } @mixin calling-text-shadow { text-shadow: 0 0 4px $color-black-alpha-40; } @mixin lonely-local-video-preview { max-height: calc(100% - 24px); height: auto; transform: rotateY(180deg); width: calc(100% - 24px); border-radius: 8px; } // --- Buttons // Individual traits @mixin button-focus-outline { &:focus { @include keyboard-mode { box-shadow: 0px 0px 0px 3px $color-ultramarine; } @include dark-keyboard-mode { box-shadow: 0px 0px 0px 3px $color-ultramarine-light; } } } @mixin button-blue-text { @include light-theme { color: $color-ultramarine; } @include dark-theme { color: $color-ultramarine-light; } } // Complete button styles @mixin button-primary { background-color: $color-ultramarine; // Note: the background colors here need to match the parent component @include light-theme { color: $color-white; border: 1px solid white; } @include dark-theme { color: $color-white-alpha-90; border: 1px solid $color-gray-95; } &:hover { @include mouse-mode { background-color: mix($color-black, $color-ultramarine, 15%); } @include dark-mouse-mode { background-color: mix($color-white, $color-ultramarine, 15%); } } &:active { // We need to include all four here for specificity precedence @include mouse-mode { background-color: mix($color-black, $color-ultramarine, 25%); } @include dark-mouse-mode { background-color: mix($color-white, $color-ultramarine, 25%); } @include keyboard-mode { background-color: mix($color-black, $color-ultramarine, 25%); } @include dark-keyboard-mode { background-color: mix($color-black, $color-ultramarine, 25%); } } @include button-focus-outline; } @mixin button-secondary { @include light-theme { color: $color-gray-90; background-color: $color-gray-05; } @include dark-theme { color: $color-gray-05; background-color: $color-gray-65; } &:hover { @include mouse-mode { background-color: mix($color-black, $color-gray-05, 15%); } @include dark-mouse-mode { background-color: mix($color-white, $color-gray-65, 15%); } } &:active { // We need to include all four here for specificity precedence @include mouse-mode { background-color: mix($color-black, $color-gray-05, 25%); } @include dark-mouse-mode { background-color: mix($color-white, $color-gray-65, 25%); } @include keyboard-mode { background-color: mix($color-black, $color-gray-05, 25%); } @include dark-keyboard-mode { background-color: mix($color-white, $color-gray-65, 25%); } } @include button-focus-outline; } @mixin button-secondary-blue-text { @include button-secondary; @include button-blue-text; } @mixin button-light { @include light-theme { color: $color-gray-90; background-color: $color-gray-02; } @include dark-theme { color: $color-gray-05; background-color: $color-gray-75; } &:hover { @include mouse-mode { background-color: mix($color-black, $color-gray-02, 10%); } @include dark-mouse-mode { background-color: mix($color-white, $color-gray-75, 10%); } } &:active { // We need to include all four here for specificity precedence @include mouse-mode { background-color: mix($color-black, $color-gray-02, 20%); } @include dark-mouse-mode { background-color: mix($color-white, $color-gray-75, 20%); } @include keyboard-mode { background-color: mix($color-black, $color-gray-02, 20%); } @include dark-keyboard-mode { background-color: mix($color-white, $color-gray-75, 20%); } } @include button-focus-outline; } @mixin button-light-blue-text { @include button-light; @include button-blue-text; } @mixin button-destructive { @include light-theme { color: $color-white; background-color: $color-accent-red; } @include dark-theme { color: $color-white-alpha-90; background-color: $color-accent-red; } &:hover { @include mouse-mode { background-color: mix($color-black, $color-accent-red, 15%); } @include dark-mouse-mode { background-color: mix($color-white, $color-accent-red, 15%); } } &:active { // We need to include all four here for specificity precedence @include mouse-mode { background-color: mix($color-black, $color-accent-red, 25%); } @include dark-mouse-mode { background-color: mix($color-white, $color-accent-red, 25%); } @include keyboard-mode { background-color: mix($color-black, $color-accent-red, 25%); } @include dark-keyboard-mode { background-color: mix($color-white, $color-accent-red, 25%); } } @include button-focus-outline; } @mixin button-green { $background-color: $color-accent-green; background-color: $background-color; color: $color-white; &:active { // We need to include all four here for specificity precedence @include mouse-mode { background-color: mix($color-black, $background-color, 25%); } @include dark-mouse-mode { background-color: mix($color-white, $background-color, 25%); } @include keyboard-mode { background-color: mix($color-black, $background-color, 25%); } @include dark-keyboard-mode { background-color: mix($color-white, $background-color, 25%); } } &[disabled] { opacity: 0.6; } @include button-focus-outline; } @mixin button-small { @include rounded-corners; padding-block: 7px; padding-inline: 14px; } // Modals @mixin modal-reset { @include popper-shadow(); border-radius: 8px; margin-block: 0; margin-inline: auto; max-height: 100%; max-width: 360px; padding: 16px; position: relative; width: 95%; display: flex; flex-direction: column; @include light-theme() { background: $color-white; color: $color-gray-90; } @include dark-theme() { background: $color-gray-95; color: $color-gray-05; } } @mixin modal-close-button { @include button-reset; position: absolute; inset-inline-end: 12px; top: 12px; height: 24px; width: 24px; @include light-theme { @include color-svg('../images/icons/v3/x/x.svg', $color-gray-75); } @include dark-theme { @include color-svg('../images/icons/v3/x/x.svg', $color-gray-15); } &:focus { @include keyboard-mode { background-color: $color-ultramarine; } @include dark-keyboard-mode { background-color: $color-ultramarine-light; } } } @mixin color-bubble($bubble-size) { background-clip: content-box; border-color: transparent; border-radius: $bubble-size; border-style: solid; border-width: 4px; cursor: pointer; height: $bubble-size; padding: 2px; width: $bubble-size; @each $color, $value in $conversation-colors { &--#{$color} { background-color: $value; } } @each $color, $value in $conversation-colors-gradient { &--#{$color} { background-image: linear-gradient( map-get($value, 'deg'), map-get($value, 'start'), map-get($value, 'end') ); } } } @mixin avatar-colors { @each $color, $value in $avatar-colors { &--#{$color} { --bg: #{map-get($value, 'bg')}; --fg: #{map-get($value, 'fg')}; background-color: var(--bg); color: var(--fg); &--icon { background-color: var(--fg); @include dark-theme { // For specificity background-color: var(--fg); } } } } } @mixin scrollbar { &::-webkit-scrollbar-thumb { border-radius: 4px; visibility: hidden; width: 6px; @include light-theme { background: $color-black-alpha-40; } @include dark-theme { background: $color-white-alpha-40; } } &::-webkit-scrollbar { background: transparent; } &::-webkit-scrollbar-track { background: transparent; } &:hover::-webkit-scrollbar-thumb { visibility: visible; } } @mixin normal-input { @include font-body-1; padding-block: 8px; padding-inline: 12px; border-radius: 6px; border-width: 2px; border-style: solid; width: 100%; @include light-theme { background: $color-white; color: $color-black; border-color: $color-gray-15; &:disabled { background: $color-gray-02; border-color: $color-gray-05; color: $color-gray-90; } } @include dark-theme { background: $color-gray-80; color: $color-gray-05; border-color: $color-gray-45; &:disabled { background: $color-gray-95; border-color: $color-gray-60; color: $color-gray-20; } } &:focus { outline: none; @include light-theme { border-color: $color-ultramarine; } @include dark-theme { border-color: $color-ultramarine-light; } } } @mixin install-screen { align-items: center; display: flex; width: 100vw; height: 100vh; justify-content: center; line-height: 30px; user-select: none; @include light-theme { background: $color-gray-02; color: $color-black; } @include dark-theme { background: $color-gray-95; color: $color-white; } h1 { @include font-title-2; } h2 { @include font-body-1; font-weight: normal; } } @mixin timeline-floating-header-node { @include rounded-corners; box-shadow: 0 1px 4px $color-black-alpha-20; @include light-theme { background: $color-white; } @include dark-theme { background: $color-gray-80; } } @mixin sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border-width: 0; } @mixin disabled { &:is(:disabled, [aria-disabled='true']) { @content; } } @mixin not-disabled { &:not(:disabled):not([aria-disabled='true']) { @content; } } @mixin position-absolute-center { position: absolute; top: 50%; /* stylelint-disable-next-line liberty/use-logical-spec */ left: 50%; /* stylelint-disable-next-line declaration-property-value-disallowed-list */ transform: translate(-50%, -50%); } @mixin position-absolute-center-x { position: absolute; /* stylelint-disable-next-line liberty/use-logical-spec */ left: 50%; /* stylelint-disable-next-line declaration-property-value-disallowed-list */ transform: translateX(-50%); } @mixin position-absolute-center-y { position: absolute; top: 50%; transform: translateY(-50%); } @mixin NavTabs__Scroller { padding-bottom: 8px; @include scrollbar; &::-webkit-scrollbar-thumb { @include light-theme { background: $color-gray-25; border-color: $color-gray-04; } @include dark-theme { background: $color-gray-45; border-color: $color-gray-80; } } } @mixin draggable-region { -webkit-app-region: drag; body.context-menu-open & { -webkit-app-region: no-drag; } } @mixin tooltip { & { @include font-body-2; @include light-theme { background-color: $color-gray-04; color: $color-black; outline: 1px solid $color-gray-20; } @include dark-theme { background-color: $color-gray-80; color: $color-gray-15; outline: 1px solid $color-gray-62; } padding-block: 5px; padding-inline: 12px; border-radius: 6px; filter: drop-shadow(0px 4px 3px $color-black-alpha-16); pointer-events: none; } & .module-tooltip-arrow::before { position: absolute; content: ''; border-style: solid; border-width: 7px; } &[data-placement='bottom'] .module-tooltip-arrow::before { @include light-theme { border-color: transparent transparent $color-gray-20 transparent; } @include dark-theme { border-color: transparent transparent $color-gray-62 transparent; } margin-top: -14px; /* stylelint-disable-next-line liberty/use-logical-spec */ margin-left: -7px; } &[data-placement='bottom'] .module-tooltip-arrow::after { @include light-theme { border-bottom-color: $color-gray-04; } @include dark-theme { border-bottom-color: $color-gray-80; } } &[data-placement='top'] .module-tooltip-arrow::before { @include light-theme { border-color: $color-gray-20 transparent transparent transparent; } @include dark-theme { border-color: $color-gray-62 transparent transparent transparent; } margin-top: 0; /* stylelint-disable-next-line liberty/use-logical-spec */ margin-left: -7px; } &[data-placement='top'] .module-tooltip-arrow::after { @include light-theme { border-top-color: $color-gray-04; } @include dark-theme { border-top-color: $color-gray-80; } } } @mixin button-active-call { $background: $color-accent-green; @include font-body-2-bold; @include rounded-corners; display: flex; width: auto; align-items: center; background-color: $background; color: $color-white; outline: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; user-select: none; &:before { $icon-size: 16px; @include color-svg( '../images/icons/v3/video/video-compact-fill.svg', $color-white ); content: ''; display: block; height: $icon-size; margin-inline-end: 4px; min-width: $icon-size; width: $icon-size; } &:not(:disabled) { &:hover { @include any-theme { background-color: darken($background, 16%); } } &:focus { @include keyboard-mode { background-color: darken($background, 16%); } } } }