Files
upstream-zulip/web/styles/app_components.css
upstream-mirror 64898b2c79
Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled
snapshot of https://github.com/zulip/zulip.git @ 48c69911c7dd4e03b20552eb3bd77ad8151c1357 (2026-09-15)
2026-09-15 15:05:30 +05:00

1455 lines
34 KiB
CSS

/* Reusable, object-oriented CSS base components for the Zulip web app
(not included in the portico CSS) */
.flex,
.show.flex {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.hide {
display: none;
}
.show {
display: block;
}
.hidden {
display: none;
visibility: hidden;
}
kbd {
display: inline-block;
border: 1px solid var(--color-kbd-border);
border-radius: 4px;
font-weight: 600;
text-align: center;
white-space: nowrap;
background-color: var(--color-kbd-background);
color: var(--color-kbd-text);
margin: 0.25em 0.1em;
padding: 0.2em 0.4em;
text-shadow: 0 1px 0 hsl(0deg 0% 100%);
/* Prevent selection */
user-select: none;
position: relative;
min-height: 1em;
min-width: 1em;
.zulip-icon::before {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
@media (width < $sm_min) {
.hide-sm {
display: none !important;
}
}
.highlighted-element {
font-weight: 600;
}
.inline-block {
display: inline-block;
}
.display-block {
display: block !important;
}
.box-shadow {
box-shadow: 0 0 10px hsl(0deg 0% 0% / 10%);
}
.clear-float {
clear: both;
}
.invisible {
visibility: hidden;
}
.order-1 {
order: 1;
}
.order-2 {
order: 2;
}
.order-3 {
order: 3;
}
.lowercase {
text-transform: lowercase;
}
/*
Consistent placeholder styling, introduced to allow us to style the
Reply box like a placeholder. Chrome uses color to set placeholder,
while Firefox uses opacity, so we need to set both properties to avoid
mixed styling.
While we usually prefer opacity for text color in Zulip, there's some
evidence Edge may have bugs in its handling of placeholder opacity
CSS: https://github.com/necolas/normalize.css/issues/741
*/
.placeholder {
color: var(--color-text-placeholder);
opacity: 1;
}
textarea::placeholder,
input::placeholder {
@extend .placeholder;
}
/* Custom resizer icon for the textarea */
textarea::-webkit-resizer {
background-image: var(--svg-resize-handle);
}
/* -- tab switcher -- */
.tab-switcher {
font-weight: initial;
margin: 2px 4px;
display: inline-block;
.ind-tab {
display: inline-block;
width: 90px;
margin: 0 -0.5px;
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
vertical-align: bottom; /* See https://stackoverflow.com/a/43266155/ */
padding: 3px 10px;
background-color: var(--color-background-tab-switcher-ind-tab);
cursor: pointer;
justify-content: center;
align-items: center;
&:focus-visible {
outline: 1px solid var(--color-outline-focus);
outline-offset: -2px;
}
&:not(.selected) {
border: 1px solid var(--color-border-tab-switcher-ind-tab);
}
&.first {
border-radius: 5px 0 0 5px;
border-right: 1px solid transparent;
}
&.middle {
border-right: 1px solid transparent;
}
&.last {
border-radius: 0 5px 5px 0;
}
&.selected {
position: relative;
background-color: var(
--color-background-tab-switcher-ind-tab-selected
);
color: hsl(0deg 0% 100%);
border: 1px solid var(--color-border-tab-switcher-ind-tab-selected);
z-index: 2;
}
&.disabled {
cursor: default;
color: var(--color-text-tab-switcher-ind-tab-disabled);
border-color: var(--color-border-tab-switcher-ind-tab-disabled);
}
}
&.large .ind-tab {
width: 100%;
}
&.allow-overflow {
display: flex;
.ind-tab {
display: flex;
text-overflow: initial;
white-space: initial;
vertical-align: middle;
}
}
}
/* -- unified overlay design component -- */
div.overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
-webkit-overflow-scrolling: touch;
background-color: var(--color-background-overlay);
z-index: 105;
pointer-events: none;
opacity: 0;
visibility: hidden;
transition: 0.2s ease-in;
transition-property: opacity, visibility;
overflow: hidden;
.overlay-content {
transform: translateY(20px);
transition: transform 0.2s ease-in;
z-index: 102;
}
&.show {
opacity: 1;
pointer-events: all;
visibility: visible;
transition: opacity 0.2s ease-out;
.overlay-content {
transform: translateY(0);
transition-timing-function: ease-out;
}
}
.overlay-scroll-container {
max-height: 60vh;
padding: 15px;
}
.overlay-container {
background-color: var(--color-background-modal);
}
&:focus {
outline: none;
}
}
.input-append {
font-size: 90%;
letter-spacing: -0.3em;
display: block;
}
.input-append input[type="text"],
input.settings_text_input {
border-radius: 5px;
box-shadow: none;
margin: 0;
}
.clear_search_button {
grid-area: 1 / 2 / 2 / 3;
padding: 0;
background: none;
color: var(--color-icons-inbox);
opacity: 0.4;
display: grid;
border: none;
font-size: inherit;
&:hover {
opacity: 1;
}
&:focus,
&:focus-visible,
&:active {
box-shadow: none;
outline: none;
}
.zulip-icon-close {
align-self: center;
}
}
.settings-highlight-box {
margin: 0;
padding: 5px 10px;
background-color: var(--color-background-modal-bar);
border: 1px solid var(--color-border-modal-bar);
border-radius: 4px;
list-style-type: none;
}
.white-box {
background-color: var(--color-background-white-box);
border: 1px solid hsl(0deg 0% 87%);
}
.guest-avatar {
position: relative;
overflow: hidden;
&::after {
content: " ";
background-color: hsl(0deg 0% 47%);
position: absolute;
bottom: -30%;
right: -30%;
width: 50%;
height: 50%;
transform: rotate(45deg);
}
}
.dependent-settings-block {
margin: 15px 0 -5px 23px;
}
.edit-controls .fa-angle-right {
font-size: 0.9em;
-webkit-text-stroke: 0.05em;
position: relative;
margin: 0 5px;
top: 9px;
}
/* Standard loading indicators generated by the loading.ts API */
.loading_indicator_spinner {
/* If you change these, make sure to adjust the constants in
loading.make_indicator as well. 38px at 14px/1em */
height: 2.7143em;
width: 2.7143em;
float: left;
}
.loading_indicator_text {
/* If you change these, make sure to adjust the constants in
loading.make_indicator as well */
margin-left: 5px;
font-size: 1.2em;
font-weight: 300;
line-height: 38px;
}
.tip {
position: relative;
display: block;
margin: 10px 0;
background-color: var(--color-background-tip);
border: 1px solid var(--color-border-tip);
border-radius: 4px;
padding: 10px;
/* previously 1rem = 16px at 14px em = 1.1428em */
font-size: 1.1428em;
line-height: 1.5;
color: hsl(0deg 0% 40%);
&::before {
content: "\f0a2";
display: inline;
margin-right: 8px;
font-family: FontAwesome;
font-weight: 600;
}
}
.demo-organization-add-email-banner {
margin-bottom: 10px;
}
.create-group-button-container {
.upgrade-organization-banner-container {
display: flex;
justify-content: center;
}
}
.organization-upgrade-banner,
.mobile-push-notifications-banner,
.admin-permissions-banner,
.desktop-setting-notifications {
margin-bottom: 10px;
width: fit-content;
}
.mobile-push-notifications-banner,
.desktop-setting-notifications,
.organization-upgrade-banner {
.banner-content {
flex-wrap: nowrap;
.banner-action-buttons {
flex-wrap: nowrap;
}
}
}
.demo-organization-warning {
margin: 10px 0;
}
.user_status_icon_wrapper {
display: inline;
white-space: nowrap;
}
.user-status-microlayout {
display: inline-grid;
grid-template-columns: minmax(0, 1fr) auto;
.user-name {
overflow: hidden;
text-overflow: ellipsis;
}
}
/* We are mostly consistent in how we style
unread counts, except for starred messages.
This is the common section.
*/
.unread_count {
float: right;
padding: 0 4px;
/* 12px at 14px/em */
font-size: 0.8571em;
/* 16px at 12px/em, owing to font-size above. */
height: 1.3333em;
line-height: 1.3333em;
font-weight: normal;
border-radius: 4px;
background-color: var(--color-background-unread-counter);
color: hsl(0deg 0% 100%);
}
/* Unread-count attention levels. */
.prominent-count {
background-color: var(--color-background-unread-counter-prominent);
color: var(--color-unread-counter-prominent);
}
.normal-count {
background-color: var(--color-background-unread-counter-normal);
color: var(--color-unread-counter-normal);
box-shadow: var(--box-shadow-unread-counter-normal);
}
.quiet-count {
background-color: var(--color-background-unread-counter-quiet);
color: var(--color-unread-counter-quiet);
font-weight: 475;
}
.unread_mention_info:not(:empty) {
margin-right: 5px;
margin-left: 2px;
opacity: var(--opacity-unread-mention-info);
}
.unread_mention_info.no-display {
display: none;
}
/* Implement the web app's default-hidden convention for alert
elements. Portico pages lack this CSS and thus show them by
default. */
.alert {
display: none;
&.show {
display: block;
}
&#organization-status {
margin: 20px;
}
&.stream_create_info {
margin: 10px 10px 0;
}
.bankruptcy_unread_count {
font-weight: 600;
}
}
.only-visible-for-spectators {
display: none;
}
.spectator-view {
/* Add this class to elements which a
* spectator cannot use. */
.hidden-for-spectators {
display: none !important;
}
.only-visible-for-spectators {
display: revert;
}
}
.animated-purple-button {
color: hsl(0deg 0% 100%);
transition: 80ms linear;
transition-property: background-color, box-shadow;
box-shadow: none;
/* This color just passes WCAG AA */
background-color: hsl(240deg 96% 68%);
cursor: pointer;
border: none;
border-radius: 4px;
text-align: center;
white-space: nowrap;
&:hover {
/* This color passes WCAG AAA */
background-color: hsl(240deg 41% 50%);
box-shadow: 0 1px 4px hsl(0deg 0% 0% / 30%);
}
&:focus {
background-color: hsl(240deg 41% 50%);
box-shadow: 0 1px 4px 0
var(--color-box-shadow-animated-purple-button-focus);
outline: none;
}
&:disabled {
cursor: not-allowed;
opacity: 0.5;
}
}
.color_animated_button {
display: flex;
justify-content: center;
background-color: var(--color-background-animated-button);
color: hsl(0deg 0% 0%);
border-radius: 4px;
.color-animated-button-text {
color: var(--color-animated-button-text);
}
&:hover {
cursor: pointer;
background-color: var(--color-background-animated-button-hover);
color: hsl(0deg 0% 100%);
.color-animated-button-text {
color: hsl(0deg 0% 100%);
transition: color 0.2s ease;
}
transition: 0.2s ease;
transition-property: background-color, color;
}
.zulip-icon,
.color-animated-button-text {
padding: 6px 3px;
}
.fa {
position: relative;
top: 3px;
}
}
.table-striped {
table-layout: auto;
border-collapse: separate;
border-spacing: 0;
margin-bottom: 20px;
width: 100%;
&:not(.table-bordered) {
thead th {
border-top: 1px solid hsl(0deg 0% 0% / 20%) !important;
border-bottom: 1px solid hsl(0deg 0% 0% / 20%) !important;
}
tbody tr:first-child td {
border-top: 0;
}
}
& thead {
background-color: var(--color-background-table-header);
}
& thead th {
/* Prevent headers from collapsing.
For headers that sort, this keeps
the icon on the same line as the
header text. */
min-width: max-content;
white-space: nowrap;
color: inherit;
word-break: normal;
.table-sortable-arrow {
/* Sub alignment works perfectly in this context,
where the table header is a text node. */
vertical-align: sub;
transform: rotate(180deg);
opacity: 0;
transition: opacity 100ms ease-out;
}
&.descend .table-sortable-arrow {
transform: rotate(0deg);
}
&:not(.active)[data-sort]:hover .table-sortable-arrow {
opacity: 0.3;
}
&.active .table-sortable-arrow {
opacity: 1;
}
&.active {
opacity: 1;
transition: opacity 100ms ease-out;
}
&[data-sort]:hover {
cursor: pointer;
background-color: var(
--color-background-table-header-sortable-hover
);
}
}
tbody > tr:nth-child(odd) {
td {
background-color: var(--color-background-modal);
}
th {
background-color: var(--color-background-tbody-th-odd);
}
}
/* Force the actions column to use the minimum space necessary */
.actions {
width: 1%;
white-space: nowrap;
.icon-button {
display: inline-flex;
align-items: center;
min-height: 2.1875em; /* 35px at 16px/1em */
}
}
th.actions {
padding-right: 1em;
}
& th,
td {
padding: 4px 5px;
}
& th {
text-align: left;
}
& td {
border-top: 1px solid var(--color-border-table-striped);
}
}
.table-bordered {
border: 1px solid var(--color-border-table-bordered);
border-collapse: separate;
border-left: 0;
border-radius: 4px;
border-spacing: 0;
width: 100%;
margin-bottom: 20px;
& th,
td {
border-left: 1px solid var(--color-border-table-bordered);
border-top: 1px solid var(--color-border-table-bordered);
padding: 4px 5px;
text-align: left;
}
thead tr:first-child th {
border-top: 0;
}
thead tr:first-child > th:first-child {
border-top-left-radius: 4px;
}
thead tr:first-child > th:last-child {
border-top-right-radius: 4px;
}
tbody:last-child tr:last-child > td:first-child {
border-bottom-left-radius: 4px;
}
tbody:last-child tr:last-child > td:last-child {
border-bottom-right-radius: 4px;
}
}
.subsection-header {
display: flex;
flex-wrap: wrap;
margin: 0.2857em 0;
gap: 0 0.625em; /* 10px at 16px em */
align-items: center;
}
.save-button-controls {
display: flex;
align-items: center;
gap: 0.3125em; /* 5px at 16px em */
&.hide {
display: none;
}
.save-button[data-status="saved"] {
pointer-events: none;
}
}
.decorated-channel-name-wrapper:not(.inline-decorated-channel-name) {
display: flex;
gap: var(--decorated-channel-name-privacy-icon-gap);
flex-wrap: nowrap;
align-items: baseline;
.channel-privacy-type-icon .zulip-icon {
/* Calculated from visual inspection
to look good for 12px-20px font sizes. */
top: 0.1467em; /* 2.2px at 15px/1em */
font-size: 0.9375em; /* 15px at 16px/1em */
&.zulip-icon-lock {
/* Calculated from visual inspection
to look good for 12px-20px font sizes. */
top: 0.08em; /* 1.2px at 15px/1em */
}
}
}
.inline-decorated-channel-name {
white-space: nowrap;
display: inline;
padding-left: var(--inline-decorated-channel-name-padding-left);
.decorated-channel-name {
margin-left: var(--inline-decorated-channel-name-privacy-icon-gap);
}
}
.channel-privacy-type-icon .zulip-icon {
position: relative;
/* Calculated from visual inspection
to look good for 12px-20px font sizes. */
top: 0.0688em; /* 1.1px at 16px/1em */
&.zulip-icon-globe,
&.zulip-icon-archive,
&.zulip-icon-hashtag {
font-size: 0.75em;
}
&.zulip-icon-lock,
&.zulip-icon-users {
font-size: 0.8em;
}
}
.zulip-icon-lock {
/* The lock icon is bottom heavy, so to make it
appear better aligned, we pull it up by a
uniform margin throughout the UI. */
/* 3.5px at 16px/1em */
margin-top: -0.2188em !important;
}
/* This includes css needed to display messages in an overlay. */
.overlay-messages-container {
position: relative;
height: 95%;
background-color: var(--color-background-modal);
border-radius: 4px;
padding: 0;
width: 58%;
overflow: hidden;
max-width: 1200px;
max-height: 1000px;
display: flex;
flex-direction: column;
@media (width < $md_min) {
height: 95%;
max-width: none;
width: 90%;
}
.overlay-messages-header {
padding-top: 4px;
padding-bottom: 8px;
text-align: center;
background-color: var(--color-background-modal-bar);
border-bottom: 1px solid var(--color-border-modal-bar);
& h1 {
margin: 0;
font-size: 1.1em;
font-weight: bold;
line-height: var(--header-line-height);
text-transform: uppercase;
}
.exit {
font-weight: 400;
position: absolute;
top: 10px;
right: 10px;
color: hsl(0deg 0% 67%);
cursor: pointer;
.exit-sign {
position: relative;
top: 1px;
margin-left: 3px;
font-size: 1.5rem;
line-height: 1;
font-weight: 600;
cursor: pointer;
}
}
}
.overlay-messages-list {
padding: 10px 25px;
overflow: auto;
.no-overlay-messages {
display: block;
margin-top: calc(45vh - 30px - 1.5em);
text-align: center;
font-size: 1.5em;
color: hsl(0deg 0% 67%);
pointer-events: none;
}
}
}
/* This is a useful grid-based mechanism to handle
message overlays with a header and scrollable
content area managed by SimpleBar. It more
robustly fills the height of the modal than
flexbox, and should be considered as an approach
for other modals that currently employ flexbox. */
.overlay-messages-container.drafts-container {
display: grid;
grid-template-rows: auto minmax(0, 1fr);
/* min-height: 0 lets the tab pane shrink inside the 1fr grid row
(grid items default to min-height: auto). */
.drafts-tab-pane {
min-height: 0;
}
.overlay-messages-list {
/* We unset the `overflow` value so SimpleBar
can manage scrolling. */
overflow: unset;
/* border-box + height: 100% makes the list fill (not overflow) the
tab pane, which the 1fr grid row bounds, so SimpleBar has a
definite height to scroll within. */
box-sizing: border-box;
height: 100%;
/* And therefore we don't need to worry about padding,
so that the scrollbar appears to reach all the way
to the bottom of the content. */
padding-bottom: 0;
/* Override the `min-width: auto` default for grid
items so the SimpleBar container can shrink on
window resize. */
min-width: 0;
}
}
.overlay-messages-container .overlay-message-row {
padding: 5px 0;
.overlay-message-info-box {
width: 100%;
margin-bottom: 10px;
&.active {
outline: 2px solid var(--color-outline-focus);
border-radius: 7px;
}
.message_row {
border-radius: 0 0 7px 7px;
border: 1px solid var(--color-message-list-border);
border-top: 0;
}
.messagebox-content {
grid-template: auto / auto max-content;
grid-template-areas:
"message controls"
"message . ";
/* to space from restore draft button */
column-gap: 5px;
padding: 10px;
.message_content {
cursor: pointer;
grid-area: message;
}
.message_top_line {
grid-area: controls;
}
}
.messagebox {
cursor: auto;
box-shadow: none;
border-radius: 0 0 7px 7px;
}
.overlay_message_controls {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
font-size: 0.9em;
[data-tippy-root] {
width: max-content;
overflow-wrap: unset;
}
.copy-overlay-message {
font-size: 1.2em;
}
.restore-overlay-message {
cursor: pointer;
color: hsl(170deg 48% 54%);
opacity: 0.7;
&:hover {
opacity: 1;
}
}
.delete-overlay-message {
cursor: pointer;
color: hsl(357deg 52% 57%);
opacity: 0.7;
padding: 0;
font-size: 1.2em;
&:hover {
opacity: 1;
}
}
}
.message_header {
/* We don't need these effects applied for message list in the drafts overlay. */
box-shadow: none !important;
border: 0 !important;
background: transparent;
}
}
}
div.overlay .flex.overlay-content > .overlay-container,
#settings_page,
.informational-overlays .overlay-content {
box-shadow: var(--box-shadow-overlay);
}
.delete-selected-drafts-button-container {
display: flex;
}
.dropdown-widget-button {
background-color: var(--color-background-dropdown-widget-button);
padding: 4px 6px;
border: 1px solid var(--color-border-dropdown-widget-button);
border-radius: 4px;
display: flex;
align-items: center;
justify-content: space-between;
outline: none;
color: var(--color-text-default);
width: var(--width-dropdown-widget-button);
font-family: inherit;
font-size: inherit;
&:disabled {
cursor: not-allowed;
background-color: var(
--color-background-disabled-dropdown-widget-button
);
/* TODO: Should this be 0.5 to match the spectator block below? */
opacity: 0.7;
}
}
.dropdown-widget-disabled-for-spectators .dropdown-widget-button {
cursor: not-allowed;
opacity: 0.5;
}
#edit_bot_owner_widget .dropdown_widget_value {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.dropdown_widget_with_label_wrapper {
margin-top: 0 !important;
}
.dropdown-current-value-not-in-options,
.setting-disabled-option {
font-style: italic;
.setting-disabled-option-icon {
align-self: center;
}
}
.empty-list-message,
.empty-table-message {
background-color: inherit;
color: var(--color-text-default);
font-size: 1.5em;
padding: 3em 1em !important;
text-align: center;
}
.filter_text_input {
padding: var(--input-vertical-padding) var(--input-horizontal-padding);
color: var(--color-text-default);
border: var(--input-border-width) solid hsl(0deg 0% 80%);
transition:
border-color linear 0.2s,
box-shadow linear 0.2s;
box-shadow: inset 0 1px 1px hsl(0deg 0% 0% / 7.5%);
border-radius: 4px;
font-family: inherit;
&:focus {
border-color: hsl(206deg 80% 62% / 80%);
outline: 0;
box-shadow:
inset 0 1px 1px hsl(0deg 0% 0% / 7.5%),
0 0 8px hsl(206deg 80% 62% / 60%);
}
}
/**
* Use the "tab-picker-vertical" class in conjunction
* for a vertical tab picker.
*/
.tab-picker {
position: relative;
display: grid;
grid-auto-flow: column;
grid-auto-columns: 1fr;
gap: var(--grid-gap-tab-picker);
box-sizing: border-box;
border-radius: 5px;
/* Using max-content ensures equal width tabs. */
min-width: max-content;
padding: 1px;
background-color: var(--color-background-tab-picker-container);
/* 16px at 15px/1em */
font-size: 1.0666em;
input[type="radio"] {
display: none;
}
.tab-option-content {
z-index: 2;
display: flex;
justify-content: center;
align-items: center;
/* Avoids layout shift while showing a border when pressed. */
border: 1px solid transparent;
border-radius: 4px;
box-sizing: border-box;
padding: 3px 7px;
color: var(--color-text-dropdown-menu);
& .zulip-icon {
/* Clear inherited position. */
position: static;
color: var(--color-icon-purple);
}
}
.tab-option:not(:checked) + .tab-option-content {
&:hover,
&:focus-visible {
background-color: var(
--color-background-tab-picker-tab-option-hover
);
}
&:active {
background-color: var(
--color-background-tab-picker-tab-option-active
);
}
}
.tab-option {
&:nth-of-type(1):checked ~ .slider {
--tab-selected-tab-picker: 0;
}
&:nth-of-type(2):checked ~ .slider {
--tab-selected-tab-picker: 1;
}
&:nth-of-type(3):checked ~ .slider {
--tab-selected-tab-picker: 2;
}
&:nth-of-type(4):checked ~ .slider {
--tab-selected-tab-picker: 3;
}
&:nth-of-type(5):checked ~ .slider {
--tab-selected-tab-picker: 4;
}
/* If a tab picker with more than 5 tabs is required,
extend this using the same pattern as above. */
}
.slider {
position: absolute;
z-index: 1;
inset: 0;
grid-column: 1 / 2;
grid-row: 1 / 2;
/* Move along X-axis: own width + gap between two tabs. */
transform: translateX(
calc(
(100% + var(--grid-gap-tab-picker)) *
var(--tab-selected-tab-picker)
)
);
transition: transform 0.25s cubic-bezier(0.64, 0, 0.78, 0);
box-sizing: border-box;
border: 1px solid var(--color-outline-tab-picker-tab-option);
border-radius: 4px;
background-color: var(--color-background-tab-picker-selected-tab);
}
/* Detect if a user has enabled a setting on their device
to minimize the amount of non-essential motion. */
@media (prefers-reduced-motion) {
.slider {
transition: none;
}
.tab-option:not(:checked) + .tab-option-content:active {
border-color: var(--color-outline-tab-picker-tab-option);
}
}
/* Related to vertical tab picker. */
&.tab-picker-vertical {
grid-auto-flow: row;
grid-auto-rows: 1fr;
.slider {
/* Move along Y-axis: own height + gap between two tabs. */
transform: translateY(
calc(
(100% + var(--grid-gap-tab-picker)) *
var(--tab-selected-tab-picker)
)
);
}
}
}
.copy-button {
display: flex;
border-radius: 4px;
color: var(--color-copy-button);
/* 2px at 16px/1em */
padding: 0.125em;
cursor: pointer;
&:hover,
&:focus-visible {
color: var(--color-copy-button-hover);
}
&:active {
color: var(--color-copy-button-active);
}
&.copy-button-square {
&:hover,
:focus-visible {
background-color: var(--color-copy-button-square-bg-hover);
}
&:active {
background-color: var(--color-copy-button-square-bg-active);
color: var(--color-copy-button-square-active);
}
}
&.copy-button-success {
color: var(--color-copy-button-success);
}
}
.text-success {
color: hsl(120.91deg 32.04% 40.39%);
}
.text-error {
color: hsl(1.06deg 44.66% 50.39%);
}
.sticky-bottom-option-button {
width: 100%;
display: flex;
position: relative;
padding: 7px 14px;
background-color: var(--color-background-neutral-subtle-action-button);
justify-content: flex-start;
color: var(--color-dropdown-item);
border: 1px solid var(--color-border-zulip-button);
border-radius: 4px;
font-family: inherit;
font-size: inherit;
&:hover {
color: var(--color-dropdown-item);
background-color: var(
--color-background-neutral-subtle-action-button-hover
);
border-color: var(--color-border-zulip-button-interactive);
}
&:focus,
&.current_selection {
background-color: var(
--color-background-neutral-subtle-action-button-hover
);
outline: none;
}
&:focus::before,
&.current_selection::before {
content: "";
position: absolute;
left: 0;
top: 1px;
bottom: 2px;
width: 3px;
background-color: var(--color-outline-focus);
border-radius: 4px;
}
}
/* Styling for invalid inputs */
input.invalid-input {
/* The use of !important is necessary here to override
border-color specified for text input elements in
dark_theme.css which has a higher specificity. */
border-color: var(--color-invalid-input-border) !important;
&:focus {
box-shadow: var(--invalid-input-box-shadow);
}
}
/* This is a utility class for line clamping. */
.line-clamp {
/* Despite the below prefixed properties being deprecated,
the co-dependency of these three properties is a fully
specified behavior and will continue to be supported.
Reference: https://developer.mozilla.org/en-US/docs/Web/CSS/line-clamp */
display: -webkit-box;
-webkit-box-orient: vertical;
/* The value of --line-clamp-value is not defined globally and instead
inherits the value from another class or the parent element. If it
is not defined, it defaults to 2. */
-webkit-line-clamp: var(--line-clamp-value, 2);
/* Also define the standard property 'line-clamp' for compatibility,
for when the vendor prefix "-webkit-" is no longer required. */
line-clamp: var(--line-clamp-value, 2);
text-overflow: ellipsis;
overflow: hidden;
}
.info-density-controls {
.button-group {
border-radius: 5px;
border: 1px solid var(--color-info-density-control-border);
width: 100%;
display: grid;
grid-auto-flow: column;
.info-density-button-container {
display: inline-flex;
flex-direction: column;
&:first-of-type {
.info-density-button {
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
}
}
&:last-of-type {
.info-density-button {
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
}
}
}
.info-density-button {
border: none;
background-color: var(--color-background-popover);
padding: 0.25em 0.4375em;
display: inline-flex;
align-items: center;
vertical-align: unset;
justify-content: center;
margin: 0;
font-size: inherit;
&:hover:not(:disabled) {
background-color: var(
--color-info-density-button-hover-background
);
}
&:focus {
outline: none;
}
&:disabled {
cursor: default;
opacity: 0.4;
}
.zulip-icon {
display: flex;
align-items: center;
color: var(--color-info-density-button-icon);
opacity: 0.7;
}
}
}
}
#uppy-editor {
.modal__title {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.loading-placeholder {
position: absolute;
top: calc(50% - 1.3572em);
left: calc(50% - 1.3572em);
}
.modal__content {
padding: 0;
max-width: 100%;
width: 100%;
}
#dialog_error {
margin: 0 1.5em 1.5em;
}
.image-cropper-container {
height: 550px;
}
.uppy-Root {
height: 100%;
overflow: hidden;
}
.uppy-ImageCropper-controls {
/* This is needed to override the top padding set for narrow
screens by cropperJS. */
padding-top: 0;
}
.uppy-c-btn {
&:focus:not(:focus-visible) {
background-color: unset;
}
&:focus-visible {
background-color: hsl(0deg 0% 100% / 50%);
}
}
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.zulip-icon-loader-circle {
animation: spin 1s linear infinite;
}