snapshot of https://github.com/zulip/zulip.git @ 48c69911c7dd4e03b20552eb3bd77ad8151c1357 (2026-09-15)
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

This commit is contained in:
upstream-mirror
2026-09-15 15:05:30 +05:00
commit 64898b2c79
8999 changed files with 1757358 additions and 0 deletions

172
web/styles/alerts.css Normal file
View File

@@ -0,0 +1,172 @@
:root {
--color-alert-red: hsl(16deg 60% 45%);
--color-alert-error-red: hsl(0deg 80% 40%);
}
.alert-display {
display: none;
&.show {
display: block;
}
}
.alert-animations {
/* TODO: Remove these animations in favour of those
in web/styles/banners.css, once all the alert popups
have been converted to use the new banner component. */
&.show {
animation-name: fadeIn;
animation-duration: 0.3s;
animation-fill-mode: forwards;
}
&.fade-out {
animation-name: fadeOut;
animation-duration: 0.3s;
animation-fill-mode: forwards;
}
.faded {
opacity: 0.7;
}
}
.home-error-bar {
display: none;
}
/* alert box component changes */
#alert-popup-container {
/* Ensure alert box remains in viewport, regardless of scroll
position in message list. */
position: fixed;
/* Offset to account for the top padding + 5px from the top. */
top: calc(5px + (-1 * var(--popup-banner-translate-y-distance)));
left: 50%;
transform: translateX(-50%);
width: 100%;
max-width: 900px;
z-index: 220;
max-height: 100%;
overflow-y: auto;
overscroll-behavior: contain;
/* Set top padding to account for the translate-y motion of the
animation to prevent the vertical scroll bar from appearing. */
padding-top: var(--popup-banner-translate-y-distance);
@media (width < $lg_min) {
max-width: 90%;
}
}
.alert-box {
display: flex;
/* Using column-reverse flex direction enables a stack-like
behavior where the most recent alert is always on top. */
flex-direction: column-reverse;
justify-content: center;
gap: 5px;
.alert {
@extend .alert-animations;
box-sizing: border-box;
width: 100%;
border-radius: 4px;
background-color: hsl(0deg 0% 100%);
position: relative;
/* gives room for the error icon and the exit button. */
padding: 10px 50px;
text-shadow: none;
color: var(--color-alert-red);
border: 1px solid var(--color-alert-red);
box-shadow: 0 0 2px var(--color-alert-red);
&::before {
float: left;
margin-left: -38px;
font-family: FontAwesome;
font-size: 1.3em;
line-height: 1;
overflow: hidden;
content: "\f071";
color: hsl(16deg 60% 55%);
}
&::after {
clear: both;
content: "";
display: table;
}
&.alert-error {
color: var(--color-alert-error-red);
border: 1px solid var(--color-alert-error-red);
box-shadow: 0 0 2px var(--color-alert-error-red);
&::before {
color: var(--color-alert-error-red);
}
}
.exit {
float: right;
margin: -10px -50px -10px 0;
padding: 13px 10px;
font-size: 2.5em;
font-weight: 300;
line-height: 1ex;
overflow: hidden;
cursor: pointer;
&::after {
content: "\d7";
}
}
}
}
/* animation section */
@keyframes fadeIn {
0% {
opacity: 0;
transform: translateY(
calc(-1 * var(--popup-banner-translate-y-distance))
);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeOut {
0% {
opacity: 1;
transform: translateY(0);
}
100% {
opacity: 0;
transform: translateY(
calc(-1 * var(--popup-banner-translate-y-distance))
);
}
}
.update-read-flags-for-narrow-banner .banner-close-button {
/* Disable pointer events to prevent user interaction,
since this button is repurposed as a loading indicator. */
pointer-events: none;
}

27
web/styles/animate.css vendored Normal file
View File

@@ -0,0 +1,27 @@
.content-expand-animation-start {
opacity: 0;
}
.content-expand-animation-end {
opacity: 1;
transition: opacity 0.2s ease-in-out;
}
.content-collapse-animation-start {
opacity: 1;
}
.content-collapse-animation-end {
opacity: 0;
transition: opacity 0.2s ease-in-out;
}
.collapse-expand-animation-extra-content-blocker {
z-index: 10000;
position: relative;
}
.mark-element-for-translation {
position: relative;
z-index: 1;
}

File diff suppressed because it is too large Load Diff

3306
web/styles/app_variables.css Normal file

File diff suppressed because it is too large Load Diff

232
web/styles/banners.css Normal file
View File

@@ -0,0 +1,232 @@
.banner-wrapper {
container: banner / inline-size;
}
.banner {
box-sizing: border-box;
display: grid;
grid-template: var(--banner-grid-template-rows) / var(
--banner-grid-template-columns
);
grid-template-areas: var(--banner-grid-template-areas);
place-items: start;
border: 1px solid;
border-radius: 6px;
}
.banner-link {
color: var(--color-text-link);
text-decoration: none;
text-decoration-color: var(--color-text-link-decoration);
&:hover {
color: var(--color-text-link-hover);
text-decoration-color: var(--color-text-link-decoration-hover);
}
&:active {
color: var(--color-text-link-active);
text-decoration-color: currentcolor;
}
}
.banner-content {
grid-area: banner-content;
display: flex;
align-items: flex-start;
flex-wrap: wrap;
width: 100%;
gap: 0 0.625em; /* 10px at 16px/1em */
}
.banner-label {
/* We allow the banner label to grow and shrink, and set
the flex basis to 60% of the query container's width.
When the width of the banner label goes below this
flex basis value, the banner action buttons are wrapped
on to the next line. */
flex: 1 1 60cqw;
/* The padding and line-height values for the banner label
are identical to those of the action buttons', to match
the height of both of these elements. This is required to
align the banner children elements vertically, since we
cannot use flexbox center alignment as we also need to
account for the UI when the banner has a muli-line label.
*/
padding: 0.25em 0; /* 4px at 16px/1em */
line-height: 1.25; /* 20px at 16px/1em */
text-align: start;
}
.banner-action-buttons {
display: flex;
flex-wrap: wrap;
gap: 0.5em; /* 8px at 16px/1em */
}
.banner-action-buttons:empty {
display: none;
}
.banner-close-button {
display: flex;
grid-area: banner-close-button;
padding: 0.6875em; /* 11px at 16px/1em */
margin-left: 0.3125em; /* 5px at 16px/1em */
&:focus-visible {
outline-offset: -2px;
}
}
.navbar-alert-banner {
border: unset;
border-bottom: 1px solid;
border-radius: 0;
place-items: start center;
.banner-content {
justify-content: center;
flex-wrap: nowrap;
}
.banner-label {
/* Reset to initial value */
flex: 0 1 auto;
}
.banner-action-buttons {
flex-wrap: nowrap;
}
}
/* This utility class defines the structure of the medium-type
navbar banners where the banner action buttons are placed
below the banner label. This utility class is required since
unlike the normal banners, the navbar counterparts have
horizontally centered elements, wherein the flex-basis logic
won't apply and we need to manually apply these properties
based on the container size queries below. */
.navbar-alert-medium-banner {
.banner-content {
flex-direction: column;
align-items: center;
}
.banner-label {
text-align: center;
}
.banner-action-buttons {
flex-wrap: wrap;
justify-content: center;
}
}
@container banner (width >= 44em) and (width < 63em) {
.navbar-alert-banner[data-process="desktop-notifications"] {
@extend .navbar-alert-medium-banner;
}
}
@container banner (width < 44em) {
.navbar-alert-banner {
@extend .navbar-alert-medium-banner;
}
}
@container banner (width < 25em) {
.banner-content {
flex-direction: column;
}
.banner-label {
/* Reset to initial value */
flex: 0 1 auto;
}
.banner-action-buttons {
flex-direction: column;
width: 100%;
}
}
.banner-neutral {
background-color: var(--color-background-neutral-banner);
color: var(--color-text-neutral-banner);
border-color: var(--color-border-neutral-banner);
}
.banner-brand {
background-color: var(--color-background-brand-banner);
color: var(--color-text-brand-banner);
border-color: var(--color-border-brand-banner);
}
.banner-info {
background-color: var(--color-background-info-banner);
color: var(--color-text-info-banner);
border-color: var(--color-border-info-banner);
}
.banner-success {
background-color: var(--color-background-success-banner);
color: var(--color-text-success-banner);
border-color: var(--color-border-success-banner);
}
.banner-warning {
background-color: var(--color-background-warning-banner);
color: var(--color-text-warning-banner);
border-color: var(--color-border-warning-banner);
}
.banner-danger {
background-color: var(--color-background-danger-banner);
color: var(--color-text-danger-banner);
border-color: var(--color-border-danger-banner);
}
@keyframes popup-banner-fadeIn {
0% {
opacity: 0;
transform: translateY(
calc(-1 * var(--popup-banner-translate-y-distance))
);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
@keyframes popup-banner-fadeOut {
0% {
opacity: 1;
transform: translateY(0);
}
100% {
opacity: 0;
transform: translateY(
calc(-1 * var(--popup-banner-translate-y-distance))
);
}
}
.popup-banner-animations {
animation-name: popup-banner-fadeIn;
animation-duration: 0.3s;
animation-fill-mode: forwards;
&.fade-out {
animation-name: popup-banner-fadeOut;
}
}
.popup-banner {
@extend .popup-banner-animations;
width: 100%;
}

154
web/styles/blueslip.css Normal file
View File

@@ -0,0 +1,154 @@
.blueslip-display {
display: none;
&.show {
display: block;
}
}
.blueslip-animations {
&.show {
animation-name: fadeIn;
animation-duration: 0.3s;
animation-fill-mode: forwards;
}
&.fade-out {
animation-name: fadeOut;
animation-duration: 0.3s;
animation-fill-mode: forwards;
}
}
.blueslip-error-container {
/* We define this variable here since this is shared
with portico. */
--blueslip-overlay-translate-y-distance: 100px;
position: fixed;
/* Offset to account for the top padding + 5px from the top. */
top: calc(5px + (-1 * var(--blueslip-overlay-translate-y-distance)));
left: 50%;
transform: translateX(-50%);
width: 100%;
max-width: min(calc(100% - 20px), 1100px);
z-index: 230;
/* Subtract 5px to account for the bottom margin and another
5px to account for the top. */
max-height: calc(100vh - 10px);
overflow-y: auto;
overscroll-behavior: contain;
/* Set top padding to account for the translate-y motion of the
animation to prevent the vertical scroll bar from appearing. */
padding-top: var(--blueslip-overlay-translate-y-distance);
display: flex;
/* Using column-reverse flex direction enables a stack-like
behavior where the most recent error is always on top. */
flex-direction: column-reverse;
gap: 5px;
}
.stacktrace {
@extend .blueslip-display, .blueslip-animations;
box-sizing: border-box;
width: 100%;
pointer-events: auto;
font-size: 1rem;
color: hsl(0deg 80% 40%);
padding: 1rem 0;
background-color: hsl(0deg 100% 98%);
border-radius: 4px;
border: 1px solid hsl(0deg 80% 40%);
box-shadow: 0 0 2px hsl(0deg 80% 40%);
.stacktrace-header {
display: flex;
justify-content: space-between;
align-items: center;
.message {
flex: 1 1 auto;
}
.warning-symbol,
.exit {
flex: 0 0 auto;
font-size: 1.3rem;
padding: 0 1rem;
}
.exit::after {
cursor: pointer;
font-size: 2.3rem;
content: "\d7";
line-height: 0.5;
}
}
.stacktrace-more-info {
font-size: 0.85rem;
white-space: pre;
font-family: "Source Code Pro", monospace;
margin-left: 3.3rem;
margin-bottom: 0.5rem;
padding: 0.5rem;
background-color: hsl(0deg 7% 98%);
}
.stacktrace-content {
font-family: "Source Code Pro", monospace;
font-size: 0.85rem;
margin-bottom: 0.5rem;
.stackframe {
overflow-wrap: break-word;
padding-left: calc(3.3rem - 14px);
padding-right: 1rem;
}
}
.expand {
cursor: pointer;
color: hsl(0deg 32% 83%);
&:hover {
color: hsl(0deg 0% 20%);
}
}
.subtle {
color: hsl(0deg 7% 45%);
}
.code-context {
color: hsl(0deg 7% 15%);
background-color: hsl(0deg 7% 98%);
box-shadow:
inset 0 11px 10px -10px hsl(0deg 7% 70%),
inset 0 -11px 10px -10px hsl(0deg 7% 70%);
margin-top: 1em;
margin-bottom: 1em;
.code-context-content {
padding: 1rem 0;
white-space: pre;
overflow-x: auto;
}
.line-number {
width: 3rem;
display: inline-block;
text-align: right;
color: hsl(0deg 7% 35%);
}
.focus-line {
background-color: hsl(0deg 7% 90%);
width: 100%;
}
}
}

73
web/styles/box_resize.css Normal file
View File

@@ -0,0 +1,73 @@
.resizable-box-handle {
position: absolute;
user-select: none;
touch-action: none;
background: transparent;
&.resizable-box-handle-top,
&.resizable-box-handle-bottom {
left: 0;
width: 100%;
height: var(--resizable-box-handle-size);
cursor: row-resize;
}
&.resizable-box-handle-left,
&.resizable-box-handle-right {
top: 0;
width: var(--resizable-box-handle-size);
height: 100%;
cursor: col-resize;
}
&.resizable-box-handle-top {
top: 0;
}
&.resizable-box-handle-bottom {
bottom: 0;
}
&.resizable-box-handle-left {
left: 0;
}
&.resizable-box-handle-right {
right: 0;
}
/* Corners sit above edges so they win pointer events in the
overlap region. */
&.resizable-box-handle-top-left,
&.resizable-box-handle-top-right,
&.resizable-box-handle-bottom-left,
&.resizable-box-handle-bottom-right {
width: var(--resizable-box-handle-size);
height: var(--resizable-box-handle-size);
z-index: 1;
}
&.resizable-box-handle-top-left {
top: 0;
left: 0;
cursor: nwse-resize;
}
&.resizable-box-handle-bottom-right {
bottom: 0;
right: 0;
cursor: nwse-resize;
}
&.resizable-box-handle-top-right {
top: 0;
right: 0;
cursor: nesw-resize;
}
&.resizable-box-handle-bottom-left {
bottom: 0;
left: 0;
cursor: nesw-resize;
}
}

678
web/styles/buttons.css Normal file
View File

@@ -0,0 +1,678 @@
.action-button {
position: relative;
display: flex;
gap: 0.75ch;
justify-content: center;
align-items: center;
line-height: 1.25;
font-size: var(--base-font-size-px);
font-family: inherit;
font-weight: 500;
letter-spacing: 0.02ch;
padding: 0.25em 0.625em;
color: var(--color-text-neutral-subtle-action-button);
background-color: var(--color-background-neutral-subtle-action-button);
border-radius: 4px;
white-space: nowrap;
user-select: none;
border: none;
cursor: pointer;
clip-path: inset(-1px);
transition: 0.05s ease-in;
transition-property: background-color, clip-path;
&:hover,
&:focus-visible {
background-color: var(
--color-background-neutral-subtle-action-button-hover
);
transition: 0.1s ease-out;
transition-property: background-color, clip-path;
}
&:active {
background-color: var(
--color-background-neutral-subtle-action-button-active
);
clip-path: inset(1px round 4px);
}
&:focus {
/* Override common button outline style set in zulip.css and dark_theme.css */
outline: none;
}
&:focus-visible {
/* Override common button outline style set in zulip.css and dark_theme.css */
outline: var(--outline-width-button-focus) solid
var(--color-outline-button-focus) !important;
outline-offset: var(--outline-offset-button-focus);
clip-path: inset(
calc(
-1 *
(
var(--outline-width-button-focus) +
var(--outline-offset-button-focus)
)
)
round 4px
);
}
&:disabled {
cursor: default;
pointer-events: none;
opacity: 0.5;
}
.zulip-icon {
/* We apply the same line-height to icons,
as those applied to the label element,
to maintain a consistent height for
icon-only action buttons. */
line-height: 1.25; /* 20px at 16px/1em */
}
}
.action-button-label {
max-width: 32ch;
text-overflow: ellipsis;
overflow: hidden;
}
/* Action buttons -- Neutral Intent */
.action-button-solid-neutral {
color: var(--color-text-neutral-solid-action-button);
background-color: var(--color-background-neutral-solid-action-button);
&:hover,
&:focus-visible {
background-color: var(
--color-background-neutral-solid-action-button-hover
);
}
&:active {
background-color: var(
--color-background-neutral-solid-action-button-active
);
}
}
.action-button-subtle-neutral {
color: var(--color-text-neutral-subtle-action-button);
background-color: var(--color-background-neutral-subtle-action-button);
box-shadow: 0 0 0.5px 0.5px var(--color-inner-shadow-action-button) inset;
&:hover,
&:focus-visible {
background-color: var(
--color-background-neutral-subtle-action-button-hover
);
}
&:active {
background-color: var(
--color-background-neutral-subtle-action-button-active
);
}
}
.action-button-text-neutral {
color: var(--color-text-neutral-text-action-button);
background-color: transparent;
&:hover,
&:focus-visible {
background-color: var(
--color-background-neutral-text-action-button-hover
);
}
&:active {
background-color: var(
--color-background-neutral-text-action-button-active
);
}
}
/* Action buttons -- Brand Intent */
.action-button-solid-brand {
color: var(--color-text-brand-solid-action-button);
background-color: var(--color-background-brand-solid-action-button);
&:hover,
&:focus-visible {
background-color: var(
--color-background-brand-solid-action-button-hover
);
}
&:active {
background-color: var(
--color-background-brand-solid-action-button-active
);
}
}
.action-button-subtle-brand {
color: var(--color-text-brand-subtle-action-button);
background-color: var(--color-background-brand-subtle-action-button);
box-shadow: 0 0 0.5px 0.5px var(--color-inner-shadow-action-button) inset;
&:hover,
&:focus-visible {
background-color: var(
--color-background-brand-subtle-action-button-hover
);
}
&:active {
background-color: var(
--color-background-brand-subtle-action-button-active
);
}
}
.action-button-text-brand {
color: var(--color-text-brand-text-action-button);
background-color: transparent;
&:hover,
&:focus-visible {
background-color: var(
--color-background-brand-text-action-button-hover
);
}
&:active {
background-color: var(
--color-background-brand-text-action-button-active
);
}
}
/* Action buttons -- Info Intent */
.action-button-solid-info {
color: var(--color-text-info-solid-action-button);
background-color: var(--color-background-info-solid-action-button);
&:hover,
&:focus-visible {
background-color: var(
--color-background-info-solid-action-button-hover
);
}
&:active {
background-color: var(
--color-background-info-solid-action-button-active
);
}
}
.action-button-subtle-info {
color: var(--color-text-info-subtle-action-button);
background-color: var(--color-background-info-subtle-action-button);
box-shadow: 0 0 0.5px 0.5px var(--color-inner-shadow-action-button) inset;
&:hover,
&:focus-visible {
background-color: var(
--color-background-info-subtle-action-button-hover
);
}
&:active {
background-color: var(
--color-background-info-subtle-action-button-active
);
}
}
.action-button-text-info {
color: var(--color-text-info-text-action-button);
background-color: transparent;
&:hover,
&:focus-visible {
background-color: var(--color-background-info-text-action-button-hover);
}
&:active {
background-color: var(
--color-background-info-text-action-button-active
);
}
}
/* Action buttons -- Success Intent */
.action-button-solid-success {
color: var(--color-text-success-solid-action-button);
background-color: var(--color-background-success-solid-action-button);
&:hover,
&:focus-visible {
background-color: var(
--color-background-success-solid-action-button-hover
);
}
&:active {
background-color: var(
--color-background-success-solid-action-button-active
);
}
}
.action-button-subtle-success {
color: var(--color-text-success-subtle-action-button);
background-color: var(--color-background-success-subtle-action-button);
box-shadow: 0 0 0.5px 0.5px var(--color-inner-shadow-action-button) inset;
&:hover,
&:focus-visible {
background-color: var(
--color-background-success-subtle-action-button-hover
);
}
&:active {
background-color: var(
--color-background-success-subtle-action-button-active
);
}
}
.action-button-text-success {
color: var(--color-text-success-text-action-button);
background-color: transparent;
&:hover,
&:focus-visible {
background-color: var(
--color-background-success-text-action-button-hover
);
}
&:active {
background-color: var(
--color-background-success-text-action-button-active
);
}
}
/* Action buttons -- Warning Intent */
.action-button-solid-warning {
color: var(--color-text-warning-solid-action-button);
background-color: var(--color-background-warning-solid-action-button);
&:hover,
&:focus-visible {
background-color: var(
--color-background-warning-solid-action-button-hover
);
}
&:active {
background-color: var(
--color-background-warning-solid-action-button-active
);
}
}
.action-button-subtle-warning {
color: var(--color-text-warning-subtle-action-button);
background-color: var(--color-background-warning-subtle-action-button);
box-shadow: 0 0 0.5px 0.5px var(--color-inner-shadow-action-button) inset;
&:hover,
&:focus-visible {
background-color: var(
--color-background-warning-subtle-action-button-hover
);
}
&:active {
background-color: var(
--color-background-warning-subtle-action-button-active
);
}
}
.action-button-text-warning {
color: var(--color-text-warning-text-action-button);
background-color: transparent;
&:hover,
&:focus-visible {
background-color: var(
--color-background-warning-text-action-button-hover
);
}
&:active {
background-color: var(
--color-background-warning-text-action-button-active
);
}
}
/* Action buttons -- Danger Intent */
.action-button-solid-danger {
color: var(--color-text-danger-solid-action-button);
background-color: var(--color-background-danger-solid-action-button);
&:hover,
&:focus-visible {
background-color: var(
--color-background-danger-solid-action-button-hover
);
}
&:active {
background-color: var(
--color-background-danger-solid-action-button-active
);
}
}
.action-button-subtle-danger {
color: var(--color-text-danger-subtle-action-button);
background-color: var(--color-background-danger-subtle-action-button);
box-shadow: 0 0 0.5px 0.5px var(--color-inner-shadow-action-button) inset;
&:hover,
&:focus-visible {
background-color: var(
--color-background-danger-subtle-action-button-hover
);
}
&:active {
background-color: var(
--color-background-danger-subtle-action-button-active
);
}
}
.action-button-text-danger {
color: var(--color-text-danger-text-action-button);
background-color: transparent;
&:hover,
&:focus-visible {
background-color: var(
--color-background-danger-text-action-button-hover
);
}
&:active {
background-color: var(
--color-background-danger-text-action-button-active
);
}
}
.icon-button {
/* This class should always be used with an icon-button-* class
defining the color scheme of the button, defined below. */
position: relative;
display: flex;
justify-content: center;
align-items: center;
aspect-ratio: 1;
font-size: var(--base-font-size-px);
background-color: transparent; /* Override button default background color */
padding: var(--icon-button-padding);
cursor: pointer;
border: none;
border-radius: 4px;
clip-path: inset(-1px);
transition: 0.05s ease-in;
transition-property: background-color, clip-path;
&:hover,
&:focus-visible {
transition: 0.1s ease-out;
transition-property: background-color, clip-path;
}
&:active {
clip-path: inset(1px round 4px);
}
&:focus {
/* Override common button outline style set in zulip.css and dark_theme.css */
outline: none;
}
&:focus-visible {
/* Override common button outline style set in zulip.css and dark_theme.css */
outline: var(--outline-width-button-focus) solid
var(--color-outline-button-focus) !important;
outline-offset: var(--outline-offset-button-focus);
clip-path: inset(
calc(
-1 *
(
var(--outline-width-button-focus) +
var(--outline-offset-button-focus)
)
)
round 4px
);
}
&:disabled {
cursor: default;
pointer-events: none;
opacity: 0.5;
}
}
.icon-button-neutral {
color: var(--color-text-neutral-icon-button);
&:hover,
&:focus-visible {
color: var(--color-text-neutral-icon-button-hover);
}
&:active {
color: var(--color-text-neutral-icon-button-active);
}
&.icon-button-square {
&:hover,
&:focus-visible {
background-color: var(
--color-background-neutral-icon-button-square-hover
);
}
&:active {
color: var(--color-text-neutral-icon-button-square-active);
background-color: var(
--color-background-neutral-icon-button-square-active
);
}
}
}
.icon-button-brand {
color: var(--color-text-brand-icon-button);
&:hover,
&:focus-visible {
color: var(--color-text-brand-icon-button-hover);
}
&:active {
color: var(--color-text-brand-icon-button-active);
}
&.icon-button-square {
&:hover,
&:focus-visible {
background-color: var(
--color-background-brand-icon-button-square-hover
);
}
&:active {
color: var(--color-text-brand-icon-button-square-active);
background-color: var(
--color-background-brand-icon-button-square-active
);
}
}
}
.icon-button-info {
color: var(--color-text-info-icon-button);
&:hover,
&:focus-visible {
color: var(--color-text-info-icon-button-hover);
}
&:active {
color: var(--color-text-info-icon-button-active);
}
&.icon-button-square {
&:hover,
&:focus-visible {
background-color: var(
--color-background-info-icon-button-square-hover
);
}
&:active {
color: var(--color-text-info-icon-button-square-active);
background-color: var(
--color-background-info-icon-button-square-active
);
}
}
}
.icon-button-success {
color: var(--color-text-success-icon-button);
&:hover,
&:focus-visible {
color: var(--color-text-success-icon-button-hover);
}
&:active {
color: var(--color-text-success-icon-button-active);
}
&.icon-button-square {
&:hover,
&:focus-visible {
background-color: var(
--color-background-success-icon-button-square-hover
);
}
&:active {
color: var(--color-text-success-icon-button-square-active);
background-color: var(
--color-background-success-icon-button-square-active
);
}
}
}
.icon-button-warning {
color: var(--color-text-warning-icon-button);
&:hover,
&:focus-visible {
color: var(--color-text-warning-icon-button-hover);
}
&:active {
color: var(--color-text-warning-icon-button-active);
}
&.icon-button-square {
&:hover,
&:focus-visible {
background-color: var(
--color-background-warning-icon-button-square-hover
);
}
&:active {
color: var(--color-text-warning-icon-button-square-active);
background-color: var(
--color-background-warning-icon-button-square-active
);
}
}
}
.icon-button-danger {
color: var(--color-text-danger-icon-button);
&:hover,
&:focus-visible {
color: var(--color-text-danger-icon-button-hover);
}
&:active {
color: var(--color-text-danger-icon-button-active);
}
&.icon-button-square {
&:hover,
&:focus-visible {
background-color: var(
--color-background-danger-icon-button-square-hover
);
}
&:active {
color: var(--color-text-danger-icon-button-square-active);
background-color: var(
--color-background-danger-icon-button-square-active
);
}
}
}
.button-loading-indicator {
position: absolute;
display: flex;
justify-content: center;
align-items: center;
.loading_indicator_spinner {
/* Override standard values defined
in web/styles/app_components.css */
height: 100%;
width: unset;
aspect-ratio: 1;
& path {
/* Set the loading indicator color
to the font color of the button. */
fill: currentcolor;
}
}
}
.button-hide-loading-indicator-on-hover:hover {
.button-loading-indicator {
visibility: hidden;
}
.action-button-label,
.zulip-icon {
visibility: visible !important;
}
}

View File

@@ -0,0 +1,61 @@
.color-swatch-list {
display: grid;
grid-template-columns: repeat(6, var(--size-color-swatch));
gap: var(--grid-gap-color-swatch);
place-items: center;
padding: var(--padding-color-swatch-list);
}
.color-swatch-input {
display: none;
}
.color-swatch-input:checked + .color-swatch-label {
outline: 2px solid var(--color-outline-focus);
outline-offset: 1px;
}
.color-swatch-label {
width: var(--size-color-swatch);
height: var(--size-color-swatch);
border-radius: 50%;
cursor: pointer;
&:focus-visible {
outline: 2px solid var(--color-outline-focus);
outline-offset: 1px;
}
}
.custom-color-picker {
position: relative;
}
.color-picker-input {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100% !important;
visibility: hidden;
}
.custom-color-swatch-icon {
border-radius: 50%;
background: var(--gradient-custom-swatch);
}
.color-picker-popover .message_header .message-header-contents {
grid-template-columns: minmax(0, 1fr) 0 0 auto 0 0;
border: none;
}
.color_picker_confirm_button {
grid-area: controls;
height: 100%;
&:focus-visible {
outline: 1px solid var(--color-outline-focus);
outline-offset: -2px;
}
}

133
web/styles/components.css Normal file
View File

@@ -0,0 +1,133 @@
/* Reusable, object-oriented CSS base components for all Zulip pages
(both web app and portico). */
label.checkbox {
padding: 0;
display: inline-block;
position: relative;
vertical-align: top;
min-height: 20px;
& input[type="checkbox"] {
position: absolute;
clip-path: rect(0 0 0 0);
~ .rendered-checkbox {
display: inline-block;
vertical-align: middle;
position: relative;
top: -2px;
padding: 2px;
margin: 0 5px 0 0;
height: 10px;
width: 10px;
font-weight: 300;
line-height: 0.8;
font-size: 1.3rem;
text-align: center;
/* Color variables are not imported to the portico, so we duplicate
the light-mode color value here as a fallback. */
border: 1px solid
var(--color-border-rendered-checkbox, hsl(0deg 0% 0% / 60%));
border-radius: 4px;
cursor: pointer;
}
&:checked ~ .rendered-checkbox {
/* As with the border color above, we duplicate
the light-mode SVG URL value here as a fallback. */
background-image: var(
--svg-url-rendered-checkbox,
url("../images/checkbox.svg")
);
background-size: 85%;
background-position: 50% 50%;
background-repeat: no-repeat;
}
&:disabled ~ .rendered-checkbox {
opacity: 0.5;
cursor: not-allowed;
}
}
&:has(.enter_sends) {
vertical-align: middle;
}
}
a.no-style {
color: inherit;
}
a.no-underline,
a.no-underline:hover,
a.no-underline:focus {
text-decoration: none;
}
.italic {
font-style: italic;
}
.simplebar-track {
.simplebar-scrollbar::before {
background-color: hsl(0deg 0% 0%);
box-shadow: 0 0 0 1px hsl(0deg 0% 100% / 33%);
}
&.simplebar-vertical {
transition: width 0.2s ease 1s; /* stylelint-disable-line plugin/no-low-performance-animation-properties */
&.simplebar-hover {
width: 15px;
transition: width 0.2s ease; /* stylelint-disable-line plugin/no-low-performance-animation-properties */
}
}
&.simplebar-horizontal {
transition: height 0.2s ease 1s; /* stylelint-disable-line plugin/no-low-performance-animation-properties */
&.simplebar-hover {
height: 15px;
transition: height 0.2s ease; /* stylelint-disable-line plugin/no-low-performance-animation-properties */
}
}
}
i.zulip-icon.zulip-icon-bot {
color: var(--color-icon-bot);
}
/* Hide the somewhat buggy browser show password feature in IE, Edge,
since it duplicates our own "show password" widget. */
input::-ms-reveal {
display: none;
}
.password-div {
position: relative;
.password_visibility_toggle {
position: absolute;
right: 10px;
top: 42px;
opacity: 0.6;
&:hover {
opacity: 1;
}
}
}
select.bootstrap-focus-style {
&:focus {
outline: 1px dotted hsl(0deg 0% 20%);
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
transition: none;
}
}

2172
web/styles/compose.css Normal file

File diff suppressed because it is too large Load Diff

303
web/styles/dark_theme.css Normal file
View File

@@ -0,0 +1,303 @@
@import url("flatpickr/dist/themes/dark.css");
%dark-theme {
color-scheme: dark;
kbd {
text-shadow: none;
}
/************************* MODAL DARK THEME *******************/
#message-formatting,
#keyboard-shortcuts {
& kbd {
border: 1px solid var(--color-hotkey-hint);
border-radius: 3px;
color: var(--color-hotkey-hint);
opacity: 0.8;
}
}
.enter_sends_choices {
color: hsl(236deg 33% 90%);
.enter_sends_minor {
color: hsl(0deg 0% 80%);
}
}
.dropdown-list-delete {
/* hsl(7deg 100% 74%) corresponds to var(--red-250) */
color: color-mix(
in oklch,
hsl(7deg 100% 74%) 70%,
transparent
) !important;
&:hover {
color: hsl(7deg 100% 74%) !important;
}
}
/* this one is because in the app we have blue and in dark-theme it should be white. */
.popover a {
color: inherit;
}
/* these are converting grey things to "new grey".
:disabled rules are exploded for CSS selector performance reasons. */
button:disabled:not(.action-button, .icon-button, .info-density-button),
option:disabled,
select:disabled,
input:disabled:not(.modal_text_input),
input:not([type="radio"], .modal_text_input):read-only,
#organization-permissions .dropdown-widget-button:disabled,
#organization-settings .dropdown-widget-button:disabled {
color: inherit;
opacity: 0.5;
}
button.info-density-button:disabled {
color: inherit;
opacity: 0.4;
}
.popover-filter-input-wrapper .popover-filter-input:focus {
background-color: hsl(225deg 6% 7%);
border: 1px solid hsl(0deg 0% 100% / 50%);
box-shadow: 0 0 5px hsl(0deg 0% 100% / 40%);
}
& select option {
background-color: var(--color-background);
color: hsl(236deg 33% 90%);
}
.pill-container.not-editable-by-user,
.group_setting_disabled .pill-container {
opacity: 0.5;
}
#searchbox {
/* Light theme shows hover mostly through box-shadow,
and dark theme shows it mostly through changing color
of the search button. */
.navbar-search:not(.expanded)
#searchbox-input-container:hover
.search_icon {
opacity: 0.75;
}
}
textarea.schedule-reminder-note:focus {
border-color: hsl(0deg 0% 0% / 90%);
}
.popover hr,
hr {
opacity: 0.2;
}
#recent-view-content-table {
border-bottom-width: 1px;
}
.drafts-container .header-body .delete-drafts-group > *:focus {
background-color: hsl(228deg 11% 17%);
}
.table-striped tbody tr:nth-child(even) td {
border-color: hsl(0deg 0% 0% / 20%);
background-color: color-mix(
in srgb,
hsl(0deg 0% 0%) 20%,
var(--color-background-modal)
);
}
.overlay-message-row
.message_header_private_message
.message_label_clickable {
padding: 4px 6px 3px;
color: inherit;
}
& time {
background: hsl(0deg 0% 0% / 20%);
box-shadow: 0 0 0 1px hsl(0deg 0% 0% / 40%);
}
.tip {
color: inherit;
}
.alert.home-error-bar {
color: hsl(236deg 33% 90%);
background-color: hsl(35deg 84% 62% / 25%);
border: 1px solid hsl(11deg 46% 54%);
}
.alert.alert-success {
color: inherit;
background-color: hsl(161deg 60% 46% / 20%);
border-color: hsl(165deg 68% 37%);
}
.alert.alert-info {
color: hsl(205deg 58% 80%);
background-color: hsl(204deg 100% 12%);
border-color: hsl(205deg 58% 69% / 40%);
}
.alert.alert-error,
.alert.alert-danger {
background-color: hsl(318deg 12% 21%);
color: inherit;
border: 1px solid hsl(0deg 75% 65%);
}
.alert-box .alert,
.alert-box .stacktrace,
.alert.alert-error {
background-color: hsl(318deg 12% 21%);
color: inherit;
border: 1px solid hsl(0deg 75% 65%);
}
.alert-box {
.alert.alert-error::before {
color: hsl(0deg 75% 65%);
}
}
.stacktrace {
color: hsl(314deg 22% 85%);
background-color: hsl(318deg 12% 21%);
border: 1px solid hsl(0deg 75% 65%);
.expand {
color: hsl(318deg 14% 36%);
}
.subtle {
color: hsl(314deg 19% 63%);
}
.stacktrace-more-info {
background-color: hsl(312deg 7% 14%);
}
.code-context {
color: hsl(314deg 27% 82%);
background-color: hsl(312deg 7% 14%);
box-shadow:
inset 0 11px 10px -10px hsl(0deg 0% 6%),
inset 0 -11px 10px -10px hsl(0deg 0% 6%);
.line-number {
color: hsl(318deg 14% 44%);
}
.focus-line {
background-color: hsl(307deg 9% 19%);
}
}
}
.top-messages-logo {
opacity: 0.7;
}
.history-limited-box {
background-color: hsl(0deg 0% 0% / 20%);
}
.searching-for-more-topics img {
filter: invert(100%);
}
.simplebar-track .simplebar-scrollbar::before {
background-color: hsl(0deg 0% 100%);
box-shadow: 0 0 0 1px hsl(0deg 0% 0% / 33%);
}
.collapse-settings-button:hover {
color: hsl(200deg 79% 66%);
}
#loading_older_messages_indicator,
#recent_view_loading_messages_indicator {
& path {
fill: hsl(0deg 0% 100%);
}
}
& a:not(:active):focus,
button:not(.action-button, .icon-button):focus-visible,
i.fa:focus,
i.zulip-icon:focus,
[role="button"]:focus {
outline-color: hsl(0deg 0% 67%);
}
.color_animated_button {
.zulip-icon {
color: hsl(0deg 0% 100%);
}
}
.panel_user_list > .pill-container,
.creator_details > .display_only_pill {
&:hover {
color: inherit;
}
> .pill {
&:focus,
&:hover {
color: inherit;
}
}
}
.help_link_widget:hover {
color: inherit;
}
#uppy-editor .uppy-ImageCropper {
/* Uppy cropper canvas has some CSS properties set for the background that
do not look good for dark theme. When using Uppy Dashboard, this can be
solved by passing `theme` field with value as true in config options.
However we are not using that plugin so we need to provide our own dark
theme CSS for this detail.
Link for source from where the CSS properties are taken -
https://github.com/transloadit/uppy/blob/b7605df9402e4db9b02828bf24558e21eae76637/packages/%40uppy/image-editor/src/style.scss#L123
and https://github.com/transloadit/uppy/blob/b7605df9402e4db9b02828bf24558e21eae76637/packages/%40uppy/image-editor/src/style.scss#L133 */
.cropper-modal {
opacity: 0.7;
background-color: hsl(0deg 0% 0%);
}
.cropper-view-box {
background: repeating-conic-gradient(
hsl(0deg 1.18% 16.67%) 0% 25%,
hsl(0deg 0% 0%) 0% 50%
)
50%/16px 16px;
}
}
}
@media screen {
:root.dark-theme {
@extend %dark-theme;
}
}
@media screen and (prefers-color-scheme: dark) {
:root.color-scheme-automatic {
@extend %dark-theme;
}
}

78
web/styles/drafts.css Normal file
View File

@@ -0,0 +1,78 @@
.drafts-container {
.banner-container {
margin: 0 25px 5px;
}
.header-body {
display: flex;
align-items: center;
flex-direction: row;
justify-content: space-between;
gap: 5px;
.drafts-header-note {
text-align: left;
margin-left: 25px;
@media (width < $lg_min) {
text-align: center;
margin-left: 0;
}
}
.delete-drafts-group {
display: flex;
justify-content: flex-end;
gap: 10px;
.delete-selected-drafts-button {
&:focus {
background-color: hsl(0deg 0% 93%);
}
}
.select-drafts-button {
display: flex;
align-items: center;
gap: 5px;
margin-right: 25px;
padding-left: 15px;
padding-right: 15px;
&:focus {
background-color: hsl(0deg 0% 93%);
}
}
.select-state-indicator {
width: 15px;
}
@media (width < $lg_min) {
margin-top: 5px;
width: 100%;
}
}
@media (width < $lg_min) {
display: block;
}
}
.drafts-list {
& h2 {
font-size: 1.1em;
line-height: normal;
margin: 10px 0 5px;
white-space: pre-wrap;
}
}
.draft-selection-checkbox {
margin-top: 0.25em;
/* Required to make sure that the checkbox icon stays inside
the grid. Any value greater than 13px (original width of
the checkbox icon) will work. */
width: 15px;
}
}

View File

@@ -0,0 +1,228 @@
/* common CSS for all image upload widget's */
.image_upload_widget {
position: relative;
border-radius: 5px;
box-shadow: 0 0 10px hsl(0deg 0% 0% / 10%);
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
.image-block {
max-height: 100%;
}
.image-hover-background {
content: "";
background-color: hsl(0deg 0% 0% / 60%);
height: 100%;
width: 100%;
z-index: 99;
position: absolute;
display: none;
cursor: pointer;
top: 0;
}
.image_upload_button {
position: absolute;
width: 100%;
height: 100%;
display: flex;
align-items: center;
text-align: center;
justify-content: center;
box-shadow: 0 0 10px hsl(0deg 0% 0% / 10%);
z-index: 99;
}
.image-delete-button {
background: none;
border: none;
cursor: pointer;
color: hsl(0deg 0% 75%);
opacity: 0;
padding: 0;
position: absolute;
font-size: 2rem;
top: 10px;
right: 10px;
z-index: 99;
line-height: 20px;
font-family: inherit;
&:focus:not(:focus-visible) {
outline: none;
}
}
.image-disabled-text {
color: hsl(0deg 0% 100%);
cursor: not-allowed;
position: absolute;
text-align: center;
visibility: hidden;
z-index: 99;
top: 0;
}
.image-delete-text,
.image-upload-text,
.image-disabled-text {
box-sizing: border-box;
width: 100%;
padding: 0 10px;
}
.image-delete-button:focus,
.image-delete-button:hover {
color: hsl(0deg 0% 100%);
}
.image-delete-button:hover ~ .image-upload-text {
visibility: hidden;
}
.image-delete-button:hover ~ .image-delete-text {
visibility: visible;
}
.image-delete-text {
color: hsl(0deg 0% 100%);
font-size: 0.85rem;
position: absolute;
visibility: hidden;
z-index: 99;
}
.image-upload-text {
cursor: pointer;
font-size: 0.85rem;
color: hsl(0deg 0% 85%);
position: absolute;
z-index: 99;
visibility: hidden;
&:focus:not(:focus-visible) {
outline: none;
}
}
.image-upload-text:hover {
color: hsl(0deg 0% 100%);
}
.upload-spinner-background {
display: flex;
justify-content: center;
align-items: center;
background-color: hsl(0deg 0% 10%);
font-size: 0.8rem;
width: 100%;
opacity: 0.8;
height: 100%;
position: absolute;
visibility: hidden;
z-index: 99;
cursor: pointer;
border-radius: 5px;
}
.hide {
display: none;
}
&:hover {
.image-upload-text {
visibility: visible;
}
.image-delete-button {
opacity: 1;
}
.image-disabled-text {
visibility: visible;
}
.image-hover-background {
display: block;
}
}
}
.user-avatar-section,
.realm-icon-section {
margin: 20px 0;
}
.realm-logo-section {
margin: 0 0 20px;
}
/* CSS related to settings page user avatar upload widget only */
#user-avatar-upload-widget {
.image-block {
width: 200px;
height: 200px;
}
}
#user-avatar-source {
font-size: 1em;
z-index: 99;
margin-top: 10px;
}
/* CSS related to settings page realm icon upload widget only */
#realm-icon-upload-widget {
width: 100px;
height: 100px;
box-shadow: 0 0 10px hsl(0deg 0% 0% / 10%);
.image-delete-button {
top: 5px;
right: 5px;
}
}
/* CSS related to settings page realm day/night logo upload widget only */
#realm-day-logo-upload-widget,
#realm-night-logo-upload-widget {
width: 220px;
height: 55px;
text-align: center;
.image-delete-button {
top: 5px;
right: 5px;
}
}
#realm-day-logo-upload-widget {
/* Match to light-theme --color-background-navbar. */
background-color: hsl(0deg 0% 97%);
padding: 5px;
}
#realm-night-logo-upload-widget {
/* Match to dark-theme --color-background-navbar. */
background-color: hsl(0deg 0% 13%);
padding: 5px;
}
.realm-logo-group {
display: flex;
flex-wrap: wrap;
gap: 20px;
.image_upload_button {
top: 0;
left: 0;
}
}
/* CSS related to upload widget's preview image */
.upload_widget_image_preview {
object-fit: cover;
}

957
web/styles/inbox.css Normal file
View File

@@ -0,0 +1,957 @@
.inbox-container {
display: flex;
flex-direction: column;
background: var(--color-background-inbox);
padding: 0;
min-height: 100vh;
#inbox-pane {
max-width: 100%;
display: flex;
flex-direction: column;
margin: var(--navbar-fixed-height) 25px 0;
a {
color: var(--color-text-message-header);
text-decoration: none;
}
.unread_count {
opacity: 1;
outline: 0 solid var(--color-background-unread-counter);
}
.search_group {
position: sticky;
top: var(--navbar-fixed-height);
background: var(--color-background-inbox);
display: flex;
/* This padding-top value aligns the inbox filters
with the filters in the left and right sidebars. */
padding: 10px 0;
z-index: 1;
}
.button-inbox-selected {
background-color: var(--color-background-button-inbox-selected);
}
#inbox-filters {
.inbox-search-wrapper {
flex-grow: 1;
max-width: var(--width-inbox-search);
}
}
.inbox-empty-text {
display: none;
}
#inbox-loading-indicator {
margin-top: 20px;
margin-bottom: var(--max-unmaximized-compose-height);
.searching-for-more-topics {
margin-left: 0;
}
}
#inbox-list {
overflow: hidden;
/* search box left border (1px) + search box right border (1px)
+ dropdown left border (1px) + dropdown right border (1px) = 4px at 16px em */
max-width: calc(
var(--width-inbox-search) +
var(--width-inbox-filters-dropdown) + 0.25em
);
.inbox-channel-topic-list {
margin: 0;
}
.inbox-focus-border {
display: flex;
min-height: 1.875em; /* 30px at 16px em */
/* Holds the horizontal layout that the removed
`border: 2px solid transparent` was reserving;
`.inbox-row`'s outline-based focus doesn't reserve
space. Vertical padding omitted so the 30px icon
wrappers fit in the 30px row height. */
padding-inline: 0.125em; /* 2px at 16px/1em */
box-sizing: border-box;
justify-content: space-between;
}
.inbox-header {
user-select: none;
display: block;
height: 1.875em; /* 30px at 16px em */
.inbox-focus-border {
height: 1.875em; /* 30px at 16px em */
}
.inbox-left-part {
grid-template:
auto / minmax(auto, min-content)
auto min-content min-content;
grid-template-areas: "header_name collapse_button unread_mention_info unread_count";
}
.inbox-header-name {
grid-area: header_name;
display: flex;
align-items: center;
overflow: hidden;
outline: 0;
cursor: pointer;
.inbox-header-name-text {
margin: 0;
padding: 1px 0;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
font-weight: 600;
}
}
&:focus {
outline: 0;
}
&:hover {
.collapsible-button {
visibility: visible;
}
}
}
.fa-lock {
margin-right: 3px;
}
.stream-privacy.filter-icon {
/* 0 5px at 16px/1em */
padding: 0 0.3125em;
margin: 0;
}
.zulip-icon-user {
position: relative;
top: -1px;
margin-right: 4px;
}
.collapsible-button {
grid-area: collapse_button;
&:hover {
cursor: pointer;
}
.zulip-icon-chevron-down {
/* We are using very specific vertical padding here
* to avoid the inbox row grid overflow. */
padding: 0.125em 0.25em; /* 2px 4px at 16px em */
vertical-align: top;
transform: rotate(180deg);
}
}
.user-circle {
/* 8.5328px at 16px/1em */
font-size: 0.5333em;
/* TODO: Refactor inbox rows to use grid, to avoid the
min-width here that holds the other rows to similar
columns. */
/* 16px at 8.5328px/1em */
min-width: 1.8751em;
top: 0;
text-align: center;
}
.zulip-icon-bot,
.conversation-partners-icon {
opacity: 0.7;
/* Required to align DM fullnames in user circle icon */
/* 2px at 16px / 1em */
margin-left: 0.125em;
}
.user_block .zulip-icon {
/* 0 5px at 16px/1em */
padding: 0 0.3125em;
&.user-circle {
/* 5px at 16px / ~ 0.5em */
padding: 0 0.586em;
}
}
.inbox-row {
user-select: none;
display: block;
background-color: var(--color-background-inbox-row);
&:hover {
background: var(--color-background-inbox-row-hover);
}
.inbox-left-part {
grid-template:
auto / min-content minmax(0, 1fr)
min-content min-content;
grid-template-areas: "match_topic_and_dm_start recipient_info unread_mention_info unread_count";
}
.inbox-topic-container .user-circle {
grid-area: match_topic_and_dm_start;
}
.recipients_info,
.inbox-topic-name {
grid-area: recipient_info;
}
}
.channel-visibility-policy-indicator,
.visibility-policy-indicator,
.inbox-action-button {
display: flex;
align-items: center;
justify-content: center;
/* Row-tall, row-square click area so there's
room for the focus ring around the inner icon
without clipping against the row's right edge.
Centering the icon (vs. anchoring to an edge)
keeps the focus ring symmetric around it. */
width: 1.875em; /* 30px — matches row height */
height: 1.875em; /* 30px — matches row height */
/* Whole 30×30 hit area is clickable; the inner
icon itself only spans ~16×16. Without this,
mouse hovers at the top/bottom fall back to the
default arrow cursor. */
cursor: pointer;
& > i,
& > .zulip-icon,
& > .recipient_bar_icon {
/* Zero out per-icon padding; the outer button
is the click area and the focus ring draws
outside the icon via `outline-offset`.
Overrides `.recipient_bar_icon`'s 6px side
padding from message_header.css. */
padding: 0;
border-radius: 0.3125em; /* 5px at 16px/1em */
}
}
.channel-visibility-policy-indicator,
.visibility-policy-indicator {
margin-left: 3px;
}
/* Normalize the inner icon's opacity to 1 so the outer
wrapper is the single source of truth for opacity.
Overrides `.recipient_bar_icon`'s 0.2 idle / 0.4
hover from message_header.css for every marker
wrapper — follow, mute, unmute, and the inherit
(default) placeholder that becomes visible on row
hover. */
.channel-visibility-policy-indicator > .recipient_bar_icon,
.visibility-policy-indicator > .recipient_bar_icon {
opacity: 1;
/* Beat `.recipient_bar_icon:hover`'s !important. */
&:hover {
opacity: 1 !important;
}
}
/* Explicit topic/channel state markers are secondary
information: visible at a glance, quieter than primary
content. 0.45 is the lowest opacity that meets WCAG
1.4.11's 3:1 non-text contrast against both the light
(~3.4:1) and dark (~3.9:1) inbox row backgrounds. */
.channel-visibility-policy-indicator,
.visibility-policy-indicator:not(
.inbox-row-visibility-policy-inherit
) {
opacity: 0.45;
}
/* Row-level interaction tiers for the icon-style wrapper
types: action menu, channel-level marker, topic-level
marker. The inherit subclass is reached via the
topic-level marker since `.inbox-row-visibility-policy-
inherit` is also a `.visibility-policy-indicator`. */
.inbox-row,
.inbox-header {
/* Mouse hover reveals/lifts wrappers to 0.45,
matching the explicit-marker idle tier. */
&:hover {
:is(
.inbox-action-button,
.channel-visibility-policy-indicator,
.visibility-policy-indicator
) {
opacity: 0.45;
}
}
/* Keyboard selection alone (row focused but not
mouse-hovered) reveals only the usually-hidden
action buttons at a much quieter 0.2. Explicit
state markers stay at their 0.45 idle — only the
tertiary reveal needs this extra-quiet step. The
`:not(:focus-visible)` exclusion ensures that when
the button itself has keyboard focus (via arrow-
right into the column), its own `:focus-visible`
rule sets the direct-interaction tier instead. */
&:focus-within:not(:focus, :hover) {
:is(
.inbox-action-button,
.inbox-row-visibility-policy-inherit
):not(:focus-visible) {
opacity: 0.2;
}
}
&:focus-visible:not(:hover) {
:is(
.inbox-action-button,
.inbox-row-visibility-policy-inherit
) {
opacity: 0.2;
}
}
/* Direct hover on a wrapper lifts to 0.7. Compound
selector beats the row-wide reveal on specificity. */
&:hover
:is(
.inbox-action-button,
.channel-visibility-policy-indicator,
.visibility-policy-indicator
):hover {
opacity: 0.7;
}
/* Wrapper keyboard focus = full opacity so the focus
ring renders at full blue saturation (WCAG 1.4.11
3:1 contrast). The inner icon dims to 0.7
separately via the rule under `&:focus-visible > i`
above. */
&:focus-within
:is(
.inbox-action-button,
.channel-visibility-policy-indicator,
.visibility-policy-indicator
):focus-visible {
opacity: 1;
}
}
.unread-count-focus-outline {
/* Because the inbox view font-size will
never be smaller than the em-equivalent
of 15px, we restate the base font-size
here so that unreads match others in
the UI at legacy size (14px). */
font-size: var(--base-font-size-px);
grid-area: unread_count;
display: flex;
align-items: center;
justify-content: center;
border-radius: 3px;
padding: 0 5px;
/* Stretch to the row to keep unread
count from affecting overall row
size as test scales up. */
align-self: stretch;
}
/* Channels, topics, and direct messages mark their unread
messages as read when their count is clicked. Folder
headers (channel folders and the direct messages section)
only show a combined count for everything inside them, so
they don't get the clickable affordance. */
:is(.inbox-row, .inbox-header:not(.inbox-folder))
.unread-count-focus-outline {
/* Whole 30×stretched hit area is clickable; surface
the same pointer cursor users had on the smaller
badge before the click target was expanded. */
cursor: pointer;
/* Forward the badge's hover outline to the wrapper:
the click handler now lives on the wrapper, so
hovering anywhere in the wrapper (not just the
small inner badge) should preview the click
affordance. */
&:hover .unread_count {
outline-width: var(--focus-ring-width-in-views);
}
}
.unread_mention_info {
grid-area: unread_mention_info;
margin-right: 0;
}
.stream-privacy {
display: flex;
align-items: center;
margin-right: 4px;
margin-left: 17px;
.zulip-icon {
line-height: 0.875em; /* 14px at 16px em */
height: 1em;
width: 1em;
}
}
.inbox-topic-name {
/* 16px channel icon width + 10px padding */
padding-left: 1.625em; /* 26x at 16px em */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
& a {
padding: 1px 0;
white-space: pre;
}
}
.inbox-left-part-wrapper {
display: flex;
width: 80%;
}
.inbox-left-part {
width: 100%;
display: grid;
align-items: center;
&:hover {
cursor: pointer;
}
.inbox-group-or-bot-dm {
position: relative;
left: -3px;
/* We don't display status emoji in group DMs,
so prepare an ordinary inline layout... */
.user-status-microlayout {
display: inline;
white-space: collapse;
}
/* ...and hide the status emoji. */
.status-emoji {
display: none;
}
}
.recipients_info {
display: flex;
flex-wrap: wrap;
column-gap: 10px;
grid-area: recipient_info;
.user_block {
display: flex;
align-items: center;
}
}
}
}
#inbox-collapsed-note {
display: none;
overflow: hidden;
max-width: calc(
var(--width-inbox-search) +
var(--width-inbox-filters-dropdown) + 0.25em
);
.inbox-collapsed-note-and-button-wrapper {
margin-top: 3px;
padding-left: 8px;
.inbox-collapsed-note-span {
font-size: 1em;
}
#inbox-expand-all-button {
display: block;
margin-top: 5px;
}
}
}
.inbox-right-part-wrapper {
flex-grow: 1;
.inbox-right-part {
display: flex;
justify-content: space-between;
flex-grow: 1;
> .inbox-action-button:first-child {
margin-left: auto;
}
}
}
.inbox-row-visibility-policy-inherit {
opacity: 0;
}
/* Stickiness: while a marker or action button has its popover
open, keep it in the direct-interaction tier (0.7) so the
user can see which element the open menu belongs to.
Substring match on `*-popover-visible` covers every class
the popover JS toggles — `topic-popover-visible` (added by
`topic_popover.ts` and `stream_popover.ts` on
`.inbox-action-button`) and `visibility-policy-popover-visible`
(added by `user_topic_popover.ts` on the visibility-policy
wrappers) — without enumerating element/class combinations,
and stays correct if a future popover follows the same
naming convention. */
[class*="-popover-visible"] {
opacity: 0.7;
}
.inbox-action-button {
outline: none;
opacity: 0;
cursor: pointer;
&.hide {
display: none;
}
& i {
/* Match the color used by `.recipient_bar_icon` on
the state marker icons, so the 3-dots and the
follow/mute/unmute markers render in the same hue
as row-attention elevates both to visible. */
color: var(--color-message-header-icon-interactive);
}
}
}
.inbox-header-stream-archived {
color: var(--color-text-message-header-archived);
margin-left: 0.3125em; /* 5px at 16px em */
}
}
#inbox-view {
display: none;
position: relative;
.inbox-folder {
margin-bottom: 1px;
background-color: transparent;
.inbox-header-name-text,
.unread_mention_info {
color: var(--color-folder-header);
opacity: 0.5;
}
.inbox-header-name-text {
font-style: normal;
font-weight: var(--font-weight-sidebar-heading);
line-height: 112.5%;
letter-spacing: var(--letter-spacing-sidebar-heading);
text-transform: uppercase;
outline: none;
/* 16px at 16px / 1em */
font-size: 1em;
/* Align the folder title with the channel privacy icons; 5px at 16px/1em.
TODO: Reduce the depth of selectors in this file,
so that the use of !important becomes unnecessary
here. */
padding-left: 0.3125em !important;
}
&:hover {
.inbox-header-name-text,
.collapsible-button .zulip-icon,
.unread_mention_info,
.unread_count {
opacity: 1;
}
}
}
.hidden_by_filters {
display: none !important;
}
.channel-folders-inbox-menu-icon {
display: grid;
/* width excluding left & right margin */
width: calc(var(--left-sidebar-vdots-width) - 4px);
cursor: pointer;
place-items: center center;
border-radius: 3px;
margin: 2px;
color: var(--color-vdots-visible);
i.zulip-icon {
font-size: 1.0625em;
}
&:hover {
color: var(--color-vdots-hover);
background-color: var(--color-background-sidebar-action-hover);
}
}
}
.inbox-container #inbox-pane #inbox-empty-without-search {
margin-top: 70px;
display: flex;
flex-direction: column;
align-items: center;
}
.inbox-empty-illustration {
width: 54px;
height: 54px;
margin-bottom: 16px;
background-color: var(--color-background-inbox-no-unreads-illustration);
mask: url("../images/empty-view-illustrations/inbox-done.svg") no-repeat
center;
}
.inbox-empty-title {
/* Matches the font-size used for .empty-list-message */
font-size: 1.5em;
color: var(--color-background-inbox-no-unreads-illustration);
}
.inbox-empty-action {
font-size: 1.3em;
padding-top: 0.5em;
}
.inbox-container #inbox-pane .inbox-empty-action-link {
color: var(--color-text-url);
&:hover {
color: var(--color-text-url-hover);
}
}
#inbox-filter_widget {
margin-right: 0.3571em; /* 5px / 14px em */
max-width: var(--width-inbox-filters-dropdown);
border: 1px solid var(--color-border-inbox-search);
background-color: var(--color-background-inbox-search);
gap: 0.3571em; /* 5px / 14px em */
/* 1.5px at 16px/1em and legacy 6px at 14px/1em. */
padding: 0.0937em 0.4285em;
&:hover {
background-color: var(--color-background-inbox-search-hover);
border: 1px solid var(--color-border-inbox-search-hover);
}
&:focus {
outline: none;
}
}
#recent-view-filter_widget .dropdown_widget_value,
#inbox-filter_widget .dropdown_widget_value {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
width: 100%;
text-align: left;
}
#recent-view-filter_widget .zulip-icon-chevron-down,
#inbox-filter_widget .zulip-icon-chevron-down {
color: var(--color-icons-inbox);
opacity: 0.4;
}
.recent-view-filter-dropdown-list-container .dropdown-list-wrapper,
.inbox-filter-dropdown-list-container .dropdown-list-wrapper,
.new_channel_privacy-dropdown-list-container .dropdown-list-wrapper,
.channel_privacy-dropdown-list-container .dropdown-list-wrapper {
/* We want these dropdowns to open to fit their
contents, which differ based on the language
in use. */
width: 100%;
/* We also don't want to set a min-width, again
so that the content is in charge. This pushes
back against more generic styles. */
min-width: unset;
}
.recent-view-filter-dropdown-list-container
.dropdown-list
.dropdown-list-item-common-styles,
.inbox-filter-dropdown-list-container
.dropdown-list
.dropdown-list-item-common-styles,
.new_channel_privacy-dropdown-list-container
.dropdown-list
.dropdown-list-item-common-styles,
.channel_privacy-dropdown-list-container
.dropdown-list
.dropdown-list-item-common-styles {
/* Parallel to the `width: 100%` set on
.dropdown-list-wrapper above, we set
min-width here to max-content so that
the box opens to accommodate different
lengths of text--and to ensure that
hovered/selectable areas look correct. */
min-width: max-content;
padding: 5px 10px;
display: flex;
flex-direction: column;
/* For columnar flex items, we need
to make set alignment to the start
of the flex container. This pushes
back against non-columnar flexboxes
that require centered alignment. */
align-items: flex-start;
}
.recent-view-filter-dropdown-list-container .dropdown-list-item-name,
.inbox-filter-dropdown-list-container .dropdown-list-item-name,
.new_channel_privacy-dropdown-list-container .dropdown-list-item-name,
.channel_privacy-dropdown-list-container .dropdown-list-item-name {
white-space: nowrap;
font-weight: 500;
padding: 0;
margin: 0;
text-overflow: ellipsis;
overflow: hidden;
}
.recent-view-filter-dropdown-list-container .dropdown-list-item-description,
.inbox-filter-dropdown-list-container .dropdown-list-item-description,
.channel_privacy-dropdown-list-container .dropdown-list-item-description,
.new_channel_privacy-dropdown-list-container .dropdown-list-item-description {
white-space: nowrap;
font-weight: 400;
font-size: 0.9285em; /* 13px at 14px em */
opacity: 0.8;
padding: 0;
text-overflow: ellipsis;
overflow: hidden;
}
.inbox-container #inbox-pane .inbox-folder .unread_count {
transition: none;
display: none;
}
#inbox-pane #inbox-list .collapsible-button {
visibility: hidden;
}
#inbox-pane #inbox-list .inbox-collapsed-state .collapsible-button {
visibility: visible;
.zulip-icon-chevron-down {
transform: rotate(0deg);
}
}
.folder-row-chevron {
color: var(--color-folder-header);
}
.channel-row-chevron {
color: var(--color-inbox-row-chevron);
}
.inbox-collapsed-state {
.folder-row-chevron,
.channel-row-chevron {
opacity: 0.5;
}
}
.inbox-container #inbox-pane .inbox-folder .unread_mention_info {
display: none;
}
.inbox-container
#inbox-pane
.inbox-folder.inbox-collapsed-state
.unread_mention_info,
.inbox-container #inbox-pane .inbox-folder.inbox-collapsed-state .unread_count {
display: inline;
}
.inbox-folder-components {
border-radius: 5px;
border: var(--inbox-view-folder-component-border-width) solid
var(--color-border-inbox-folder-component);
overflow: hidden;
&:has(.inbox-row:not(.hidden_by_filters)),
&:has(.inbox-header:not(.hidden_by_filters)) {
margin-bottom: 0.5em;
}
}
.inbox-folder.inbox-collapsed-state,
.inbox-folder.hidden_by_filters {
+ .inbox-folder-components {
border: 0;
}
}
#inbox-pane #inbox-list .inbox-collapsed-state {
+ .inbox-folder-components,
+ .inbox-topic-container {
display: none;
}
}
#inbox-pane
#inbox-list
.inbox-streams-container
.inbox-header
.inbox-header-name {
/* 5px at 16px / 1em */
padding: 1px 0.3125em 1px 0;
}
#inbox-view.no-visible-focus-outlines
.inbox-container
#inbox-pane
[tabindex="0"] {
/* Remove default focus outline from elements. */
&:focus-visible {
outline: 0;
}
}
#inbox-view:not(.no-visible-focus-outlines) .inbox-container #inbox-pane {
/* Only show focus outlines when user uses keyboard. */
#inbox-filter_widget {
&:focus-visible {
outline: var(--focus-ring-outline);
}
}
.inbox-row,
.inbox-header {
&:focus-visible {
/* Flush outline on the row itself instead of the old
2px inner-border on `.inbox-focus-border`; matches
`.recent-view-body-row`'s focus style, but inbox rows
are tighter (30px) so the outline sits at the row
edges instead of insetting 4px. */
outline: var(--focus-ring-outline);
outline-offset: var(--focus-ring-outline-offset-in-views);
border-radius: 0.3125em; /* 5px at 16px/1em */
}
}
.inbox-header:focus-visible {
.collapsible-button > .zulip-icon {
opacity: 1;
}
.collapsible-button {
visibility: visible;
}
}
/* Focus ring for the icon-style row actions. Outline lives on
the wrapper (at full opacity) so the ring renders at full
blue saturation and meets WCAG 1.4.11's 3:1 contrast
requirement for focus indicators. Negative outline-offset
pulls the 1.5px stroke inside the 30×30 wrapper bounds so it
doesn't clip against the row's overflow:hidden edge, giving
a 26×26 effective ring. The inner icon dims separately. */
.channel-visibility-policy-indicator,
.visibility-policy-indicator,
.inbox-action-button {
&:focus-visible {
outline: var(--focus-ring-outline);
outline-offset: -0.125em; /* -2px at 16px/1em */
border-radius: 0.3125em; /* 5px at 16px/1em */
> i,
> .zulip-icon,
> .recipient_bar_icon {
opacity: 0.7;
}
}
}
/* Focus ring drawn as a pseudo-element on the inner badge,
with `5px` of breathing room outside the badge. A
wrapper-sized outline (the previous implementation) leaves a
wide gap between the ring and the badge fill that reads as a
second concentric outline; the deliberate 5px gap here reads
as padding around a single badge instead. */
.unread-count-focus-outline {
&:focus-visible {
outline: none;
.unread_count {
position: relative;
/* Suppress the inner `.normal-count` `inset`
`box-shadow` so it doesn't sit as a second,
narrower outline inside the focus ring. */
box-shadow: none;
&::after {
content: "";
position: absolute;
inset: -0.3125em; /* -5px at 16px/1em */
border: var(--focus-ring-outline);
border-radius: var(
--unread-count-focus-outline-radius-in-views
);
pointer-events: none;
}
}
}
}
.inbox-folder {
&:focus-visible {
.inbox-header-name-text,
.collapsible-button .zulip-icon,
.unread_mention_info,
.unread_count {
opacity: 1;
}
background: light-dark(
transparent,
var(--color-background-hover-popover-menu)
);
}
}
}
#inbox-view .inbox-container #inbox-pane #inbox-expand-all-button {
--color-outline-button-focus: var(--color-outline-focus);
--outline-offset-button-focus: var(--focus-ring-outline-offset-in-views);
}
#inbox-animation-extra-content-blocker {
background: var(--color-background-inbox);
}

View File

@@ -0,0 +1,128 @@
.informational-overlays {
.overlay-content {
width: var(--informational-overlay-max-width);
margin: 0 auto;
position: relative;
top: calc((30vh - 50px) / 2);
border-radius: 4px;
overflow: hidden;
background-color: var(--color-background-modal);
}
.overlay-tabs {
padding: 10px 0;
border-bottom: 1px solid hsl(0deg 0% 0% / 20%);
.tab-switcher {
margin-left: 15px;
}
.exit {
float: right;
font-size: 1.5rem;
color: hsl(0deg 0% 67%);
font-weight: 600;
margin: 0 15px;
cursor: pointer;
}
}
.overlay-modal {
padding-bottom: 10px;
.overlay-scroll-container {
height: 70vh;
text-align: center;
outline: none;
.help-table {
table-layout: fixed;
}
& th {
font-weight: 400;
}
}
}
& td.operator {
font-size: 0.9em;
}
.poll-question-header {
display: inline;
}
}
.hotkeys_table {
table-layout: fixed;
width: 100%;
vertical-align: middle;
display: table;
& td.definition {
/* keeps dividing line at same width for all tables in model. */
width: calc(50% - 11px);
text-align: right;
}
& td.definition .emoji-1f44d {
vertical-align: middle;
margin-bottom: 0.6em;
}
.hotkey {
font-weight: normal;
}
.arrow-key {
font-size: 1.36em;
line-height: 1;
padding: 0 0.2em 0.2em;
}
& th {
width: 245px;
text-align: center;
/* aligns table name with dividing line */
}
& td:not(.definition) {
text-align: left;
white-space: normal;
font-weight: bold;
}
}
#keyboard-shortcuts table {
margin-bottom: 10px !important;
}
@media only screen and (width < $md_min) {
.informational-overlays {
.overlay-content {
width: calc(100% - 20px);
/* Constrain to same width as in larger viewports. */
max-width: var(--informational-overlay-max-width);
}
.tab-switcher {
display: flex;
&.large .ind-tab {
width: 100%;
}
}
.hotkeys_table {
width: 100%;
display: table;
}
}
}
#message-formatting-first-header,
#search-operators-first-header {
width: 40%;
}

470
web/styles/input_pill.css Normal file
View File

@@ -0,0 +1,470 @@
.pill-container {
display: inline-flex;
gap: var(--vertical-spacing-input-pill) var(--horizontal-spacing-input-pill);
flex-wrap: wrap;
min-width: 0;
background-color: var(--color-background-pill-container);
padding: var(--outer-spacing-input-pill-container);
border: 1px solid var(--color-border-pill-container);
border-radius: 4px;
align-items: center;
cursor: text;
.pill {
display: inline-flex;
align-items: center;
max-width: 100%;
min-width: 0;
position: relative;
height: var(--height-input-pill);
/* Make sure the `height` property
is the authoritative source of an
input pill's height, without being
affected by borders, etc. */
box-sizing: border-box;
margin: 0;
color: inherit;
border-radius: 4px;
background-color: var(--color-background-input-pill);
cursor: pointer;
/* Not focus-visible, because we want to support mouse+backpace to
delete pills */
&:focus {
/* Box shadow instead of border, because user pills have avatars
that extend all the way to the edge of the pill. */
box-shadow: 0 0 0 1px var(--color-focus-outline-input-pill) inset;
outline: none;
/* For user pills with avatars, the avatar covers up the box
shadow, so we also have to make a border around the avatar. */
.pill-image-border {
border-right: none;
border-radius: 4px 0 0 4px;
border-color: var(--color-focus-outline-input-pill);
}
}
/* Ensure a hover state on pills that can be clicked
to reveal a user card. */
&.view_user_profile:hover {
background-color: var(--color-background-input-pill-hover);
}
.invited-user-name {
flex: 1;
min-width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-right: 3px;
}
.pill-image {
height: var(--height-input-pill);
width: var(--height-input-pill);
border-radius: 4px;
}
.pill-image-border {
height: var(--height-input-pill);
width: var(--height-input-pill);
position: absolute;
box-sizing: border-box;
border: 1px solid;
border-radius: 4px;
border-color: var(--color-border-input-pill-image);
}
.pill-label {
/* Treat as flex container to better position status
emoji and control ellipsis on the pill value. */
display: flex;
/* Allow label to collapse for ellipsis effect. */
min-width: 0;
align-items: center;
margin: 0 var(--input-pill-side-padding);
.zulip-icon-bot {
margin-left: 0.4em;
}
}
.pill-value {
flex: 0 1 auto;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
line-height: 1.5;
}
.pill-close-button,
.pill-expand-button {
font-size: 0.7142em; /* 10px at 14px em */
/* Let the close button's box stretch,
but no larger than the height of the
banner box when the action button
achieves its full height (margin,
padding, and height), which keeps
the X vertically centered with it. */
align-self: stretch;
max-height: 52px;
/* Display as flexbox to better control
the X icon's position. This creates
an anonymous flexbox item out of the
::before content where the icon sits. */
display: flex;
align-items: center;
color: var(--color-input-pill-close);
opacity: 0.7;
}
.exit,
.expand {
width: var(--length-input-pill-exit);
height: var(--length-input-pill-exit);
display: flex;
justify-content: center;
margin-right: var(--length-input-pill-exit-margin);
border-radius: 2px;
&:hover {
background: var(--color-background-input-pill-exit-hover);
.pill-close-button,
.pill-expand-button {
opacity: 1;
}
}
}
.group-members-count {
opacity: 0.65;
}
&.deactivated-pill {
background-color: var(--color-background-deactivated-user-pill);
opacity: 0.7;
&:focus {
border-color: var(--color-focus-outline-deactivated-user-pill);
}
.pill-close-button {
color: var(--color-close-deactivated-user-pill);
}
.exit:hover {
background: var(
--color-background-exit-hover-deactivated-user-pill
);
}
> img {
opacity: 0.5;
}
}
}
.slashed-circle-icon {
position: absolute;
background-color: var(--color-background-deactivated-user-pill);
padding: 0.2em;
font-size: 0.7em;
border-radius: 0.625em;
bottom: -0.0625em;
left: 1.1em;
opacity: 1;
}
&.not-editable {
cursor: not-allowed;
border: none;
background-color: transparent;
padding: 0;
.pill {
padding-right: 4px;
cursor: not-allowed;
&:focus {
color: inherit;
border: 1px solid hsl(0deg 0% 0% / 15%);
background-color: hsl(0deg 0% 0% / 7%);
}
.exit {
display: none;
}
}
}
.input {
display: inline-block;
/* This keeps the input sized to
the same height as pills. */
line-height: var(--height-input-pill);
padding: 0 4px;
min-width: 2px;
overflow-wrap: anywhere;
flex: 1 1 auto;
outline: none;
}
&.not-editable-by-user {
cursor: not-allowed;
background-color: var(--color-background-pill-container-input-disabled);
.pill {
cursor: not-allowed;
}
.exit {
display: none;
}
}
&.invalid {
border-color: var(--color-invalid-input-border);
box-shadow: var(--invalid-input-box-shadow);
}
}
.person_picker {
.pill:hover {
background-color: var(--color-background-input-pill-hover);
}
}
#compose-direct-recipient .pill-container {
border: 1px solid var(--color-compose-recipient-box-border-color);
.input:first-child:empty::before {
content: attr(data-no-recipients-text);
opacity: 0.5;
}
&:has(.input:focus) {
border-color: var(--color-compose-recipient-box-has-focus);
}
}
.low-attention-recipient-row #compose-direct-recipient .pill-container {
&:has(.pill):not(:focus-within) {
/* In the low-attention state, prevent this from being
part of the flex layout on .pill-container when there
are already user pills (when the "Add user" text is
never displayed, as specified on the
.input:first-child:empty::before selector above).
We avoid doing this when there is focus inside the
pill container, so that typing in the recipient row
after narrowing to a DM conversation with `Ctrl + .`
doesn't create a palimpsest of text over the existing
user pill. */
#private_message_recipient {
position: absolute;
top: 0;
left: 0;
}
}
}
#invitee_emails_container .pill-container,
#integration-url-filter-branches .pill-container {
width: 100%;
box-sizing: border-box;
background-color: var(
--color-background-pill-container-without-placeholder
);
}
#invitee_emails_container .pill-container.not-editable-by-user {
height: var(--height-input-pill);
background-color: var(--color-background-pill-container-input-disabled);
}
.add_subscribers_container .pill-container,
.add_streams_container .pill-container,
.add-user-group-container .pill-container,
.add_members_container .pill-container {
width: 100%;
.input:first-child:empty::before {
opacity: 0.5;
content: attr(data-placeholder);
}
}
.add_streams_container .input,
.add-user-group-container .input {
flex-grow: 1;
}
.display_only_pill,
.display_only_group_pill {
/* Ensure middle alignment in table contexts. */
vertical-align: middle;
/* Resetting these values to prevent unintended inheritance from .pill-container styles. */
padding: 0;
border: 0;
/* Particularly in light mode, pill colors are almost identical
to the settings modal background. We place a higher-contrast
outline on them accordingly. */
.pill,
.pill:focus {
outline: 1px solid var(--color-outline-low-attention-input-pill);
/* We offset the outline so that pills do not grow in size
when presented with an outline. */
outline-offset: -1px;
/* Display `(you)` with the same 5px padding to the right
as ordinary .pill-value text. */
.my_user_status {
padding-right: 5px;
}
/* Zero out the margin placed to the left of the bot icon,
and add in the 5px of padding to the right. Specificity
necessitates duplicating the .pill-label class here. */
.pill-label .zulip-icon-bot {
margin-left: 0;
padding-right: 5px;
}
.deactivated-user-icon {
padding-right: 5px;
}
}
.pill:focus {
/* We don't want to flash a focus outline
when clicking on display-only pills. */
box-shadow: unset;
/* We also suppress the border on the pill
image. */
.pill-image-border {
border: 0;
}
}
}
/* These pill are similar to .not-editable, but are meant to show
profile cards when clicked. */
.user-type-custom-field-pill-container > .display_only_pill,
.panel_user_list > .pill-container,
.creator_details > .display_only_pill {
gap: 2px;
flex-wrap: nowrap;
&.inline_with_text_pill > .pill-deactivated {
font-size: 0.9em;
padding-right: 2px;
}
&:hover {
color: inherit;
}
/* `settings.css` applies a :focus-within box-shadow to `.custom_user_field .pill-container`
and `.bot_owner_user_field .pill-container`.That effect conflicts with the unified pill
styling (no shadow), so we override it here. */
&:focus-within {
box-shadow: none;
}
> .pill {
border: none;
text-decoration: none;
&:focus {
color: inherit;
}
> .pill-label {
margin: 0;
> .pill-value {
padding: 5px;
max-width: 165px;
}
}
}
}
#user-profile-modal {
.user-type-custom-field-pill-container {
display: inline-flex;
gap: var(--vertical-spacing-input-pill)
var(--horizontal-spacing-input-pill);
max-width: 100%;
flex-wrap: wrap;
/* We override the `.pill-container` style defined in
`settings.css` to ensure it expands to full width, preventing
names in the pill from being cut off in the user profile modal. */
.pill-container {
max-width: 100%;
}
/* We're overriding the `.pill-value` style defined
in `settings.css` to ensure that names inside the pill
render fully and aren't cut off in the user profile modal. */
.pill-value {
max-width: 100%;
}
}
}
.group_entirely_not_subscribed .display_only_group_pill,
.creator_details > .display_only_pill {
/* In places where the pills are not used in tables,
but instead alongside text, we revert back to the
baseline vertical alignment. */
vertical-align: baseline;
}
.creator_details > .display_only_pill.inline_with_text_pill {
padding: 0;
> .pill {
margin: 0;
align-items: baseline;
> .pill-image {
/* Add line-height equal to height to mimic baseline alignment. */
line-height: 20px;
align-self: center;
}
> .pill-label {
> .pill-value {
padding: 0 5px;
max-width: 200px;
}
> .my_user_status {
margin-right: 2px;
}
}
}
}
.display_only_group_pill .zulip-icon-user-group {
/* Reserve the same square box for the user-group
icon as on pill images. */
height: var(--height-input-pill);
width: var(--height-input-pill);
/* Center the icon in the box using flex. */
display: flex;
align-items: center;
justify-content: center;
}

154
web/styles/inputs.css Normal file
View File

@@ -0,0 +1,154 @@
.input-active-styles {
color: var(--color-text-input);
background-color: var(--color-background-input-focus);
outline-color: var(--color-outline-input-focus);
}
.input-element-wrapper {
display: grid;
grid-template:
/* We present a variable to set a uniform row height when needed;
otherwise, we fall back to the sensible `auto` value. */
[input-element-start] "icon-starting-offset input-icon icon-content-gap content button-content-gap input-button button-ending-offset" var(
--input-element-row-height,
auto
)
[input-element-end] / [input-element-start] var(
--input-icon-starting-offset
)
var(--input-icon-width) var(--input-icon-content-gap) minmax(0, 1fr)
var(--input-button-content-gap) var(--input-button-width) var(
--input-button-ending-offset
)
[input-element-end];
align-items: center;
.input-element {
grid-area: input-element;
box-sizing: border-box;
padding: 0.25em 0.5em; /* 4px at 16px/1em and 8px at 16px/1em */
font-size: var(--base-font-size-px);
font-family: inherit;
line-height: 1.25;
text-overflow: ellipsis;
color: var(--color-text-input);
background: var(--color-background-input);
outline: 1px solid var(--color-outline-input);
outline-offset: -1px;
border: none;
border-radius: 4px;
transition: 0.1s linear;
transition-property: outline-color, box-shadow;
&:hover {
outline-color: var(--color-outline-input-hover);
}
&:focus {
@extend .input-active-styles;
box-shadow: 0 0 5px var(--color-box-shadow-input-focus);
}
}
&.has-input-icon .input-element {
padding-left: calc(
var(--input-icon-starting-offset) + var(--input-icon-width) +
var(--input-icon-content-gap)
);
}
&.has-input-button .input-element {
padding-right: calc(
var(--input-button-content-gap) + var(--input-button-width) +
var(--input-button-ending-offset)
);
}
/* Special styles for input with pills */
&.has-input-pills .pill-container {
.input {
flex-grow: 1;
/* Override default values in web/styles/input_pill.css */
padding: 0;
line-height: inherit;
&:empty::before {
color: var(--color-text-placeholder);
content: attr(data-placeholder);
}
}
.pill {
height: 1.25em; /* 20px at 16px/1em */
}
&:has(.input:hover) {
outline-color: var(--color-outline-input-hover);
}
&:has(.input:focus) {
@extend .input-active-styles;
box-shadow: 0 0 5px var(--color-box-shadow-input-focus);
}
}
}
.input-icon {
grid-area: input-icon;
/* Matching the color of input icon on the left to
that of a neutral icon button gives us a consistent
look when paired with the input button on the right. */
color: var(--color-text-neutral-icon-button);
/* We need to set the z-index, since the input icon
comes before the input element in the DOM, but we
want to display it over the input element in the UI. */
z-index: 1;
pointer-events: none;
}
.input-button {
grid-area: input-button;
padding: 0.25em; /* 4px at 16px/1em */
}
.filter-input {
/* We use the `input` tag in the selector to avoid conflicts
with the pill containing counterpart, which uses a `contenteditable`
div instead of an input element, and thus doesn't support the
placeholder pseudo-classes. */
input.input-element {
&:placeholder-shown {
/* In case of filter inputs, when the input field
is empty, we hide the input button and adjust
the right padding to compensate for the same. */
padding-right: 0.5em;
~ .input-close-filter-button {
visibility: hidden;
}
}
&:not(:placeholder-shown) {
@extend .input-active-styles;
}
}
/* Specific styles for filter input with pills */
&.has-input-pills {
&:not(:has(.pill)):has(.input:empty) {
.input-element {
padding-right: 0.5em;
}
.input-close-filter-button {
visibility: hidden;
}
}
&:has(.pill):not(:has(.input:empty)) .input-element {
@extend .input-active-styles;
}
}
}

2744
web/styles/left_sidebar.css Normal file

File diff suppressed because it is too large Load Diff

243
web/styles/lightbox.css Normal file
View File

@@ -0,0 +1,243 @@
#lightbox_overlay {
background-color: hsl(227deg 40% 16%);
display: flex;
flex-direction: column;
height: 100dvh;
.image-preview {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
position: relative;
width: 100%;
margin: 0;
overflow: hidden;
background-size: contain;
background-repeat: no-repeat;
background-position: center center;
& img {
cursor: move;
max-height: 100%;
max-width: 100%;
object-fit: contain;
}
.zoom-element {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
}
.video-player {
flex: 1;
display: flex;
width: 100%;
align-items: center;
justify-content: center;
margin: 0;
overflow: hidden;
& video {
max-width: 100%;
max-height: 100%;
}
}
.exit {
flex-shrink: 0;
color: hsl(0deg 0% 100% / 80%);
font-size: 14px;
line-height: 31px;
opacity: 0;
pointer-events: none;
cursor: pointer;
transition: opacity 0.2s ease;
}
&.show .exit {
pointer-events: auto;
opacity: 1;
}
.media-info-wrapper {
display: flex;
justify-content: end;
align-items: start;
gap: 20px;
padding: 20px;
background-color: transparent;
}
.media-actions {
display: flex;
flex-shrink: 0;
gap: 10px;
.lightbox-media-action {
font-size: 0.9rem;
min-width: inherit;
padding: 4px 10px;
border: 1px solid hsl(0deg 0% 100% / 60%);
background-color: transparent;
color: hsl(0deg 0% 100%);
border-radius: 4px;
text-decoration: none;
display: inline-block;
&:hover {
background-color: hsl(0deg 0% 100%);
border-color: hsl(0deg 0% 100%);
color: hsl(227deg 40% 16%);
}
}
.disabled {
opacity: 0.7;
cursor: default;
&:hover {
background-color: transparent;
color: hsl(0deg 0% 100%);
border: 1px solid hsl(0deg 0% 100% / 60%);
}
}
}
.media-description {
container: media-description / inline-size;
flex: 1;
/* setting min-width to an absolute value will make
sure the text gets truncated in case of overflow */
min-width: 0;
font-size: 1.1rem;
color: hsl(0deg 0% 100%);
.title {
vertical-align: top;
font-weight: 400;
line-height: normal;
/* Required for text-overflow */
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.user {
font-weight: 300;
line-height: normal;
text-overflow: ellipsis;
overflow: hidden;
white-space: pre;
&::before {
margin-right: 5px;
content: "\2014";
}
}
}
.player-container {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
& iframe {
aspect-ratio: 16/9;
/* for screen_width<=lg_min aspect-ratio will be
maintained given portrait mode is used */
/* in landscape max-height will prevent overflow,
however user will benefit more from using
youtube fullscreen at that point */
width: 100%;
/* maintains the aspect ratio for screen_width>=lg_min. */
max-width: $lg_min;
/* height(media_info_container) + height(center) < 200px */
max-height: calc(100dvh - 200px);
}
}
.center {
display: flex;
justify-content: center;
align-items: center;
gap: 2px;
padding: 12px 20px;
.arrow {
color: hsl(0deg 0% 100%);
/* The thumbnails do not scale, so we express these
dimensions as pixels, too. */
font-size: 35px;
line-height: 25.6px;
cursor: pointer;
opacity: 0.5;
transition: opacity 0.3s ease;
&:hover {
opacity: 1;
}
}
.image-list {
position: relative;
display: inline-block;
font-size: 0;
max-width: 40vw;
overflow: hidden;
white-space: nowrap;
.image {
display: inline-block;
vertical-align: top;
width: 50px;
height: 50px;
margin: 0 2px;
background-color: hsl(0deg 0% 94% / 20%);
opacity: 0.5;
background-size: cover;
background-position: center;
cursor: pointer;
&.selected {
opacity: 1;
}
}
.lightbox_video video {
width: 50px;
height: 50px;
}
}
}
}
/* hide media-description if it has width less than 100px */
@container media-description (max-width: 100px) {
.media-description {
.title {
display: none;
}
.user {
display: none;
}
}
}

View File

@@ -0,0 +1,96 @@
.message-edit-history-container {
.header-body {
display: flex;
align-items: center;
flex-direction: row;
justify-content: space-between;
gap: 5px;
@media (width < $lg_min) {
display: block;
}
}
.message_edit_history_content {
.highlight_text_inserted {
color: var(--color-message-edit-history-text-inserted);
background-color: var(
--color-message-edit-history-background-inserted
);
}
.highlight_text_deleted {
color: var(--color-message-edit-history-text-deleted);
background-color: var(
--color-message-edit-history-background-deleted
);
text-decoration: line-through;
word-break: break-all;
}
}
.messagebox-content {
display: block !important;
.message_content {
cursor: default !important;
}
}
#message-history-error {
/*
styles are based on .model_content
see web/styles/modal.css
*/
font-size: 1rem;
display: none;
margin: 10px;
}
.overlay_loading_indicator_style {
width: 100% !important;
height: 100% !important;
display: flex;
justify-content: center;
align-items: center;
}
}
#message-history-overlay
.message-edit-history-container
.message_edit_history_content {
/* We need to use different color for links to distinguish
them from normal text and have enough contrast so that they
are visible clearly. */
.highlight_text_inserted,
.highlight_text_deleted {
> a {
color: light-dark(
var(--color-markdown-link),
hsl(200.27deg 100% 85.49%)
);
}
}
/* Show `Link:` text of media beside it
to clarify click area for opening media
in new tab vs in lightbox. */
.highlight_text_inserted:has(.media-anchor-element .media-image-element),
.highlight_text_deleted:has(.media-anchor-element .media-image-element) {
> .media-anchor-element {
display: flex;
overflow-wrap: anywhere;
> .media-image-element {
flex-grow: 1;
flex-shrink: 0;
}
}
@media (width < $sm_min) {
> .media-anchor-element {
flex-direction: column;
}
}
}
}

View File

@@ -0,0 +1,387 @@
.message_header {
vertical-align: middle;
text-align: left;
/* We can overflow-y if the font size gets big */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-weight: 600;
position: relative;
z-index: 0;
background-color: var(--color-background);
.message-header-contents {
font-size: 1.0714em; /* 15px at 14px em */
line-height: 1.2;
display: grid;
grid-template-areas: "stream-label separator topic controls error date";
grid-template-columns: minmax(0, 1fr) 0 0 0 auto auto;
align-items: center;
height: 1.8666em; /* 28px at 15px em */
border: 1px solid var(--color-message-header-contents-border);
border-bottom-color: var(--color-message-header-contents-border-bottom);
border-radius: 7px 7px 0 0;
}
&.message_header_stream {
/* Add box shadow to hide message border (first one) and message
selected box shadow (second one) that are visible
due to top borders of sticky recipient bar being curved. */
box-shadow:
-1px -5px 0 5px var(--color-background),
1px -5px 0 0 var(--color-background);
.message-header-contents {
/* The separator column holds only the fixed-size chevron
icon, so it gets an explicit width; the other columns
hold variable-width content and stay auto/1fr. */
grid-template-columns: auto 1em auto 1fr auto auto;
align-items: stretch;
}
& .message_label_clickable {
user-select: text;
color: var(--color-text-message-header);
&:hover {
color: var(--color-text-message-header);
text-decoration: none;
}
}
& .message_label_clickable:focus {
outline: none;
text-decoration: none;
}
.stream_topic_separator {
grid-area: separator;
line-height: 0;
display: flex;
align-items: center;
color: var(--color-message-header-icon-non-interactive);
position: relative;
top: 0.5px;
}
.stream_topic {
grid-area: topic;
display: flex;
align-items: center;
padding: 5px 6px 5px 2px;
overflow: hidden;
text-overflow: ellipsis;
position: relative;
top: 0.5px;
.message_label_clickable.narrows_by_topic {
overflow: hidden;
text-overflow: ellipsis;
span.stream-topic-inner {
white-space: pre;
}
}
}
.stream-privacy i {
font-size: 1.0714em; /* 15px at 14px em */
&.zulip-icon-globe,
&.zulip-icon-hashtag {
position: relative;
top: -0.5px;
}
}
.recipient_bar_controls {
opacity: 0;
transition: opacity 0.15s ease-out;
}
.recipient_bar_controls:focus-within {
opacity: 1;
transition: opacity 0.1s ease-in;
}
/* Show the bar controls on hover with a slight delay */
&:hover {
.recipient_bar_controls {
opacity: 1;
transition: opacity 0.15s ease-in 0.1s;
}
}
/* Always show controls when header is sticky */
&.sticky_header {
.recipient_bar_controls {
opacity: 1;
transition: opacity 0.15s ease-in;
}
}
}
#report-message-preview-container & {
/* We don't need these effects applied for message preview in the report
message modal. */
box-shadow: none;
border: 0;
background: transparent;
}
}
.message_header_private_message {
/* This is required for box-shadow appear above the message content. */
z-index: 1;
box-shadow:
-1px -5px 0 5px var(--color-background),
1px -5px 0 0 var(--color-background);
background-color: var(--color-background);
.message-header-contents {
border-color: var(--color-private-message-header-border);
border-bottom-color: var(--color-private-message-header-border-bottom);
background-color: var(--color-background-private-message-header);
}
.message_label_clickable {
user-select: text;
display: flex;
padding: 5px 0 5px 10px;
font-weight: 600;
height: 1.4142em; /* 19px at 14px em */
line-height: 1.4142em; /* 19px at 14px em */
color: var(--color-text-message-header);
&:hover {
color: var(--color-text-message-header) !important;
text-decoration: none;
}
&:focus {
outline: none;
text-decoration: none;
}
.private_message_header_icon {
font-size: 1.0714em; /* 15px at 14px em */
position: relative;
top: 1px;
width: 1.0666em; /* 16px at 15px em */
height: 1.0666em; /* 16px at 15px em */
}
.private_message_header_name {
overflow: hidden;
text-overflow: ellipsis;
i.zulip-icon-bot {
vertical-align: middle;
position: relative;
top: -1px;
padding-left: 0.3em;
}
}
}
}
.message-feed {
.recipient_row {
/* Browser overlaps extra top padding of message header and
the bottom border message if this margin is not present. */
margin-bottom: 1px;
border-radius: 5px;
.message_row:last-of-type {
border-radius: var(--last-message-row-border-radius);
border-bottom: 1px solid var(--color-message-list-border);
.unread-marker-fill {
border-radius: 0 0 0 7px;
height: calc(100% - 2px);
}
.messagebox {
border-radius: 0 0 7px 7px;
padding-bottom: 4px;
}
}
.message_header + .message_row {
.messagebox {
padding-top: 5px;
}
}
.recipient_row_date {
cursor: pointer;
}
}
.message_header {
position: sticky;
top: var(--navbar-fixed-height);
/* Needs to be higher than the z-index of date_row. */
z-index: 4;
box-shadow: 0 -1px 0 0 var(--color-background);
.message-header-contents {
margin-top: var(--header-padding-bottom);
}
&.sticky_header {
box-shadow: var(--unread-marker-left) -1px 0 0
var(--color-background);
.recipient_row_date {
display: flex;
}
}
}
}
.stream_label {
grid-area: stream-label;
display: flex;
align-items: center;
gap: 5px;
padding: 5px 2px 5px 10px;
position: relative;
top: 0.5px;
text-decoration: none;
font-weight: 600;
overflow: hidden;
.zulip-icon.zulip-icon-globe {
position: relative;
top: 1px;
}
&:hover {
text-decoration: none;
}
.stream-privacy {
min-width: unset;
width: 1.1428em; /* 16px at 14px em */
height: 1.1428em; /* 16px at 14px em */
display: flex;
justify-content: center;
align-items: center;
.hashtag {
padding-right: 0;
&::after {
font-size: 1.1428em; /* 16px at 14px em */
}
}
}
.message-header-stream-name {
overflow: hidden;
text-overflow: ellipsis;
}
.message-header-stream-archived {
color: var(--color-text-message-header-archived);
}
}
.recipient_bar_controls {
grid-area: controls;
display: flex;
align-items: center;
&.topic-edit-mode .recipient-bar-control {
display: none;
}
}
.on_hover_topic_read {
opacity: 0.7;
&:hover {
cursor: pointer;
opacity: 1;
}
}
.error-icon-message-recipient {
grid-area: error;
}
.recipient_row_date {
grid-area: date;
color: var(--color-date);
font-size: calc(12em / 14);
padding: 0 10px;
text-align: right;
display: flex;
align-items: center;
font-style: normal;
font-weight: 600;
line-height: 17px; /* identical to box height, or 131% */
letter-spacing: 0.04em;
text-transform: uppercase;
padding-top: 1px;
/* Display the date when unchanged only for sticky headers. */
&.recipient_row_date_unchanged {
display: none;
.sticky_header & {
display: flex;
}
}
&.hide-date-separator-header {
display: none;
}
}
.recipient_bar_icon {
color: var(--color-message-header-icon-interactive);
opacity: 0.2;
padding-left: 6px;
padding-right: 6px;
&:hover {
opacity: 0.4 !important;
cursor: pointer;
}
}
.spectator-view .recipient_bar_controls .recipient-bar-control {
&.recipient-bar-copy-link-to-topic {
display: flex;
}
&.recipient-row-topic-menu {
display: none;
}
}
@container message-lists (width < $cq_ml_min) {
.recipient-bar-control {
display: none;
/* Always show the vdots */
&.recipient-row-topic-menu {
display: block;
}
}
}
@media (width < $ml_min) {
.without-container-query-support {
.recipient-bar-control {
display: none;
/* Always show the vdots */
&.recipient-row-topic-menu {
display: block;
}
}
}
}

1406
web/styles/message_row.css Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,168 @@
#message_view_header {
color: var(--color-text-message-view-header);
z-index: 2;
float: left;
height: var(--header-height);
width: 100%;
line-height: var(--header-height);
display: flex;
align-items: baseline;
white-space: nowrap;
cursor: default;
.hidden {
display: none;
}
/* TODO: Remove the `.navbar_title` span from the `message_view_header.hbs`
template. That span predates the use of flexbox, and makes it so that
elements like the `.narrow_description` have different line-height and
sizing contexts depending on whether it's a view or a narrow being shown. */
.message-header-stream-settings-button,
.navbar_title {
padding: 0 2px 0 6px;
overflow: hidden;
text-overflow: ellipsis;
color: inherit;
text-decoration: none;
/* Create a flex container for the icon and
stream name. */
display: flex;
/* We want to use baseline alignment so that the
stream name and narrow description sit on
the same invisible line. */
align-items: baseline;
.zulip-icon {
/* Pull the icon out of baseline alignment,
and center with stream name. */
align-self: center;
}
.zulip-icon-inbox {
/* 16px at 16px em, inherited from .navbar_title */
font-size: 1em;
}
@media (height < $short_navbar_cutoff_height) {
padding: 0 3.5px; /* based on having ~41.66% decrease */
}
.navbar-icon {
margin: 0 6px 0 5px;
/* Align all icons to center. */
align-self: center;
}
}
.message-header-navbar-title {
font-weight: 600;
/* 16px at 14px em */
font-size: 1.1429em;
/* Allow long navbar titles (e.g., stream names) to collapse. */
flex: 0 1 auto;
overflow: hidden;
text-overflow: ellipsis;
}
.message-header-archived {
color: var(--color-text-message-header-archived);
cursor: default;
padding-left: 5px;
}
.narrow_description {
/* Flexbox's baseline alignment is responsible for
matching the description's baseline to the title.
Here, normal just ensures a comfortable, i18n-friendly
line height for the description. */
line-height: normal;
background: none;
color: inherit;
font-weight: 400;
/* Padding to the left separates the description from the
view label or channel name; padding to the right keeps
the description from getting too close to the closed
search box--with a pleasing equidistant space in both
places. */
padding: 0 10px;
overflow: hidden;
text-overflow: ellipsis;
/* The very last element in the navbar is the search icon, the second
last element is the narrow description (even if it is empty when a
channel contains no description). The flex-grow property ensures this
element takes up the entirety of the white space, while flex-shrink
accommodates narrower viewports. Setting flex-basis 0 enables an
ellipsis to be displayed, as the 0 takes the place of the max-content
default that would otherwise run titles under the search box. */
flex: 1 1 0;
margin: 0;
.help_link_widget,
.fa {
margin: 0;
}
.fa-question-circle-o {
/* Override relative position for sake of
Chrome paint bug that keeps the circle
visible when ellipses are showing. */
position: static;
}
.help_link_widget {
/* With relative positioning no longer
in effect, we vertically align the
help icon with an inline-block assist,
which is present on the .fa class. */
vertical-align: middle;
}
}
.view-description-extended {
overflow-wrap: break-word;
hyphens: auto;
overflow: hidden;
white-space: normal;
background-color: var(--color-background-navbar);
border-radius: 5px;
animation: expand-extended-description 100ms ease-in both;
&.leaving-extended-view-description {
animation: contract-extended-description 100ms ease-in both !important;
}
}
}
/* Media Queries */
@media only screen and (width <= 620px) {
.view-description-extended {
padding-top: 0.5em !important;
}
}
/* stylelint-disable plugin/no-low-performance-animation-properties */
@keyframes expand-extended-description {
from {
max-height: var(--header-height);
padding-bottom: 0;
}
to {
max-height: 1000px;
padding-bottom: 5px;
}
}
@keyframes contract-extended-description {
from {
max-height: 1000px;
padding-bottom: 5px;
}
to {
max-height: var(--header-height);
padding-bottom: 0;
}
}

1316
web/styles/modal.css Normal file

File diff suppressed because it is too large Load Diff

1479
web/styles/popovers.css Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,593 @@
.activity-page {
margin: 10px;
}
.activity-head {
background-color: hsl(208deg 100% 97%);
position: sticky;
top: 0;
& th {
border-bottom: 1px solid hsl(0deg 0% 87%);
}
}
.activity-foot {
background-color: hsl(208deg 100% 97%);
font-weight: 700;
position: sticky;
bottom: 0;
}
.table-striped {
& tr.recently_active {
& td {
background-color: hsl(120deg 97% 83%);
}
&:nth-child(odd) td {
background-color: hsl(120deg 70% 76%);
}
}
}
.installation-activity-filter-container {
width: fit-content;
border-radius: 4px;
background-color: hsl(0deg 0% 92%);
border: 1px solid hsl(0deg 0% 68%);
padding: 0 8px;
margin: 8px;
}
.summary-table,
.analytics-table {
border: 1px solid hsl(0deg 0% 87%);
border-collapse: separate;
border-left: 0;
border-radius: 4px;
border-spacing: 0;
margin: 0 10px;
& th,
td {
border-left: 1px solid hsl(0deg 0% 87%);
text-align: left;
&.number {
text-align: right;
}
}
th {
vertical-align: bottom;
padding: 8px;
}
td {
border-top: 1px solid hsl(0deg 0% 87%);
white-space: nowrap;
padding: 2px 8px;
max-width: 320px;
overflow: hidden;
text-overflow: ellipsis;
}
tbody > tr:nth-child(odd) > td {
background-color: hsl(0deg 0% 98%);
}
tbody tr:first-child td {
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 tr:last-child > td:first-child {
border-bottom-left-radius: 4px;
}
tbody tr:last-child > td:last-child {
border-bottom-right-radius: 4px;
}
thead tr:first-child th {
border-top: 0;
}
}
tr.admin td:first-child {
font-weight: bold;
color: hsl(240deg 100% 50%);
font-size: 110%;
}
.good {
font-weight: bold;
color: hsl(120deg 100% 33%);
}
.bad {
font-weight: bold;
color: hsl(0deg 100% 39%);
}
.support-query-result,
.remote-support-query-result {
background-color: hsl(60deg 12% 94%);
padding: 10px;
border-radius: 7px;
box-shadow: 0 10px 7px -6px hsl(0deg 2% 45%);
margin-bottom: 10px;
& select {
height: 30px;
width: 220px;
padding: 0 6px;
font-size: 14px;
color: hsl(0deg 0% 33%);
border-radius: 4px;
border: 1px solid hsl(0deg 0% 80%);
cursor: pointer;
background-color: hsl(0deg 0% 100%);
vertical-align: text-bottom;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
&:focus {
outline: 1px dotted hsl(0deg 0% 20%);
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
}
}
& input {
width: 206px;
padding: 4px 6px;
border-radius: 4px;
border: 1px solid hsl(0deg 0% 80%);
color: hsl(0deg 0% 33%);
vertical-align: text-bottom;
transition:
border-color linear 0.2s,
box-shadow linear 0.2s;
box-shadow: inset 0 1px 1px hsl(0deg 0% 0% / 7.5%);
&: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(206.494deg 80% 62% / 60%);
}
}
.demo-label,
.cloud-label,
.deactivated-label,
.remote-label {
padding: 4px 8px 0;
font-size: 0.9em;
font-weight: bold;
color: hsl(0deg 0% 100%);
text-shadow: 0 -1px 0 hsl(0deg 0% 0% / 25%);
border-radius: 3px;
}
.demo-label {
background-color: hsl(0deg 0% 0%);
}
.cloud-label {
background-color: hsl(280deg 100% 40%);
}
.deactivated-label {
background-color: hsl(2deg 64% 53%);
}
.remote-label {
background-color: hsl(186deg 76% 36%);
}
.current-audit-log {
color: hsl(123deg 46% 34%);
}
.current-plan-data-missing,
.stale-audit-log {
color: hsl(2deg 64% 53%);
}
}
.realm-management-actions {
#new_subdomain {
width: 225px;
}
.support-add-redirect-url-section {
display: block;
padding: 6px 10px;
#add_redirect_url {
width: auto;
}
.support-redirect-url-label {
display: inline;
}
}
}
.reactivate-remote-server-button,
.deactivate-remote-server-button,
.delete-next-fixed-price-plan-button,
.approve-sponsorship-button,
.support-search-button,
.support-submit-button,
.delete-user-button,
.scrub-realm-button {
padding: 6px 12px;
display: inline-block;
margin-bottom: 0;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
font-weight: normal;
line-height: 1.4286;
text-align: center;
white-space: nowrap;
vertical-align: text-bottom;
cursor: pointer;
user-select: none;
background-image: none;
&:hover,
&:focus {
text-decoration: none;
}
&:focus {
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
}
&:active {
background-image: none;
outline: 0;
}
&[disabled] {
pointer-events: none;
cursor: not-allowed;
opacity: 0.65;
}
}
.deactivate-remote-server-button {
margin-top: 6px;
}
.support-search-form {
margin: 10px;
}
.support-search-button {
vertical-align: middle;
margin: 10px;
}
.approve-sponsorship-button {
vertical-align: middle;
color: hsl(123deg 46% 34%);
background-color: hsl(114deg 25% 85%);
border: 1px solid hsl(123deg 46% 34%);
border-radius: 2px;
&:hover,
&:focus,
&:active {
color: hsl(123deg 50% 30%);
background-color: hsl(114deg 28% 82%);
border: 1px solid hsl(123deg 50% 30%);
}
}
.support-search-button,
.support-submit-button {
color: hsl(0deg 0% 20%);
background-color: hsl(0deg 0% 100%);
border: 1px solid hsl(0deg 0% 83%);
border-radius: 2px;
&:hover,
&:focus,
&:active {
color: hsl(0deg 0% 20%);
background-color: hsl(0deg 0% 92%);
border: 1px solid hsl(0deg 0% 68%);
}
}
.deactivate-remote-server-button,
.delete-next-fixed-price-plan-button,
.delete-user-button,
.scrub-realm-button {
color: hsl(0deg 0% 100%);
background-color: hsl(2deg 64% 58%);
border: 1px solid hsl(2deg 64% 53%);
border-radius: 2px;
&:hover,
&:focus,
&:active {
color: hsl(0deg 0% 100%);
background-color: hsl(2deg 65% 50%);
border: 1px solid hsl(2deg 65% 41%);
}
}
.reactivate-remote-server-button {
color: hsl(0deg 0% 100%);
background-color: hsl(35deg 70% 56%);
border: 1px solid hsl(35deg 70% 50%);
border-radius: 2px;
&:hover,
&:focus,
&:active {
color: hsl(0deg 0% 100%);
background-color: hsl(35deg 82% 40%);
border: 1px solid hsl(35deg 82% 30%);
}
}
.reactivate-remote-server-button,
.deactivate-remote-server-button {
font-size: 15px;
line-height: 1.8;
max-width: 550px;
overflow: hidden;
text-overflow: ellipsis;
}
.user-information-section.user-deactivated,
.user-realm-information-section.realm-deactivated,
.support-query-result.realm-deactivated {
background-color: hsl(22deg 100% 92%);
}
.remote-support-query-result.remote-server-deactivated {
background-color: hsl(2deg 64% 58%);
.remote-server-section {
background-color: hsl(22deg 100% 92%);
}
.remote-realms-section {
background-color: hsl(23deg 100% 95%);
}
}
.delete-next-fixed-price-plan-form,
.delete-user-form {
margin: 8px 0;
}
.installation-activity-header {
text-align: center;
}
.support-section-header {
font-size: 1.2em;
font-weight: bold;
line-height: 20px;
margin: 0 0 8px;
}
.support-realm-icon {
max-width: 25px;
position: relative;
vertical-align: middle;
top: -2px;
}
.reactivate-remote-server-form,
.deactivate-remote-server-form,
.realm-support-information,
.remote-server-information,
.remote-realm-information,
.remote-sponsorship-details,
.support-form,
.next-plan-information,
.current-plan-information {
margin-bottom: 10px;
}
.scrub-realm-form {
padding-top: 10px;
}
.search-query.input-xxlarge {
width: 530px;
padding: 4px 14px;
margin-bottom: 0;
border-radius: 8px;
border: 1px solid hsl(0deg 0% 80%);
box-shadow: inset 0 1px 1px hsl(0deg 0% 0% / 7.5%);
transition:
border-color linear 0.2s,
box-shadow linear 0.2s;
color: hsl(0deg 0% 33%);
&: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(206.494deg 80% 62% / 60%);
}
}
@media (width <= 767px) {
.search-query.input-xxlarge {
width: 100%;
}
}
.remote-support-query-result {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-auto-rows: minmax(100px, auto);
grid-template-areas: "server realms";
}
.remote-server-section {
grid-area: server;
}
.remote-realms-section {
grid-area: realms;
}
.user-support-container {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-auto-rows: minmax(100px, auto);
grid-template-areas: "realm user";
}
.user-information-section {
grid-area: user;
}
.user-realm-information-section {
grid-area: realm;
}
.confirmation-container {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-auto-rows: minmax(100px, auto);
grid-template-areas: "confirmation-realm confirmation";
}
.confirmation-information-section {
grid-area: confirmation;
}
.confirmation-realm-section {
grid-area: confirmation-realm;
}
.confirmation-information-section,
.confirmation-realm-section,
.user-information-section,
.user-realm-information-section,
.remote-realms-section,
.remote-server-section {
border: 2px solid hsl(330deg 3% 40%);
border-radius: 4px;
padding: 10px;
}
.confirmation-realm-section,
.user-realm-information-section,
.remote-server-section {
background-color: hsl(60deg 11% 86%);
}
.confirmation-information-section,
.user-information-section,
.remote-realms-section {
background-color: hsl(60deg 12% 90%);
}
.remote-realm-container {
padding-bottom: 25px;
}
.deactivation-information-container,
.activity-header-information,
.push-notification-status,
.realm-management-actions,
.next-plan-container,
.current-plan-container,
.support-sponsorship-container {
border-radius: 4px;
padding: 10px;
margin: 10px 0;
}
.activity-header-information {
border: 2px solid hsl(330deg 3% 40%);
background-color: hsl(60deg 12% 90%);
width: fit-content;
}
.activity-header-entry {
margin: 0;
padding: 2px 0;
}
.push-notification-status,
.realm-management-actions {
border: 2px solid hsl(186deg 76% 36%);
background-color: hsl(188deg 35% 87%);
}
.deactivation-information-container {
border: 2px solid hsl(2deg 64% 58%);
background-color: hsl(23deg 100% 95%);
}
.next-plan-container,
.current-plan-container,
.support-sponsorship-container {
border: 2px solid hsl(33deg 99% 60%);
}
.support-sponsorship-container {
background-color: hsl(30deg 100% 96%);
}
.next-plan-container {
background-color: hsl(31deg 100% 91%);
}
.current-plan-container {
background-color: hsl(31deg 100% 83%);
}
.discounted-price-form {
display: flex;
flex-direction: column;
gap: 5px;
align-items: flex-start;
.support-submit-button {
margin-top: 5px;
}
}
.container {
width: 940px;
margin-right: auto;
margin-left: auto;
@media (width <= 767px) {
width: auto;
}
@media (767px < width <= 979px) {
width: 724px;
}
@media (width >= 1180px) {
width: 1170px;
}
}

View File

@@ -0,0 +1,752 @@
.billing-upgrade-page {
font-family: "Source Sans 3 VF", sans-serif;
background-color: hsl(0deg 0% 98%);
height: 100vh;
.page-content {
padding: 100px 0 30px;
}
.main {
width: 800px;
max-width: 90%;
margin: 0 auto;
}
& h1 {
margin: 30px 0;
font-weight: bold;
}
.nav {
margin-bottom: 0;
}
.support-link {
margin: 10px 20px;
& a,
a:hover,
a:visited {
color: hsl(170deg 47% 33%);
font-weight: 400;
}
}
.license-management,
.payment-schedule {
& label {
display: inline-block;
}
& input[type="radio"] {
display: none;
&:checked {
+ .box {
background-color: hsl(153deg 32% 55%);
color: hsl(0deg 0% 100%);
border-color: hsl(152deg 33% 39%);
}
}
}
.box {
width: 120px;
height: 70px;
background-color: hsl(0deg 0% 94%);
transition: 0.2s ease;
transition-property: background-color, border-color;
display: inline-block;
text-align: center;
cursor: pointer;
position: relative;
border: 1px solid hsl(0deg 0% 91%);
border-radius: 8px;
margin: 0 10px 5px 0;
padding: 30px 20px;
vertical-align: top;
&:hover {
background-color: hsl(0deg 0% 91%);
border-color: hsl(0deg 0% 80%);
}
.schedule-time {
font-weight: bold;
font-size: 1.2em;
margin-top: 10px;
}
.schedule-amount {
margin-top: 5px;
font-size: 1.1em;
height: 50px;
}
.schedule-amount-2 {
font-size: 0.9em;
}
.management-type {
font-weight: bold;
font-size: 1.2em;
margin-top: 10px;
}
.management-type-text {
font-size: 1.1em;
margin-top: 5px;
}
}
}
.stripe-button-el {
padding: 11px 25px;
font-weight: 400;
color: hsl(0deg 0% 100%);
background: linear-gradient(
145deg,
hsl(191deg 56% 55%),
hsl(169deg 65% 42%)
);
box-shadow: 0 3px 10px hsl(0deg 0% 0% / 20%);
border: 0;
height: 40px;
margin: 5px 0 0;
& span {
background: 0;
box-shadow: none;
font-family: "Source Sans 3 VF", sans-serif;
line-height: 20px;
}
}
.stripe-button-el:hover {
background-color: hsl(169deg 65% 42%);
box-shadow: 0 3px 10px hsl(0deg 0% 0% / 30%);
}
.stripe-button-el:active,
.stripe-button-el:enabled:active {
background-color: hsl(169deg 70% 32%);
& span {
background: 0;
box-shadow: none;
}
}
.stripe-button-el:disabled {
& span {
background: none;
}
}
.stripe-button-el:hover:disabled {
box-shadow: none;
background-color: hsl(0deg 0% 78%);
pointer-events: none;
}
.invoice-button {
font-size: 19px;
}
.upgrade-button-container {
display: inline-block;
}
#manual_license_count,
#invoiced_licenses {
width: 50px;
}
#licenses_at_next_renewal_input,
#new_licenses_input {
width: 206px;
}
#error-message-box {
margin-top: 10px;
font-weight: 600;
display: none;
}
#restartsession-loading,
#webhook-loading,
#sponsorship-loading,
#planchange-loading,
#licensechange-loading,
#cardchange-loading,
#invoice-loading,
#autopay-loading {
display: none;
min-height: 55px;
text-align: center;
}
#restartsession-success,
#webhook-success,
#sponsorship-success,
#planchange-success,
#licensechange-success,
#cardchange-success,
#invoice-success,
#autopay-success {
text-align: center;
display: none;
}
#restartsession-error,
#webhook-error,
#sponsorship-error,
#planchange-error,
#licensechange-error,
#cardchange-error,
#invoice-error,
#autopay-error {
text-align: center;
display: none;
}
.zulip-loading-logo {
margin: 0 auto;
width: 30px;
/* Adjusting height moves the loading spinner vertically.
Manually adjusted to align spinner and z-logo. */
height: 23px;
}
.zulip-loading-logo svg circle {
fill: hsl(0deg 0% 27%);
stroke: hsl(0deg 0% 27%);
}
.zulip-loading-logo svg path {
fill: hsl(0deg 0% 100%);
stroke: hsl(0deg 0% 100%);
}
#restartsession_loading_indicator,
#webhook_loading_indicator,
#sponsorship_loading_indicator,
#planchange_loading_indicator,
#licensechange_loading_indicator,
#cardchange_loading_indicator,
#invoice_loading_indicator,
#autopay_loading_indicator {
margin: 10px auto;
}
#restartsession_loading_indicator_box_container,
#webhook_loading_indicator_box_container,
#sponsorship_loading_indicator_box_container,
#planchange_loading_indicator_box_container,
#licensechange_loading_indicator_box_container,
#cardchange_loading_indicator_box_container,
#invoice_loading_indicator_box_container,
#autopay_loading_indicator_box_container {
position: absolute;
left: 50%;
}
#restartsession_loading_indicator_box,
#webhook_loading_indicator_box,
#sponsorship_loading_indicator_box,
#planchange_loading_indicator_box,
#licensechange_loading_indicator_box,
#cardchange_loading_indicator_box,
#invoice_loading_indicator_box,
#autopay_loading_indicator_box {
position: relative;
left: -50%;
top: -41px;
z-index: 10;
border-radius: 6px;
}
#restartsession_loading_indicator .loading_indicator_text,
#webhook_loading_indicator .loading_indicator_text,
#sponsorship_loading_indicator .loading_indicator_text,
#planchange_loading_indicator .loading_indicator_text,
#licensechange_loading_indicator .loading_indicator_text,
#cardchange_loading_indicator .loading_indicator_text,
#invoice_loading_indicator .loading_indicator_text,
#autopay_loading_indicator .loading_indicator_text {
margin-left: -25px;
}
& select,
input,
textarea {
font-family: inherit;
}
}
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
appearance: none;
margin: 0;
}
input[name="licenses"] {
appearance: textfield;
}
#confirm-licenses-modal .dialog_submit_button:active {
/* This is needed to avoid button background changing
when clicking on it due to "button:active" CSS in
legacy_landing_page.css. */
background-color: hsl(240deg 96% 68%);
}
#account-deactivated-success-page .white-box,
#server-deactivate-page .white-box,
#remote-billing-confirm-login-page .white-box,
#remote-billing-confirm-email .white-box,
#server-login-page .white-box,
#upgrade-page .white-box,
#billing-page .white-box,
.sponsorship-page .white-box {
margin: 30px;
}
#account-deactivated-success-page #account-deactivated-success-page-details,
#server-deactivate-page #server-deactivate-details,
#server-login-page #server-login-page-details,
#upgrade-page #upgrade-page-details,
#billing-page #billing-page-details,
.sponsorship-page #sponsorship-form {
margin: 0;
}
#sponsorship-discount-details {
font-weight: normal;
margin: 2px 14px;
padding-top: 0;
text-align: left;
overflow-wrap: break-word;
width: 450px;
}
@media (width < 600px) {
#server-login-page,
#billing-page,
.sponsorship-page {
transform: scale(0.8);
margin: -50px;
}
.sponsorship-status-page {
transform: scale(0.8);
margin: -10px -50px -50px;
}
#upgrade-page {
transform: scale(0.8);
margin: -50px 0;
}
}
@media (width < 460px) {
#upgrade-page,
#billing-page,
.sponsorship-page {
transform: scale(0.6);
margin: -150px -100px;
}
#server-login-page,
.sponsorship-status-page {
transform: scale(0.6);
margin: -50px -150px -100px;
}
#upgrade-page {
transform: scale(0.6);
margin: -150px 0;
}
}
#billing-page .billing-status-page .pitch {
width: 100%;
}
.billing-status-page {
font-weight: 400;
}
.sponsorship-status-page .white-box {
font-weight: 400;
margin: 30px;
padding: 0;
}
.billing-status-page p:last-child,
.sponsorship-status-page p:last-child {
margin-bottom: 0;
}
.remote-billing-confirm-login-form-field-error,
.server-login-form-field-error,
.upgrade-page-error,
.sponsorship-field-error,
.billing-page-error,
.billing-page-success {
text-align: left;
margin-left: 2px;
margin-top: 5px;
}
.header.portico-header {
display: block;
}
#upgrade-page #upgrade-manual-license-count-wrapper,
#billing-page .input-box-number {
text-align: left;
}
#account-deactivated-success-page .pitch,
#server-deactivate-details .pitch,
#remote-billing-confirm-login-page .pitch,
#server-login-page .pitch,
.sponsorship-page .pitch,
.sponsorship-status-page .pitch,
#upgrade-page .pitch,
#billing-page .pitch {
width: 600px;
}
#billing-page .number-input-with-label {
display: flex;
align-items: flex-end;
}
#upgrade-page #manual_license_count,
#billing-page
.white-box
.input-box-number
.number-input-with-label
.short-width-number-input {
width: 100px;
}
#billing-page .number-input-with-label .licence-count-in-use {
padding-left: 10px;
vertical-align: bottom;
position: relative;
bottom: 15px;
}
#billing-page #org-billing-frequency-confirm-button,
#billing-page .license-count-update-button {
margin: 0 auto;
font-size: 1.1rem;
margin-right: 0;
width: 100px;
}
#billing-page #org-billing-frequency-confirm-button.hide,
#billing-page .license-count-update-button.hide {
display: none;
}
#billing-page #org-billing-frequency-confirm-button {
margin: 0;
display: block;
position: absolute;
top: 25px;
right: 0;
}
#billing-page #current-license-change-form,
#billing-page #next-license-change-form {
margin-bottom: 0;
}
.billing-page-success {
display: none;
}
#billing-page-details .input-box.billing-page-field.plan-toggle-action,
#billing-page-details .input-box.billing-page-field.user-stripe-card-update {
margin-top: 10px;
text-align: left;
}
#update-card-button {
position: relative;
.billing-button-loader {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
margin: 0;
}
}
#billing-page-details .plan-toggle-action .plan-toggle-action-button {
margin: 20px auto 0;
background-color: hsl(0deg 70% 40%);
}
#reactivate-subscription
.reactivate-current-plan-button.plan-toggle-action-button {
background-color: hsl(146deg 92% 26%);
}
#server-deactivate-button,
#remote-billing-confirm-login-button,
#remote-billing-confirm-email-button,
#server-login-button,
#org-upgrade-button {
margin: 0;
width: 100%;
}
#upgrade-page-details #payment-schedule-select {
&:disabled {
&:hover {
cursor: not-allowed;
}
}
}
#upgrade-page-details #upgrade-payment-by-invoice-container {
padding-top: 0;
}
#upgrade-page-details #upgrade-payment-method-container {
display: flex;
align-items: center;
gap: 10px;
}
#upgrade-page-details #upgrade-add-card-button,
#billing-page-details .user-stripe-card-update .user-stripe-card-update-button {
margin: 0;
width: auto;
}
#upgrade-page-details #upgrade-cardchange-form {
margin: 0;
}
#server-login-page-details #server-login-form-title .not-editable-realm-field {
font-weight: 500;
padding-top: 0;
}
#server-login-page-details #server-login-form-title,
#billing-page-details .stripe-customer-billing-portal,
#billing-page-details #complimentary-access-end-notice,
#billing-page-details .next-payment-info {
width: 450px;
padding-top: 0;
}
#sponsorship-status-page-details .input-box.sponsorship-form-field:first-child,
#billing-page-details .input-box.billing-page-field:first-child {
margin-top: 10px;
}
.sponsorship-status-page .support-link,
#billing-page .support-link {
text-align: center;
}
.micromodal {
.modal__title {
font-weight: 600;
}
.modal__content {
font-weight: 400;
}
}
#upgrade-page-details #free-trial-top-banner {
margin-top: 10px;
}
#upgrade-page-details .support-link,
#account-deactivated-success-page-details .not-editable-realm-field,
#server-deactivate-details
#server-deactivate-form-top-description
.not-editable-realm-field,
#remote-billing-confirm-email-intro .not-editable-realm-field,
#billing-page-details .billing-frequency-message.not-editable-realm-field,
#free-trial-top-banner .not-editable-realm-field,
#upgrade-page-details .license-management-section .not-editable-realm-field,
#upgrade-page-details #payment-by-invoice-setup .not-editable-realm-field,
#upgrade-page-details .top-of-page-notice .not-editable-realm-field {
padding-top: 0;
}
#free-trial-alert-message,
#free-trial-top-banner {
text-align: left;
}
#upgrade-page-sponsorship-pending-message-top,
#billing-sponsorship-pending-message-top,
#sponsorship-status-success-message-top,
#upgrade-success-message-top,
#billing-success-message-top {
display: block;
text-align: center;
font-weight: 400;
padding: 10px 20px;
width: 600px;
}
.upgrade-page-success {
display: none;
padding: 8px 0;
width: 450px;
text-align: center;
margin: auto;
}
#upgrade-payment-info {
display: grid;
grid-template: auto auto auto 1fr / 1fr auto;
grid-template-areas:
"payment-info add-card"
"stripe-portal stripe-portal"
"alert-message alert-message";
width: 450px;
margin: auto;
}
#update-invoice-billing-info {
display: flex;
flex-direction: column;
width: 450px;
margin: auto;
}
#upgrade-payment-info .alert {
grid-area: alert-message;
}
#upgrade-page-details .stripe-billing-information {
grid-area: stripe-portal;
padding-top: 2px;
}
#upgrade-page-details #upgrade-payment-method-wrapper {
margin-left: 0;
grid-area: payment-info;
width: 100%;
}
/* Used class twice to increase specificity */
#upgrade-payment-info .upgrade-add-card-container.upgrade-add-card-container {
grid-area: add-card;
width: auto;
margin-right: 0;
}
#billing-page-details
.org-billing-frequency-wrapper.input-box
.billing-frequency-select {
width: 150px;
}
#billing-page-details .org-billing-frequency-wrapper.billing-page-field,
#upgrade-page-details .upgrade-add-card-container {
text-align: left;
}
#server-deactivate-error,
#server-login-error,
#autopay-error {
font-weight: 400;
margin-left: 0;
}
#sponsorship-status-page-details .input-box.sponsorship-form-field {
margin: 20px;
}
#sponsorship-form .upgrade-button-container {
display: block;
text-align: center;
}
.billing-page-discount {
opacity: 0.8;
font-weight: 400;
}
.remote-billing-button-loader {
display: none;
vertical-align: top;
position: relative;
height: 30px;
margin-top: -10px;
top: 5px;
}
#server-deactivate-error,
#server-login-error {
text-align: left;
margin: 0 auto;
max-width: 400px;
padding: 10px 25px;
}
#upgrade-page-details #due-today-for-future-update-wrapper {
display: none;
}
#remote-billing-confirm-login-form .text-error {
margin-bottom: 15px;
}
#server-deactivate-details .upgrade-button-container,
#server-login-page-button-container {
width: 450px;
margin: 30px auto 0;
}
#remote-billing-confirm-login-button,
#remote-billing-confirm-email-button,
#billing-page-details #complimentary-access-end-notice-wrapper {
margin-top: 10px;
}
#remote-billing-confirm-email-form #remote-billing-confirm-email-intro {
margin-top: -10px;
}
#remote-billing-confirm-login-form #remote-billing-confirm-login-tos-wrapper {
margin: 0 auto 10px;
}
#account-deactivated-success-page-details
#account-deactivated-success-page-top-message {
margin-top: 10px;
}
.flat-discount-minus-sign {
position: absolute;
left: -10px;
}
.flat-discount-separator {
border-bottom: 1px solid hsl(0deg 0% 0%);
}
.toggle-license-management {
cursor: pointer;
}
#billing-page-details .billing-page-license-management-description {
padding-top: 5px;
}

View File

@@ -0,0 +1,732 @@
.showing-cloud .self-hosted-comparison,
.showing-self-hosted .cloud-comparison {
display: none;
}
.zulip-plans-comparison {
--color-heading-text: hsl(0deg 0% 100%);
--color-table-text: hsl(223deg 40% 25%);
--color-table-background: hsl(0deg 0% 100% / 80%);
--color-table-link: hsl(210deg 94% 30%);
--color-table-link-hover: hsl(210deg 100% 45%);
--color-table-link-active: hsl(210deg 100% 40%);
--color-table-link-decoration: hsl(210deg 94% 30% / 20%);
--color-table-link-decoration-hover: hsl(210deg 100% 45% / 70%);
--color-table-link-decoration-active: hsl(210deg 100% 40%);
--border-radius-table: 11px;
.icon-cloud {
--icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' viewBox='0 0 16 16'%3e%3cpath fill='%23313F68' d='M4.333 13.333c-1.01 0-1.875-.35-2.591-1.05-.717-.7-1.075-1.555-1.075-2.566 0-.867.26-1.64.783-2.317A3.465 3.465 0 0 1 3.5 6.1a4.525 4.525 0 0 1 1.667-2.483A4.561 4.561 0 0 1 8 2.667c1.3 0 2.403.452 3.308 1.358.906.906 1.359 2.008 1.359 3.308a2.91 2.91 0 0 1 1.908.992c.506.572.758 1.242.758 2.008 0 .834-.291 1.542-.875 2.125a2.893 2.893 0 0 1-2.125.875h-8Z' opacity='.5'/%3e%3c/svg%3e");
}
.icon-self-hosted {
--icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' viewBox='0 0 16 16'%3e%3cpath fill='%23313F68' fill-opacity='.5' d='M11.333 10.667a.98.98 0 0 0 .709-.284.957.957 0 0 0 .291-.716.964.964 0 0 0-.291-.709.965.965 0 0 0-.709-.291.957.957 0 0 0-.716.291.979.979 0 0 0-.284.709.971.971 0 0 0 1 1ZM1.333 6 3.6 3.733c.122-.122.264-.22.425-.291.161-.073.336-.109.525-.109h6.883c.19 0 .364.036.525.109.161.072.303.17.425.291L14.667 6H1.333Zm1.334 6.667a1.29 1.29 0 0 1-.95-.384 1.29 1.29 0 0 1-.384-.95v-4h13.334v4c0 .378-.13.695-.392.95a1.298 1.298 0 0 1-.942.384H2.667Z'/%3e%3c/svg%3e");
}
.icon-infinity {
--icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none' viewBox='0 0 24 24'%3e%3cpath fill='black' fill-rule='evenodd' d='M2.464 8.464A5 5 0 0 1 6 7c2.16 0 4.155 1.251 6 3.4C13.845 8.251 15.84 7 18 7a5 5 0 1 1 0 10c-2.16 0-4.155-1.251-6-3.4-1.845 2.149-3.84 3.4-6 3.4a5 5 0 0 1-3.536-8.536ZM10.731 12C8.983 9.857 7.4 9 6 9a3 3 0 1 0 0 6c1.4 0 2.983-.857 4.731-3Zm2.538 0c1.748 2.143 3.331 3 4.731 3a3 3 0 0 0 0-6c-1.4 0-2.983.857-4.731 3Z' clip-rule='evenodd'/%3e%3c/svg%3e");
}
.icon-check {
--icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none' viewBox='0 0 24 24'%3e%3cpath fill='black' fill-rule='evenodd' d='M20.707 5.793a1 1 0 0 1 0 1.414l-11 11a1 1 0 0 1-1.414 0l-5-5a1 1 0 1 1 1.414-1.414L9 16.086 19.293 5.793a1 1 0 0 1 1.414 0Z' clip-rule='evenodd'/%3e%3c/svg%3e");
}
.icon-x {
--icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none' viewBox='0 0 24 24'%3e%3cpath fill='black' d='M18.707 6.707a1 1 0 0 0-1.414-1.414L12 10.586 6.707 5.293a1 1 0 0 0-1.414 1.414L10.586 12l-5.293 5.293a1 1 0 1 0 1.414 1.414L12 13.414l5.293 5.293a1 1 0 0 0 1.414-1.414L13.414 12l5.293-5.293Z'/%3e%3c/svg%3e");
}
.icon-wrench {
--icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none' viewBox='0 0 24 24'%3e%3cpath fill='black' fill-rule='evenodd' d='M16.578 3.36a5.25 5.25 0 0 0-5.866 7.302.75.75 0 0 1-.153.84l-6.91 6.91a1.371 1.371 0 0 0 1.94 1.94l6.91-6.91a.75.75 0 0 1 .839-.154 5.25 5.25 0 0 0 7.301-5.866l-2.915 2.915a1.75 1.75 0 0 1-2.45 0l-.006-.005-1.605-1.606a1.75 1.75 0 0 1 0-2.45l.005-.005 2.91-2.91Zm-2.293-1.503a6.75 6.75 0 0 1 3.993.491.75.75 0 0 1 .22 1.214l-3.766 3.766a.25.25 0 0 0 0 .347l1.593 1.593a.25.25 0 0 0 .347 0L20.438 5.5a.75.75 0 0 1 1.214.221 6.75 6.75 0 0 1-8.445 9.131l-6.557 6.56a2.871 2.871 0 1 1-4.06-4.06l6.558-6.56a6.75 6.75 0 0 1 5.138-8.935Z' clip-rule='evenodd'/%3e%3c/svg%3e");
}
.icon {
vertical-align: middle;
display: inline-block;
flex-shrink: 0;
background-color: currentcolor;
mask-position: center;
mask-repeat: no-repeat;
mask-image: var(--icon);
width: 24px;
height: 24px;
}
.icon-plan {
/* Smaller icons within the table headings. */
width: 16px;
height: 16px;
}
.icon-with-copy {
/* Treat copy and a nearby icon as
a unit when reflowing text for narrower
viewports. */
display: inline-block;
}
.grouped-icons {
display: flex;
justify-content: center;
gap: 4px;
}
h2 {
font-family: var(--font-ss3);
font-weight: 450;
font-size: 44px;
line-height: 120%;
text-align: center;
margin: 64px 0 10px;
color: var(--color-heading-text);
}
.h2-subheader {
font-family: var(--font-ops);
font-weight: 400;
font-size: 16px;
line-height: 145%;
text-align: center;
color: var(--color-heading-text);
/* Let subheader text fill the same area as
text in the comparison table below. */
padding: 0 14px;
a {
color: inherit;
text-decoration: underline;
text-decoration-color: hsl(0deg 0% 100% / 50%);
transition: text-decoration 0.4s ease-out;
&:hover {
text-decoration-color: hsl(0deg 0% 100%);
text-decoration-thickness: 2px;
transition: none;
}
}
}
.comparison-tabs {
width: max-content;
margin: 0 auto;
display: flex;
justify-content: center;
gap: 3px;
background-color: hsl(0deg 0% 100% / 70%);
padding: 3px;
border-radius: 6px;
}
.comparison-tab {
font-family: var(--font-ops);
font-size: 14px;
padding: 4px 16px;
color: hsl(223deg 40% 30%);
font-weight: 600;
border: none;
background-color: hsl(0deg 0% 100% / 0%);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border-radius: 3px;
cursor: pointer;
transition: background-color 120ms ease-out;
}
.comparison-tab:hover {
transition: none;
background-color: hsl(0deg 0% 100% / 40%);
}
.comparison-tab:active {
background-color: hsl(0deg 0% 100% / 70%);
}
.comparison-tab._selected {
background-color: hsl(0deg 0% 100% / 80%);
cursor: default;
}
.comparison-table {
font-family: var(--font-ss3);
font-size: 16px;
line-height: 18px;
text-align: center;
border-collapse: collapse;
margin: 24px auto 48px;
border-radius: var(--border-radius-table);
color: var(--color-table-text);
background: var(--color-table-background);
}
&.education-features-comparison {
--zulip-cell-background-color: hsl(217deg 71% 78%);
--zulip-cell-foreground-color: hsl(210deg 4% 10%);
--zulip-column-border-width: 8px;
--zulip-column-border-negative-offset: calc(
var(--zulip-column-border-width) * -1
);
padding-bottom: 48px;
.comparison-table {
font-weight: normal;
table-layout: fixed;
width: 100%;
max-width: 800px;
}
.comparison-table-feature {
width: 30%;
}
th.zulip-cell {
padding: 0;
}
th.zulip-cell .label-zulip {
display: block;
/* We make the border larger by 1px to avoid an
occasional rendering bug in Chrome that lets
daylight in between the border here and the
top of the table row. */
border-top: calc(var(--zulip-column-border-width) + 1px) solid
var(--zulip-cell-background-color);
margin: var(--zulip-column-border-negative-offset)
var(--zulip-column-border-negative-offset) 0;
/* The hard-coded numeric values here keep the Zulip
label aligned with other labels in the table-header
row. */
padding: calc(var(--zulip-column-border-width) + 7px)
calc(var(--zulip-column-border-width) + 2px)
calc(var(--zulip-column-border-width) + 5px);
}
.zulip-cell {
color: var(--zulip-cell-foreground-color);
background: var(--zulip-cell-background-color);
border-left: var(--zulip-column-border-width) solid
var(--zulip-cell-background-color);
border-right: var(--zulip-column-border-width) solid
var(--zulip-cell-background-color);
/* Don't display stripes in the Zulip column. */
border-top-color: var(--zulip-cell-background-color);
}
.zulip-cell .bottom-border {
display: block;
width: 100%;
padding: 0 var(--zulip-column-border-width);
border-bottom: var(--zulip-column-border-width) solid
var(--zulip-cell-background-color);
position: absolute;
bottom: var(--zulip-column-border-negative-offset);
left: var(--zulip-column-border-negative-offset);
}
}
.comparison-table a {
color: var(--color-table-link);
text-underline-offset: 4px;
text-decoration-thickness: 1px;
text-decoration-color: var(--color-table-link-decoration);
}
.comparison-table a:hover {
color: var(--color-table-link-hover);
text-decoration-color: var(--color-table-link-decoration-hover);
}
.comparison-table a:active {
color: var(--color-table-link-active);
text-decoration-color: var(--color-table-link-decoration-active);
}
.comparison-table th:first-child {
border-top-left-radius: var(--border-radius-table);
}
.comparison-table th:last-child {
border-top-right-radius: var(--border-radius-table);
}
.comparison-table th,
.comparison-table .subheader {
position: sticky;
font-weight: 700;
color: hsl(223deg 40% 30% / 100%);
background: hsl(209deg 41% 94%);
}
.comparison-table .competitor-cell {
padding: 12px 10px 10px;
font-weight: normal;
/* Use gray color on competitor cells for
checkmarks and warnings (language counts). */
&.comparison-value-positive,
&.comparison-value-warning {
color: hsl(224deg 8% 50% / 100%);
}
/* Use red color on competitor cells for
X marks. */
&.comparison-value-negative {
color: hsl(0deg 36% 58%);
}
}
.comparison-table th .icon {
display: none;
}
.subheader-filler {
background: hsl(209deg 41% 94%);
}
.comparison-table th {
padding: 12px 2px 10px;
top: 60px;
z-index: 1;
}
.comparison-table td.subheader.comparison-table-feature {
font-weight: 600;
color: hsl(223deg 40% 30% / 80%);
top: 64px;
z-index: 2;
padding-top: 8px;
padding-bottom: 8px;
text-transform: uppercase;
font-size: 15px;
letter-spacing: 0.1ch;
}
.comparison-table td {
padding: 8px;
border-top: 1px solid hsl(209deg 40% 40% / 30%);
line-height: 18px;
}
.comparison-table colgroup col:first-child {
width: 340px;
}
.comparison-table colgroup col {
width: 112px;
}
.comparison-table th.comparison-table-feature,
.comparison-table td.comparison-table-feature {
text-align: left;
padding-left: 14px;
}
.comparison-table td.comparison-table-feature {
color: hsl(223deg 40% 16%);
}
.comparison-table-feature-desc {
font-size: 14px;
opacity: 0.7;
margin-top: 4px;
}
.comparison-value-positive {
color: hsl(147deg 80% 29% / 100%);
}
.comparison-value-warning {
color: hsl(42deg 85% 35% / 100%);
}
.comparison-value-negative {
color: hsl(224deg 8% 50% / 100%);
}
.comparison-value-positive,
.comparison-value-warning,
.comparison-value-negative {
/* Set positioning context for use
with tooltip hovers on icons. */
position: relative;
/* Make non-icon text (e.g., "Billed hourly")
less prominent than other text in the table's
rows or columns. */
font-size: 0.9em;
}
&:not(.education-features-comparison) {
.comparison-table tr {
transition: background-color 200ms ease-out;
}
.comparison-table tbody tr:hover {
background-color: hsl(0deg 0% 100% / 30%);
}
.comparison-value-positive::after,
.comparison-value-warning::after,
.comparison-value-negative::after {
content: attr(data-title);
white-space: nowrap;
font-size: 14px;
line-height: 14px;
font-weight: 400;
display: block;
padding: 2px 4px;
border-radius: 5px;
background-color: hsl(208deg 43% 93%);
/* the color of mixed hover row with bg */
position: absolute;
top: calc(50% + 11px);
left: 50%;
/* Keep tooltips over top of other elements,
including the headers within the table. */
z-index: 3;
transform: translateX(-50%);
visibility: hidden;
opacity: 0;
transition:
visibility 301s linear,
opacity 250ms ease-out;
}
.comparison-value-positive:hover::after,
.comparison-value-warning:hover::after,
.comparison-value-negative:hover::after {
transition:
visibility 0s linear,
opacity 250ms ease-out;
transition-delay: 200ms;
visibility: visible;
opacity: 1;
}
}
@media (width <= 940px) {
h1 {
font-size: 40px;
}
.h1-subheader {
font-size: 16px;
}
h2 {
font-size: 32px;
margin-bottom: 4px;
}
.h2-subheader {
font-size: 14px;
}
}
@media (width <= 730px) {
.comparison-table th {
font-size: 14px;
line-height: 18px;
box-sizing: border-box;
height: 44px;
padding: 4px 2px;
}
.comparison-table td {
font-size: 14px;
line-height: 18px;
}
.comparison-table td.subheader.comparison-table-feature {
font-size: 13px;
line-height: 16px;
box-sizing: border-box;
padding-top: 10px;
padding-bottom: 8px;
}
.comparison-table td.comparison-table-feature {
font-size: 14px;
}
.comparison-table-feature-desc {
font-size: 12px;
line-height: 14px;
}
}
@media (width <= 670px) {
.comparison-table .competitor-cell {
padding: 12px 2px 10px;
}
}
@media (width <= 500px) {
.comparison-table th {
font-size: 13px;
line-height: 14px;
}
.comparison-table th.comparison-table-feature,
.comparison-table td.comparison-table-feature {
padding-left: 8px;
}
.comparison-table td {
padding: 4px 2px;
border-top: 1px solid hsl(209deg 40% 40% / 30%);
line-height: 18px;
}
.comparison-table td.comparison-value-positive,
.comparison-table td.comparison-value-warning,
.comparison-table td.comparison-value-negative {
font-size: 13px;
line-height: 14px;
}
.comparison-value-positive::after,
.comparison-value-warning::after,
.comparison-value-negative::after {
display: none;
}
}
}
/* No header to accommodate on self-hosted realms,
so keep sticky headers at the top of the viewport. */
.zulip-plans-comparison.is-self-hosted-realm {
.comparison-table {
th,
td.subheader.comparison-table-feature {
top: 0;
}
}
}
/* Change comparison-table visibility on /plans
based on active plan tab. */
#showing-tab-cloud,
#showing-tab-hosted,
#showing-tab-all {
.comparison-tab-cloud,
.comparison-tab-self-hosted,
.comparison-tab-all {
background-color: hsl(0deg 0% 100% / 0%);
cursor: pointer;
&:hover {
background-color: hsl(0deg 0% 100% / 40%);
}
}
}
.showing-cloud:not(:has(div[id^="showing-tab"])),
#showing-tab-cloud {
.comparison-tab-cloud {
background-color: hsl(0deg 0% 100%);
cursor: default;
&:hover {
background-color: hsl(0deg 0% 100%);
}
}
#self-hosted-plan-comparison,
#all-plan-comparison,
.subheader-open-source,
.subheader-self-hosted-legend,
.self-hosted-feature-only,
.self-hosted-cell {
display: none;
}
#cloud-plan-comparison,
.cloud-cell {
display: revert;
}
/* When only Cloud plans appear in the comparison table,
we need to set the border-radius here that is otherwise
displayed on the table itself.
TODO: Unset this property when the header row is stuck,
so that the little flutter of feature-table lines cannot
be seen by eagle-eyed users. */
th.last-cloud-th {
border-top-right-radius: var(--border-radius-table);
}
}
.showing-self-hosted:not(:has(div[id^="showing-tab"])),
#showing-tab-hosted {
.comparison-tab-self-hosted {
background-color: hsl(0deg 0% 100%);
cursor: default;
&:hover {
background-color: hsl(0deg 0% 100%);
}
}
#cloud-plan-comparison,
#all-plan-comparison,
.cloud-cell {
display: none;
}
#self-hosted-plan-comparison,
.subheader-open-source,
.subheader-self-hosted-legend,
.self-hosted-feature-only,
.self-hosted-cell {
display: revert;
}
th.last-cloud-th {
border-top-right-radius: unset;
}
}
#showing-tab-all {
.comparison-tab-all {
background-color: hsl(0deg 0% 100%);
cursor: default;
&:hover {
background-color: hsl(0deg 0% 100%);
}
}
#cloud-plan-comparison,
#self-hosted-plan-comparison {
display: none;
}
.subheader-open-source,
.subheader-self-hosted-legend,
#all-plan-comparison,
.cloud-cell,
.self-hosted-cell,
.self-hosted-feature-only {
display: revert;
}
th.last-cloud-th {
border-top-right-radius: unset;
}
.comparison-table th .icon {
display: revert;
}
.comparison-table th {
vertical-align: top;
}
.comparison-table th.comparison-table-feature {
padding-top: 28px;
}
.comparison-table td.stuck {
padding-top: 24px;
}
@media (width <= 730px) {
.comparison-table th {
padding: 8px 2px;
}
.comparison-table th.comparison-table-feature {
padding-top: 24px;
padding-left: 14px;
}
.comparison-table td.subheader.comparison-table-feature {
/* Line up features with plan titles. */
line-height: 18px;
}
.comparison-table td.stuck {
padding-top: 20px;
padding-bottom: 6px;
vertical-align: top;
}
}
@media (width <= 684px) {
.comparison-table td.stuck {
height: 60px;
}
}
@media (width <= 500px) {
.comparison-table td.subheader.comparison-table-feature {
/* Line up features with plan titles. */
line-height: 14px;
min-width: auto;
}
.comparison-table td.stuck {
top: 64px;
height: 55px;
}
}
@media (width <= 405px) {
/* For very tiny views, we get rid of the luxury
of any horizontal padding that's left. */
.comparison-table-feature,
.cloud-cell,
.self-hosted-cell {
padding-left: 0;
padding-right: 0;
}
/* Keep the comparison-table features from
colliding with the left screen edge, though. */
.comparison-table-feature {
padding-left: 2px;
}
}
}
@media (width <= 500px) {
.comparison-table td.subheader.comparison-table-feature {
min-width: max-content;
}
}
@media (width <= 460px) {
.zulip-plans-comparison .comparison-tab-all {
display: none;
}
}
@media (width <= 356px) {
.zulip-plans-comparison
.comparison-table
td.subheader.comparison-table-feature {
height: 51px;
top: 60px;
}
.zulip-plans-comparison .comparison-table th {
height: 51px;
}
}
.features-page {
/* Always suppress subheadings on /features */
#cloud-plan-comparison,
#self-hosted-plan-comparison,
#all-plan-comparison {
display: none !important;
}
/* Maintain the margin-top ordinarily set on
the subheadings. */
.comparison-tabs {
margin-top: 64px;
}
}

View File

@@ -0,0 +1,44 @@
.dev-help-page {
.white-box {
width: 40%;
margin: 0 auto;
}
.dev-help-header {
text-align: center;
margin-bottom: 1em;
}
.invalid-path-error {
color: hsl(1.1deg 44.7% 50.4%);
font-weight: 400;
display: block;
text-align: center;
margin-top: 1em;
}
.dev-help-actions {
margin-top: 1.5em;
display: flex;
justify-content: center;
gap: 5px;
.dev-help-action-button {
display: inline-block;
font-weight: 400;
color: hsl(170deg 41% 52%);
border: 1px solid hsl(170deg 41% 52%);
border-radius: 4px;
padding: 6px 12px;
transition:
color 0.3s ease,
border-color 0.3s ease;
&:hover {
color: hsl(156deg 62% 61%);
border-color: hsl(156deg 62% 61%);
}
}
}
}

View File

@@ -0,0 +1,134 @@
#dev-email-log-container {
.dev-email-log-header {
position: fixed;
font-family: sans-serif;
font-size: 14px;
}
.dev-email-log-header,
.dev-forward-email-alert {
padding: 10px;
border: 1px solid hsl(190deg 65% 84%);
border-radius: 4px;
background-color: hsl(200deg 65% 91%);
color: hsl(200deg 50% 45%);
}
.dev-email-log-actions,
.dev-forward-email-alert {
& a {
color: hsl(200deg 50% 45%);
&:hover {
color: hsl(200deg 56% 33%);
}
}
}
.dev-email-log-actions {
padding-bottom: 5px;
}
.dev-email-log-text-only {
text-align: right;
}
.dev-email-log-content {
padding-top: 100px;
max-width: 1000px;
& h4 {
color: hsl(0deg 0% 20%);
margin: 2px 10px;
}
.email-text,
.email-html {
margin: 10px;
}
.email-text pre {
font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
color: hsl(0deg 0% 20%);
display: block;
padding: 9.5px;
margin: 0 0 10px;
font-size: 13px;
line-height: 20px;
word-break: break-all;
overflow-wrap: break-word;
white-space: pre-wrap;
background-color: hsl(0deg 0% 96%);
border: 1px solid hsl(0deg 0% 75%);
border-radius: 4px;
}
}
#smtp_form_status,
.hide {
display: none;
}
#forward_email_modal {
.modal__content label {
font-size: 1rem;
display: block;
cursor: pointer;
}
#forward_address_sections,
.dev-forward-email-alert {
margin: 5px 0;
}
& label.radio {
padding-left: 20px;
}
& input[type="radio"] {
float: left;
width: auto;
cursor: pointer;
margin: -4px 0 0 -20px;
line-height: normal;
&:focus {
outline: 1px dotted hsl(0deg 0% 20%);
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
}
}
& input[type="text"] {
padding: 4px 6px;
color: hsl(0deg 0% 33%);
border-radius: 4px;
border: 1px solid hsl(0deg 0% 80%);
box-shadow: inset 0 1px 1px hsl(0deg 0% 0% / 7.5%);
transition:
border-color linear 0.2s,
box-shadow linear 0.2s;
margin-bottom: 10px;
width: 206px;
&: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%);
}
}
}
& input[type="checkbox"] {
width: auto;
cursor: pointer;
&:focus {
outline: 1px dotted hsl(0deg 0% 20%);
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
}
}
}

View File

@@ -0,0 +1,228 @@
:root {
--color-footer-background: hsl(238deg 28% 27%);
--color-links: hsl(238deg 100% 82%);
@media (prefers-color-scheme: dark) {
--color-footer-background: hsl(238deg 28% 21%);
}
}
/* These need to be here so that other pages don't need to import a different library for these icons to work */
.footer-social-icon-mastodon {
--footer-social-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='28' height='28' fill='none' viewBox='0 0 28 28'%3e%3cg fill='%23A3A5F8'%3e%3cpath d='M18.42 16.43h2.38v-6.02c0-1.23-.3-2.2-.94-2.93a3.27 3.27 0 0 0-2.55-1.1c-1.22 0-2.15.47-2.76 1.41l-.6 1-.58-1a3.07 3.07 0 0 0-2.76-1.4c-1.05 0-1.9.37-2.55 1.1a4.3 4.3 0 0 0-.94 2.92v6.02H9.5v-5.84c0-1.23.52-1.86 1.55-1.86 1.15 0 1.72.74 1.72 2.2v3.2h2.38v-3.2c0-1.46.57-2.2 1.71-2.2 1.04 0 1.56.63 1.56 1.86v5.84Z'/%3e%3cpath fill-rule='evenodd' d='M3 0a3 3 0 0 0-3 3v22a3 3 0 0 0 3 3h22a3 3 0 0 0 3-3V3a3 3 0 0 0-3-3H3Zm18.39 3.6s3.26 1.45 3.26 6.43c0 0 .04 3.66-.46 6.2-.31 1.63-2.81 3.4-5.69 3.74-1.5.18-2.97.34-4.54.27-2.57-.12-4.6-.62-4.6-.62 0 .24.02.48.05.72.33 2.53 2.51 2.68 4.58 2.76 2.09.07 3.94-.52 3.94-.52l.09 1.89s-1.46.78-4.06.92c-1.43.08-3.21-.03-5.28-.58-4.5-1.19-5.27-5.98-5.39-10.84a100.6 100.6 0 0 1-.02-3v-.94c0-4.97 3.27-6.43 3.27-6.43 1.64-.76 4.46-1.07 7.39-1.1H14c2.93.03 5.75.34 7.39 1.1Z' clip-rule='evenodd'/%3e%3c/g%3e%3c/svg%3e");
}
.footer-social-icon-bluesky {
--footer-social-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='28' height='28' viewBox='0 0 568 501' fill='none'%3e%3cpath fill='%23A3A5F8' d='M123.121 33.664C188.241 82.553 257.473 181.247 284 234.247c26.527-53 95.759-151.694 160.879-200.583C491.866-1.595 568-26.713 568 60.142c0 17.34-9.945 145.713-15.778 166.555-20.275 72.453-94.155 90.933-159.875 79.706C507.222 326.108 536.444 388.731 473.333 451.355c-119.86 118.875-172.205-29.838-185.624-67.928-2.46-6.984-3.61-10.252-3.61-7.474 0-2.778-1.149.49-3.609 7.474-13.42 38.09-65.764 186.803-185.624 67.928-63.111-62.624-33.89-125.247 64.985-145.345-65.72 11.227-139.6-7.253-159.875-79.706C9.945 205.855 0 77.482 0 60.142 0-26.713 76.134-1.595 123.121 33.664Z'/%3e%3c/svg%3e");
}
.footer-social-icon-linkedin {
--footer-social-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='28' height='28' fill='none' viewBox='0 0 28 28'%3e%3cg clip-path='url(%23a)'%3e%3cpath fill='%23A3A5F8' d='M28 3a3 3 0 0 0-3-3H3a3 3 0 0 0-3 3v22a3 3 0 0 0 3 3h22a3 3 0 0 0 3-3V3ZM8.27 23.9H4.19V10.5h4.18v13.4h-.1ZM6.17 8.7A2.42 2.42 0 0 1 3.8 6.3c0-1.3 1.1-2.4 2.39-2.4 1.3 0 2.39 1.1 2.39 2.4 0 1.3-1 2.4-2.4 2.4Zm17.65 15.2h-4.09v-6.5c0-1.5 0-3.5-2.2-3.5-2.18 0-2.48 1.7-2.48 3.4v6.6h-4.09V10.5h3.99v1.8h.1c.6-1 1.89-2.2 3.88-2.2 4.19 0 4.98 2.8 4.98 6.4v7.4h-.1Z'/%3e%3c/g%3e%3cdefs%3e%3cclipPath id='a'%3e%3cpath fill='white' d='M0 0h28v28H0z'/%3e%3c/clipPath%3e%3c/defs%3e%3c/svg%3e");
}
#footer {
background: var(--color-footer-background);
box-sizing: border-box;
& ul {
/* Override bootstrap defaults */
list-style: none;
margin: 0;
}
.footer__container {
max-width: 1132px;
padding: 52px 52px 0;
display: flex;
justify-content: space-between;
gap: 40px;
flex-flow: row wrap;
margin: 0 auto;
}
.footer__section {
flex-shrink: 0;
}
.footer__section-title {
display: block;
font-style: normal;
font-weight: 700;
font-size: 18px;
line-height: 133%;
letter-spacing: 0.1em;
color: hsl(0deg 0% 100%);
opacity: 0.8;
text-transform: uppercase;
border-bottom: 0;
margin-bottom: 0;
margin-block: 0;
}
.footer__section ul {
margin: 20px 0 28px;
padding-left: 0;
display: flex;
flex-direction: column;
align-items: flex-start;
& li {
margin-bottom: 10px;
}
}
& li {
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 20px;
color: var(--color-links);
border-bottom: 1px solid var(--color-footer-background);
}
& a,
a:visited {
font-weight: 400;
font-size: 16px;
color: var(--color-links);
text-decoration: none;
}
& a:hover,
a:focus {
color: var(--color-links);
border-bottom: 1px solid var(--color-links);
transition: none;
text-decoration: none;
outline: none;
}
.footer__legal {
margin: 12px 0 0;
padding: 0 52px;
border-top: 1px solid hsl(0deg 0% 100% / 10%);
& a {
border-bottom: 1px solid var(--color-footer-background);
&:hover {
border-bottom: 1px solid var(--color-links);
}
}
&.footer__legal_not_corporate {
margin-top: 0;
}
}
.footer__legal-container {
max-width: 1132px;
padding: 15px 0;
display: flex;
place-content: center space-between;
flex-flow: row wrap;
margin: 0 auto;
box-sizing: border-box;
font-style: normal;
font-weight: 400;
font-size: 14px;
line-height: 18px;
}
.footer__legal-spacer {
flex-grow: 1;
}
.footer__legal-container .copyright {
color: hsl(0deg 0% 100% / 50%);
@media screen and (width <= 600px) {
/* Maintain space between copyright
and legal links when flex-wrapping
at narrower viewports. */
margin-bottom: 8px;
}
}
.footer__legal-container a {
font-size: 14px;
line-height: 18px;
}
.footer__legal-container a:not(:last-child) {
margin-right: 2em;
}
.footer__section .extra_margin {
margin-bottom: 40px;
}
.footer-social-links {
margin: -25px 0 20px;
display: flex;
gap: 8px;
}
.footer-social-icon {
width: 28px;
height: 28px;
display: inline-block;
flex-shrink: 0;
background-color: hsl(238.59deg 85.86% 80.59%);
mask-position: center;
mask-repeat: no-repeat;
mask-image: var(--footer-social-icon);
transition: background-color 150ms ease-out;
&:hover {
background-color: hsl(238.6deg 84.31% 90%);
}
&:active {
background-color: hsl(240deg 86.67% 97.06%);
}
}
/* #footer responsivity and global fixes */
@media (width <= 1280px) {
.footer__container {
justify-content: flex-start;
row-gap: 0;
}
.footer__legal-container {
justify-content: flex-end;
}
}
@media (width <= 600px) {
.footer__legal {
padding: 0 10px;
}
.footer__legal-spacer {
width: 100%;
}
.footer__legal-container {
column-gap: 20px;
justify-content: center;
& a:not(:last-child) {
margin-right: 0;
}
}
}
@media (width <= 400px) {
.footer__container {
gap: 0;
flex-direction: column;
}
.footer__section .extra_margin {
margin-bottom: 36px;
}
}
}

1127
web/styles/portico/hello.css Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,911 @@
/* Override `width: 100vw` from marketing_page.css, which makes
html wider than the visible content area when a classic scrollbar
is present — the CSS `vw` unit always includes the scrollbar's
width. That shifts every centered element rightward by half the
scrollbar width, producing asymmetric side margins on the gradient.
`scrollbar-gutter: stable` reserves gutter space even when no
scrollbar is shown, preventing layout jiggle when switching between
categories whose heights differ enough to add or remove the
vertical scrollbar. */
html:has(.portico-landing.integrations) {
width: auto;
scrollbar-gutter: stable;
}
.portico-landing.integrations {
--color-integrations-category-text: hsl(223deg 6% 35%);
/* Card-style border used for `.integration-lozenge` tiles on the
catalog and `.eligible_realm` rows on /communities. */
--color-integrations-card-border: hsl(0deg 0% 88%);
--color-integrations-card-border-hover: hsl(0deg 0% 73%);
color: hsl(0deg 0% 27%);
font-weight: normal;
/* Extra gradient space below the main card before the footer.
This is padding-bottom on .portico-landing (not margin-bottom
on .main), so the blue gradient background fills the space
instead of margin-collapsing through to an invisible external
gap. */
padding-bottom: 60px;
/* Modern blue gradient background, matching /plans and the homepage.
This replaces the complex multi-layer gradient system from
zerver/gradients.html (which is no longer included on these pages). */
background: linear-gradient(
180deg,
var(--color-background-gradient-start) 0%,
var(--color-background-gradient-end) 100%
);
& h1,
h2,
h3 {
&:hover::after {
content: none;
}
}
.markdown {
font-size: 1.1rem;
}
.main {
width: calc(100% - 100px);
max-width: 1170px;
margin: 0 auto;
border-radius: 10px;
background-color: hsl(0deg 0% 100%);
/* The catalog page gets a gray content area so the white
integration cards stand out. Specific integration doc pages
keep the default white content background. */
&:has(.catalog) {
background-color: hsl(0deg 0% 96%);
}
& img {
/* Replace the Bootstrap image reset that the new
integrations_bundle no longer provides. Without these,
images embedded in integration instructions render at
their natural size and overflow the content column. */
max-width: 100%;
height: auto;
vertical-align: middle;
box-shadow: none;
border: none;
}
/* Below 1000px the sidebar is hidden, so narrow the main's
own side margins at the same breakpoint. This keeps the
"blue gradient strip" on each side of the main card
consistently sized across the two primary layouts. */
@media (width <= 1000px) {
width: calc(100% - 50px);
}
@media (width <= 450px) {
width: 100%;
}
}
.padded-content {
/* `.integration-request` already supplies 20px of bottom
padding, so we only need a small bottom gap here to give
the buttons breathing room inside the gray card. */
padding: 50px 0 20px;
.inner-content {
min-height: 870px;
}
@media (width <= 450px) {
padding: 40px 0 20px;
}
/* On doc pages at narrow widths the header is a compact
logo + name + back row, so it needs less top breathing
room than the catalog page's tall heading block. */
&:has(#integration-instructions-group) {
@media (width <= 1000px) {
padding-top: 25px;
}
}
}
& ol li {
list-style: none;
& ul li {
list-style: inherit;
}
}
/* Left-align the header block with the cards column, so the title,
subtitle, and search box don't spill into the sidebar area. The
sidebar column below takes 230 px (200 sidebar + 30 gutter),
and the first visible `.integration-lozenge` sits another 5 px
further right due to its own horizontal margin, so we pad the
header block to start at 265 px from the main card's left edge
to land on the card's visible left edge. At narrow widths, the
sidebar is hidden and the header reverts to its original
near-full-width layout. */
.integration-main-text {
padding: 0 30px 0 265px;
@media (width <= 1000px) {
padding: 0 30px;
}
}
.portico-page-heading {
font-size: 2.5em;
font-weight: 300;
line-height: 1.2;
text-align: center;
/* When the heading wraps to multiple lines at narrow widths,
balance the line widths so the top line doesn't end up much
longer than the bottom (which makes a centered title look
visually off-center). */
text-wrap: balance;
/* At widths where the sidebar is visible, left-align the
title so its left edge matches the left edge of the cards
column below. */
@media (width > 1000px) {
text-align: left;
}
}
.portico-page-subheading {
font-weight: 400;
font-size: 1.1em;
color: hsl(0deg 0% 40%);
line-height: 1.2;
text-align: center;
@media (width > 1000px) {
text-align: left;
}
}
/* Markdown-style blue links used in the page subheading and in
content areas that render user-facing text. */
.portico-page-subheading a,
.integration-instructions a,
.eligible_realm_end_notice a {
color: var(--color-link);
text-decoration: underline;
text-decoration-color: var(--color-link-underline);
text-underline-offset: 0.1176em;
&:hover {
color: var(--color-link-hover);
text-decoration-color: var(--color-link-underline-hover);
}
}
#integration-search {
margin-bottom: 30px;
padding: 0 30px 0 265px;
@media (width <= 1000px) {
padding: 0 30px;
margin-bottom: 10px;
}
.searchbar {
display: flex;
justify-content: center;
margin: 30px auto 0;
/* At widths where the sidebar is visible, left-align the
search box so its left edge matches the left edge of
the cards column below. */
@media (width > 1000px) {
justify-content: flex-start;
}
.searchbar-reset {
position: relative;
width: 100%;
max-width: 500px;
.search_input {
/* border-box so width:100% includes the 70px of
horizontal padding; otherwise the search box
overflows its 500px cap by padding width. */
box-sizing: border-box;
font-size: 1em;
font-family: inherit;
width: 100%;
height: 45px;
padding: 0 20px 0 50px;
border-radius: 5px;
border: 1px solid hsl(0deg 0% 80%);
display: block;
color: hsl(0deg 0% 33%);
margin-bottom: 10px;
transition: border-color linear 0.2s;
&:focus {
border-color: var(--color-link);
outline: 0;
}
}
& .zulip-icon-search {
position: absolute;
left: 18px;
top: 13px;
font-size: 18px;
color: hsl(0deg 0% 53%);
pointer-events: none;
}
}
}
}
.catalog {
/* `flow-root` makes .catalog a new block formatting context
that contains the floated sidebar, so when a category has
few cards the sidebar doesn't overflow past .catalog's
bottom into the page background below. */
display: flow-root;
margin-top: 20px;
padding: 0 30px;
min-height: 500px;
.integration-categories-sidebar {
float: left;
width: 200px;
padding: 0 30px 0 0;
margin: 0;
&.sticky {
position: sticky;
top: 40px;
margin: auto;
height: 490px;
z-index: 10;
}
& h3 {
font-weight: 700;
font-size: 1.1em;
margin: 0 0 5px;
color: var(--color-integrations-category-text);
}
/* Visually separate "Custom integrations" (the second h3)
from the last category pill above it. */
& h3 ~ h3 {
margin-top: 20px;
}
& a {
display: block;
text-decoration: none;
font-weight: 400;
font-size: 0.95em;
padding: 6px 10px;
margin: 2px 0;
border-radius: 5px;
color: var(--color-integrations-category-text);
cursor: pointer;
&:hover {
color: hsl(223deg 6% 15%);
}
&.selected {
background-color: hsl(0deg 0% 100%);
color: hsl(223deg 6% 25%);
/* !important is needed to override the
`.portico-landing a:not(.button) {
font-weight: inherit !important }` rule
in marketing_page.css. */
font-weight: 600 !important;
}
}
@media (width <= 1000px) {
display: none;
}
}
}
.integration-categories-dropdown {
display: none;
/* Match the search box at narrow widths: the outer container
takes 30px horizontal padding, and the inner elements
(toggle + list) cap at 500px and center within the content
area — mirroring the `#integration-search { padding: 0 30px }`
+ `.searchbar-reset { max-width: 500px }` structure. */
@media (width <= 1000px) {
display: block;
margin: 0;
padding: 0 30px;
& h3 {
font-weight: 400;
font-size: 1.1em;
text-align: left;
margin: 0;
padding: 12px 20px;
&.heading {
font-weight: 600;
}
}
.integration-toggle-categories-dropdown,
.dropdown-list {
/* border-box so the 1px border is included in the
500px cap, matching the search input's dimensions
exactly. */
box-sizing: border-box;
width: 100%;
max-width: 500px;
margin-left: auto;
margin-right: auto;
}
.integration-toggle-categories-dropdown {
cursor: pointer;
display: flex;
position: relative;
padding: 0;
align-items: center;
justify-content: space-between;
/* White background so the toggle button stands out
from the gray catalog content area, matching the
white-highlight selected pill in the wide-window
sidebar. */
background-color: hsl(0deg 0% 100%);
border: 1px solid hsl(0deg 0% 80%);
/* Match the rounded corners of the search input. */
border-radius: 5px;
}
& i {
margin-right: 10px;
font-size: 24px;
}
/* The outer box is on the list container, so the items
inside have no borders and no dividers between them.
`border-top: 0` lets the list visually continue the
toggle's bottom edge without a double line. Only the
bottom corners are rounded; `overflow: hidden` clips
the selected item's white background so it doesn't
extend past the rounded border at the bottom. */
.dropdown-list {
display: none;
cursor: pointer;
padding: 0;
border: 1px solid hsl(0deg 0% 80%);
border-top: 0;
border-radius: 0 0 5px 5px;
overflow: hidden;
& a {
display: block;
text-decoration: none;
font-weight: 400;
font-size: 1em;
padding: 12px 20px;
color: var(--color-integrations-category-text);
&:hover {
color: hsl(223deg 6% 15%);
}
&.selected {
background-color: hsl(0deg 0% 100%);
color: hsl(223deg 6% 25%);
font-weight: 600 !important;
}
}
}
}
/* While the dropdown list is open, square the toggle's
bottom corners so they meet the list's straight top edge
cleanly instead of showing a rounded-to-straight seam.
jQuery's `slideToggle` sets `style="display: block"` inline
on the list when open, which we detect via :has(). */
&:has(.dropdown-list[style*="display: block"])
.integration-toggle-categories-dropdown {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
}
.integration-lozenges {
overflow: hidden;
text-align: left;
@media (width <= 1000px) {
text-align: center;
}
/* The lozenge cards are wrapped in bare <a> tags, which
inherit the browser-default underline. Suppress it so
the underline doesn't peek through the whitespace gaps
between inline-block cards as visible dashes. */
& > a {
text-decoration: none;
}
}
.integration-lozenge {
vertical-align: top;
width: 153px;
height: 200px;
padding: 0 4px;
margin: 7px 5px;
border-radius: 5px;
border: 1px solid var(--color-integrations-card-border);
background-color: hsl(0deg 0% 100%);
color: var(--color-integrations-category-text);
text-align: center;
transition: 0.3s ease;
transition-property: border-color;
&:not([hidden]) {
display: inline-block;
}
&:hover {
border-color: var(--color-integrations-card-border-hover);
}
.integration-secondary-line-text {
margin: 0;
line-height: 10px;
font-size: 0.65em;
font-weight: 400;
}
.integration-category,
.realm-category {
font-size: 0.85em;
margin-top: 5px;
font-weight: 400;
color: hsl(216deg 23% 13% / 50%);
}
&.without-category {
height: 180px;
}
.integration-logo {
margin: 34px auto 20px;
height: 60px;
}
.fa-plus {
font-size: 59px;
}
.integration-name {
font-size: 1.2em;
font-weight: 400;
margin: 10px 4px 0;
&.with-secondary {
font-size: 1.1em;
margin-top: 4px;
}
}
@media (width <= 830px) {
width: 134px;
height: 180px;
.integration-logo {
margin: 34px auto 15px;
height: 45px;
}
.fa-plus {
font-size: 45px;
}
.integration-name {
font-size: 1em;
}
.integration-category,
.realm-category {
font-size: 0.8em;
}
}
@media (width <= 375px) {
width: 120px;
}
@media (width <= 357px) {
width: 108px;
height: 160px;
&.without-category {
height: 140px;
}
.integration-logo {
margin: 28px auto 10px;
height: 45px;
}
.integration-name {
font-size: 0.9em;
}
.integration-category,
.realm-category {
font-size: 0.77em;
}
}
}
.integration-request {
font-size: 1em;
/* Generous top/bottom padding so the "Don't see an integration
you need?" section has visible breathing room between the
last row of cards above and the section content below. */
padding: 40px 0 20px;
/* At widths where the header is left-aligned, left-align
the bottom section to match. The 5px left padding lines
up the text with the visible card edges, since each
`.integration-lozenge` has a 5px horizontal margin. */
@media (width > 1000px) {
text-align: left;
padding-left: 5px;
}
& p {
margin: 0 0 20px;
}
/* Lay out the /why-zulip-style hero-buttons as a flex row,
since the flex rule in marketing_page.css is scoped to
.why-page and its siblings. Centered at narrow widths,
left-aligned at wide widths to match the header. */
.hero-buttons {
display: flex;
flex-flow: row wrap;
justify-content: center;
@media (width > 1000px) {
justify-content: flex-start;
/* Each .button has 10px margin from marketing_page.css,
which pushes its visible border right of the text
above. Pull the flex container left to compensate. */
margin-left: -10px;
}
}
}
/* -- integration instructions -- */
#integration-instructions-group {
padding: 0 50px;
display: flex;
.integration-instruction-block {
display: flex;
flex-direction: column;
/* Let the left column size to its widest child (the logo
box, or a long category pill) plus a right-padding
gutter. The instructions column uses `flex: 1` to fill
the remaining space. */
flex-shrink: 0;
padding-right: 30px;
.integration-lozenge {
width: 125px;
height: auto;
padding: 0 5px 20px;
/* Align the top of the logo card with the visible
cap-height of the title's first character. This
`margin-top` is tuned so that the card's top edge
sits on the same horizontal line as the top of
the "Z" (or equivalent cap-height glyph) in the
h1 title, not on its typographic ascender line.
The value balances the 15px top margin the shared
markdown rules give h1 plus the half-leading and
ascender offset that sit above the visible glyph
in Source Sans 3 VF at the h1's font-size. */
margin: 25px 21px 20px;
order: 1;
.integration-category,
.realm-category {
display: none;
}
@media (width <= 1000px) {
border: none;
display: flex !important;
align-items: center;
justify-content: space-between;
height: auto;
width: auto;
margin: unset;
padding: unset;
.integration-logo {
/* Override the shared catalog-card rule at
`@media (width <= 830px)` that shrinks logos
to 45px — the doc-page header should keep the
full-size logo at every width. */
height: 60px;
margin: 0;
}
.integration-name {
font-size: 1.2em !important;
margin: 0 0 0 20px;
}
}
}
.name {
display: none;
font-size: 1.4em;
font-weight: 400;
margin: 0;
padding-bottom: 35px;
}
.categories {
order: 2;
/* Left-align the pills with the logo box above, which
has `margin: 0 21px 20px` on `.integration-lozenge`. */
margin-left: 21px;
/* Each category is its own <a> wrapping an <h3>. Lay
them out as a vertical flex column so the pills are
sized to their content (like in-app pills) instead
of stretching to a fixed width. */
display: flex;
flex-direction: column;
align-items: flex-start;
& a {
text-decoration: none;
}
.integration-category,
.realm-category {
font-size: 0.95em;
font-weight: 400;
padding: 5px 14px;
margin: 0 0 6px;
border-radius: 14px;
background-color: hsl(210deg 94% 42% / 10%);
color: hsl(210deg 70% 35%);
&:hover {
background-color: hsl(210deg 94% 42% / 17%);
color: hsl(210deg 70% 28%);
}
}
}
#integration-list-link {
margin-left: 30px;
order: 3;
color: var(--color-integrations-category-text);
text-decoration: none;
display: flex;
align-items: center;
.zulip-icon-arrow-left {
font-size: 16px;
margin-right: 5px;
}
/* Show "Back to list" on wide screens, "Back" on
narrow where space is limited. */
.integrations-back-label-short {
display: none;
}
@media (width <= 1000px) {
.integrations-back-label-long {
display: none;
}
.integrations-back-label-short {
display: inline;
}
}
&:hover {
color: hsl(223deg 6% 15%);
}
@media (width >= 1000px) {
margin-top: 15px;
}
}
@media (width <= 1000px) {
flex-direction: row;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
/* Drop the column-gutter padding on the right so
the back-link reaches the card's right edge
instead of sitting 30 px inside it. */
padding: 0 0 12px;
border-bottom: 1px solid hsl(0deg 0% 88%);
}
@media (width <= 1000px) {
.name {
display: block;
}
.categories {
display: none;
}
}
}
.integration-instructions {
.logos_disclaimer {
font-size: 14px;
font-style: italic;
}
@media (width >= 1000px) {
/* Fill whatever space the left column leaves after
sizing to its widest child. min-width: 0 lets the
column shrink below its intrinsic content size when
the viewport is narrow. */
flex: 1;
min-width: 0;
}
@media (width <= 1000px) {
margin-left: 0;
& h1:first-child {
margin-top: 0;
}
}
}
@media (width <= 1000px) {
flex-direction: column;
}
@media (width <= 768px) {
padding: 0 25px;
}
@media (width <= 450px) {
padding: 0 12px;
}
}
}
.portico-landing.integrations.communities {
.main {
max-width: 900px;
}
/* `.eligible_realm` has no horizontal margin, unlike
`.integration-lozenge` which has a 5 px left/right margin, so
the communities heading needs 5 px less left padding than the
integrations heading to line up with the first realm's visible
left edge. */
.integration-main-text {
padding-left: 260px;
@media (width <= 1000px) {
padding-left: 30px;
}
}
.portico-page-heading {
color: hsl(0deg 0% 0%);
font-size: 44px;
}
.portico-page-subheading {
font-weight: 300;
color: hsl(0deg 0% 20%);
max-width: 800px;
line-height: 150%;
margin: auto;
}
.integration-categories-sidebar h3 {
color: hsl(0deg 0% 0%);
font-size: 20px;
font-weight: 600;
}
.catalog {
margin-top: 50px;
}
.eligible_realms {
display: flex;
flex-direction: column;
.eligible_realm {
display: flex;
margin-bottom: 12px;
padding: 15px 20px;
border: 1px solid var(--color-integrations-card-border);
border-radius: 6px;
transition: border-color 0.3s ease;
background-color: hsl(0deg 0% 100%);
/* The card itself is an <a>, which would otherwise inherit
the underline from `.portico-landing a:not(.button)` in
marketing_page.css and visibly underline the name and
description text inside it. */
text-decoration: none;
&:hover {
border-color: var(--color-integrations-card-border-hover);
}
.eligible_realm_logo {
display: flex;
margin-right: 20px;
width: 60px;
height: 60px;
}
.eligible_realm_details {
display: flex;
flex-direction: column;
justify-content: center;
.eligible_realm_name {
font-weight: 400;
font-size: 21px;
color: hsl(220.6deg 20% 33.3%);
line-height: 23px;
margin: 0 0 2px;
}
.eligible_realm_description {
font-weight: 400;
font-size: 15px;
color: hsl(220deg 2.7% 56.5%);
line-height: 19px;
margin: 0;
/* For restricting text to only two lines.
See https://caniuse.com/?search=display%3A%20-webkit-box for support. */
overflow: hidden;
display: -webkit-box; /* stylelint-disable-line value-no-vendor-prefix */
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
}
}
& hr {
margin: 10px 0;
border: 0;
border-top: 1px solid hsl(0deg 0% 93%);
}
.eligible_realm_end_notice {
text-align: center;
font-size: 18px;
}
}
}

View File

@@ -0,0 +1,183 @@
#custom_http_headers {
width: 100%;
height: 60px;
resize: vertical;
}
#dev-panel {
margin-left: 3%;
margin-right: 3%;
padding: 20px 10px 15px;
background-color: hsl(0deg 0% 100% / 48%);
box-shadow: 0 0 40px hsl(0deg 0% 0% / 6%);
& textarea {
color: hsl(0deg 0% 33%);
background-color: hsl(0deg 0% 100%);
border-radius: 4px;
vertical-align: middle;
border: 1px solid hsl(0deg 0% 80%);
padding: 4px 6px;
margin-bottom: 10px;
font-size: 14px;
box-shadow: inset 0 1px 1px hsl(0deg 0% 0% / 7.5%);
transition:
border-color linear 0.2s,
box-shadow linear 0.2s;
&:focus {
border-color: hsl(206.5deg 80% 62% / 80%);
outline: 0;
box-shadow:
inset 0 1px 1px hsl(0deg 0% 0% / 7.5%),
0 0 8px hsl(206.5deg 80% 62% / 60%);
}
&:read-only {
cursor: not-allowed;
}
}
& select,
textarea,
input {
font-family: inherit;
}
& select {
height: 30px;
padding: 0 6px;
width: 220px;
font-size: 14px;
color: hsl(0deg 0% 33%);
border-radius: 4px;
border: 1px solid hsl(0deg 0% 80%);
margin-bottom: 10px;
cursor: pointer;
background-color: hsl(0deg 0% 100%);
vertical-align: middle;
}
& input {
padding: 4px 6px;
color: hsl(0deg 0% 33%);
border-radius: 4px;
border: 1px solid hsl(0deg 0% 80%);
box-shadow: inset 0 1px 1px hsl(0deg 0% 0% / 7.5%);
transition:
border-color linear 0.2s,
box-shadow linear 0.2s;
margin-bottom: 10px;
&: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%);
}
}
}
#fixture_body {
width: 90%;
height: 500px;
resize: vertical;
}
#idp-results {
width: 100%;
height: 500px;
resize: vertical;
background-color: hsl(0deg 0% 100%);
}
.top-navbar {
display: grid;
grid-template-columns: 1fr 5fr 1fr;
}
#URL {
width: 100%;
}
#stream_name,
#topic_name {
width: 206px;
}
.buttons {
display: flex;
justify-content: space-between;
width: 92%;
margin-right: 2%;
button {
padding: 6px 12px;
color: hsl(0deg 0% 100%);
background-color: hsl(208deg 56% 53%);
border: 1px solid hsl(208deg 56% 47%);
&:hover,
&:active,
&:focus {
background-color: hsl(208deg 56% 45%);
border-color: hsl(208deg 56% 36%);
}
}
}
.centerpiece {
font-size: 30px;
text-align: center;
}
.inline {
display: flex;
flex-wrap: nowrap;
justify-content: space-between;
margin-right: 10%;
}
.optional {
color: hsl(0deg 0% 50%);
}
.pad-small {
height: 100px;
width: 100%;
}
.row1 {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
margin-left: 2%;
margin-right: 2%;
}
.row2 {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
margin-left: 2%;
margin-right: 2%;
}
.row3 {
display: flex;
flex-flow: column wrap;
justify-content: flex-start;
margin-left: 2%;
margin-right: 2%;
}
.row4 {
display: grid;
grid-template-columns: 1fr 1fr;
margin-left: 2%;
margin-right: 2%;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
@import url("../components.css");
@import url("portico_variables.css");
@import url("legacy_portico.css");
@import url("portico_modals.css");
@import url("portico_signin.css");
@import url("markdown.css");
@import url("navbar.css");
@import url("footer.css");

View File

@@ -0,0 +1,596 @@
.markdown {
/* Copied from app_variables.css to be used in portico pages. */
--color-copy-button: color-mix(
in oklch,
hsl(229deg 9% 36%) 70%,
transparent
);
--color-copy-button-hover: hsl(229deg 9% 36%);
--color-copy-button-active: color-mix(
in oklch,
hsl(229deg 9% 36%) 70%,
transparent
);
--color-copy-button-success: hsl(146deg 90% 27%);
/* User circles */
/* stylelint-disable color-no-hex */
--color-user-circle-active: light-dark(#43a35e, #4cdc75);
--color-user-circle-idle: light-dark(#f5b266, #ae640a);
--color-user-circle-offline: light-dark(#c1c6d7, #454854);
--color-user-circle-deactivated: hsl(0deg 0% 50%);
--gradient-user-circle-idle: linear-gradient(
to right,
var(--color-user-circle-idle) 5%,
light-dark(
color-mix(
in srgb,
var(--color-user-circle-idle) 78.9%,
transparent
),
var(--color-user-circle-idle)
)
25%,
transparent 100%
);
/* stylelint-enable color-no-hex */
font-weight: 400;
font-size: 1rem;
line-height: 1.5;
& h1[id],
h2[id],
h3[id],
h4[id] {
&::before {
display: block;
content: " ";
visibility: hidden;
}
}
& h2[id],
h3[id],
h4[id] {
&::before {
margin-top: -10px;
height: 10px;
}
}
& h1 {
border-bottom: 1px solid hsl(0deg 0% 93%);
padding-bottom: 10px;
margin-bottom: 15px;
&[id] {
&::before {
margin-top: -30px;
height: 30px;
}
}
&#zulip-administration {
font-size: 1.75em;
padding: 10px 0;
margin-bottom: 0;
line-height: 100%;
}
}
& h2 {
font-size: 1.5em;
line-height: 1.25;
margin: 20px 0 5px;
}
& h3 {
font-size: 1.25em;
line-height: 1.25;
opacity: 1;
margin: 20px 0 5px;
}
& h1,
h2,
h3 {
font-weight: 700;
user-select: none;
&:hover {
cursor: pointer;
&::after {
display: inline-block;
font: normal normal normal 16px/1 FontAwesome;
cursor: pointer;
content: "\f0c1";
margin-left: 5px;
vertical-align: middle;
}
}
}
& h5,
h6 {
margin: 10px 0;
line-height: 20px;
}
/* Since markdown doesn't make it easy to put an HTML element around a
markdown table, we instead have a model of putting an empty div
before it to configure a specific table's styling. */
& div.centered_table + table td:not(:first-child),
div.centered_table + table th {
text-align: center;
}
.legend_symbol {
position: absolute;
left: calc(340px);
transform: translateX(-50%);
/* Adjust for 50px closed left sidebar state */
@media (width <= 800px) {
left: calc(5% + 50px);
}
}
.legend_label {
position: relative;
left: calc(30px);
}
& li {
line-height: 150%;
}
& ol {
counter-reset: item;
list-style: none;
& > li {
position: relative;
vertical-align: top;
/* This needs to be wide enough for 2-digit numbers. */
padding-left: 33px;
top: -2px;
counter-increment: item;
&::before {
position: absolute;
top: 0;
left: 0;
content: counter(item);
display: inline-block;
vertical-align: top;
padding: 3px 6.5px 3px 7.5px;
margin-right: 5px;
background-color: var(--color-link);
color: hsl(0deg 0% 100%);
border-radius: 100%;
font-size: 0.9em;
line-height: 1.1;
text-align: center;
}
.codehilite {
background-color: hsl(0deg 0% 100%);
& pre {
white-space: pre;
overflow-x: auto;
}
}
& > ul {
margin-bottom: 5px;
}
}
@media (width <= 500px) {
margin-left: 0;
}
}
& ul {
margin: 0 10px 15px 25px;
/* Avoid extra whitespace after nested bulleted lists. */
& ul {
margin: 0 20px;
}
& > li {
margin: 5px 0 10px;
& > p {
margin: 0 0 5px;
}
& > p:first-child {
margin: 0;
}
}
}
.content {
padding: 30px;
max-width: 700px;
background-color: hsl(0deg 0% 100%);
& ol li p:not(:first-child) {
display: block;
}
& > ol {
margin: 15px 10px;
& > li {
margin: 2.5px 0;
}
}
& i.icon-collapsed-state {
transform: rotate(270deg);
}
@media (width <= 500px) {
padding: 10px;
}
}
& i.zulip-icon {
margin: 0 2px 2px;
vertical-align: middle;
}
& i.zulip-icon.mobile-help {
color: hsl(244deg 69% 62%);
}
& a {
color: hsl(176deg 46% 41%);
font-weight: 600;
& code {
color: hsl(176deg 46% 41%);
}
}
& strong {
font-weight: 600;
}
& img {
vertical-align: top;
box-shadow: 0 0 4px hsl(0deg 0% 0% / 5%);
border: 1px solid hsl(0deg 0% 87%);
border-radius: 4px;
&.inline {
height: 1.4em;
box-shadow: none;
}
}
.warn,
.tip,
.keyboard_tip {
position: relative;
display: block;
background-color: hsl(210deg 22% 96%);
border: 1px solid hsl(210deg 22% 90%);
border-radius: 4px;
padding: 10px;
margin: 5px 0;
& p {
margin-bottom: 0;
}
& p:first-of-type {
display: inline;
}
}
.tip,
.keyboard_tip {
background-color: hsl(46deg 63% 95%);
border: 1px solid hsl(46deg 63% 84%);
}
.tip::before {
display: inline;
content: "\f0eb Tip: ";
font-family: FontAwesome, "Source Sans 3 VF", sans-serif;
font-weight: 600;
}
.keyboard_tip::before {
display: inline;
content: "\f11c Keyboard shortcut: ";
font-family: FontAwesome, "Source Sans 3 VF", sans-serif;
font-weight: 600;
}
.user-circle {
/* We make the circles slightly larger than in the
sidebar UI, so they look good next to the copy. */
font-size: 0.7em;
display: inline-block;
/* 1.5px at 11.2px/1em */
vertical-align: 0.1339em;
}
.user-circle-active {
color: var(--color-user-circle-active);
}
.user-circle-idle {
background: var(--gradient-user-circle-idle);
background-clip: text;
-webkit-text-fill-color: transparent;
}
.with_avatar .user-circle-idle {
background: var(--gradient-user-circle-idle-avatar);
}
.user-circle-offline {
color: var(--color-user-circle-offline);
}
.user-circle-deactivated {
color: var(--color-user-circle-deactivated);
}
& kbd {
/* Same as kbd in app_components.css */
display: inline-block;
border: 1px solid hsl(0deg 0% 80%);
border-radius: 4px;
font-weight: 600;
white-space: nowrap;
background-color: hsl(0deg 0% 98%);
color: hsl(0deg 0% 20%);
text-shadow: 0 1px 0 hsl(0deg 0% 100%);
/* Different from app_components.css */
/* Removed margin setting */
font-size: 0.85em;
padding: 0 0.4em;
&.arrow-key {
/* Same as in informational_overlays.css */
line-height: 1;
padding: 0 0.2em 0.2em;
/* Different from informational_overlays.css */
font-size: 1.2em;
}
}
& code {
/* Same font-family as zulip.css */
font-family: "Source Code Pro", monospace;
/* Same as base rules for code elements in rendered_markdown.css */
font-size: 0.825em;
unicode-bidi: embed;
direction: ltr;
color: hsl(0deg 0% 0%);
border-radius: 3px;
/* Different from base rules for code elements in rendered_markdown.css */
white-space: initial;
padding: 0 4px;
background-color: hsl(0deg 0% 93%);
overflow-wrap: break-word;
}
.codehilite {
/* Relative positioning is used to place the copy-to-clipboard button
in the top-right corner. */
position: relative;
}
& pre {
& code {
font-size: 14px;
white-space: pre-wrap;
padding: 0;
color: inherit;
background-color: transparent;
border: 0;
}
}
.copy-codeblock {
cursor: pointer;
/* Invisible default button */
background: none;
border: none;
padding: 0;
position: absolute;
/* Position the button in the top-right corner */
top: 10px;
right: 10px;
line-height: 3px;
&:focus {
outline-offset: -1px;
outline-color: hsl(176deg 46% 41%);
}
}
.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-success {
color: var(--color-copy-button-success);
}
}
.tabbed-section {
& ol {
margin-left: 15px;
margin-top: 10px;
}
& ul.nav {
margin: 0;
& li {
display: inline-block;
padding: 5px 14px;
margin: 0;
cursor: pointer;
&.active {
color: var(--color-link);
margin-bottom: -1px;
border: 1px solid hsl(0deg 0% 87%);
border-bottom: 1px solid hsl(0deg 0% 100%);
border-radius: 4px 4px 0 0;
}
}
}
&.no-tabs ul.nav {
display: none;
}
.blocks {
padding: 10px;
margin-bottom: 10px;
border: 1px solid hsl(0deg 0% 87%);
.tab-content {
display: none;
}
}
&.has-tabs .blocks {
border-radius: 0 6px 6px;
}
/* A "no-tabs" section is a degenerate zero-tabs wrapper that
just holds flowing markdown content. The border and radius
on `.blocks` exist to visually group a tab bar with the
content below it, which is meaningless when there's no tab
bar — it just draws a random frame around a subset of the
content. Drop the frame in that case.
Reset padding to 0 and use `margin-block` instead so that
the top/bottom margins collapse with adjacent paragraphs,
the first list item's margin, etc. — that gives a natural,
single breathing-room gap of ~20 px instead of stacking
the preceding paragraph's 10 px margin on top of the
block's own space. */
&.no-tabs .blocks {
padding: 0;
margin-block: 20px;
border: 0;
border-radius: 0;
}
&.no-tabs .blocks > .tab-content,
&.has-tabs .blocks > .active {
display: block;
}
}
& blockquote {
padding: 0 0 0 15px;
margin: 0 0 20px;
border-left: 5px solid hsl(0deg 0% 93%);
& p {
font-size: 17.5px;
font-weight: 300;
line-height: 1.25;
margin-bottom: 0;
}
}
}
/* Shared element-level styling for pages that render markdown as
content documentation: /help and /api pages (under `.help .markdown`)
and integration doc pages (under `.integration-instructions`).
Keeping these rules in one place lets both the help bundle and the
integrations bundle depend on a single source of truth — markdown.css
is already loaded by both. Marketing pages (which also load this
file but don't match either selector) are intentionally unaffected,
because their layouts rely on different defaults for lists,
paragraph margins, and so on. */
.help .markdown,
.integration-instructions {
& p {
margin: 0 0 10px;
}
& p:empty {
margin: 0;
}
& h1 {
margin-top: 15px;
}
& h4 {
margin: 15px 0 5px;
}
& hr {
margin: 20px 0;
border: 0;
border-top: 1px solid hsl(0deg 0% 93%);
}
/* Reset the browser-default left padding on lists so numbered
items and bullets align with surrounding paragraphs. */
& ul,
& ol {
padding: 0;
}
/* Fenced code blocks: light gray card with a subtle border and
rounded corners. The 15px vertical margins give surrounding
paragraphs visible breathing room around the code card, which
a tighter 10px bottom margin (and 0 top) was not providing.
The explicit font-size matches the `pre code` rule in .markdown
above and ensures code blocks render at the same size whether
the codehilite wraps content in <code> or not (some integration
pages generate bare <pre> without a <code> child). */
& pre {
padding: 9.5px;
margin: 15px 0;
font-family: "Source Code Pro", monospace;
font-size: 14px;
color: hsl(0deg 0% 0%);
word-break: break-all;
line-height: 20px;
background-color: hsl(0deg 0% 96%);
border: 1px solid hsl(0deg 0% 0% / 15%);
border-radius: 4px;
}
/* Inline `<code>` inside a link inherits the link color. */
& a code {
color: var(--color-link);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,763 @@
:root {
/* Actual navbar height is determined by label padding
(16px top/bottom) and line-height (28px): 16 + 28 + 16 = 60px.
Keep this variable in sync with those values. */
--navbar-height: 60px;
--announcement-banner-background: hsl(216deg 90% 82%);
--announcement-banner-foreground: hsl(240deg 100% 5.9%);
--announcement-banner-border: hsl(240deg 100% 5.9% / 12%);
--announcement-banner-link-underline: hsl(240deg 100% 5.9% / 80%);
}
/* Suppress rubber-band overscroll on marketing pages: the banner is
sticky and in-flow while .top-menu is fixed, so during overscroll
above scroll: 0 the banner briefly drifts down with the document
while the navbar stays pinned, exposing a gap and visually
overlapping the navbar's top edge. */
html {
overscroll-behavior-y: none;
}
/* Announcement banner above the marketing nav, shown when
LANDING_PAGE_NAVBAR_MESSAGE is configured. Lives in navbar.css
(loaded by every marketing-page bundle, including landing-page-hello)
so /hello renders it identically to /plans, /why-zulip, etc.
position: sticky keeps the banner in flow — so .top-menu's
`position: fixed; top: auto` still resolves to "just below this
banner" — while pinning the banner to the viewport top on scroll.
z-index 11 sits above .top-menu (z-index 10) so the two sit
flush during scroll without flicker. */
#navbar-custom-message {
position: sticky;
top: 0;
z-index: 11;
padding: 10px;
text-align: center;
font-size: 1.125rem; /* 18px at 16px/1rem */
font-weight: 450;
letter-spacing: 0.01em;
background: var(--announcement-banner-background);
color: var(--announcement-banner-foreground);
border-bottom: 1px solid var(--announcement-banner-border);
& a {
color: var(--announcement-banner-foreground);
text-decoration: underline;
text-decoration-color: var(--announcement-banner-link-underline);
text-decoration-thickness: 2px;
text-underline-offset: 2px;
transition: text-decoration-color 0.2s;
}
& a:hover {
text-decoration-color: var(--announcement-banner-foreground);
}
}
details summary::-webkit-details-marker {
/* Hide the default arrow on Safari. */
display: none;
}
.top-menu :focus-visible {
/* Show white outline by default */
/* This overrides outline-style: auto which
renders a double outline of white and dark blue */
outline: 1px solid hsl(0deg 0% 100%);
&.top-menu-item,
&.nav-menu-label {
outline-offset: -2px;
}
}
.nav-zulip-logo {
background-image: url("data:image/svg+xml,%3Csvg width='108' height='28' viewBox='0 0 108 28' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_555_12341234)'%3E%3Cpath d='M25.0534 4.18667C25.0534 5.59714 24.4217 6.85034 23.4589 7.6106L14.1128 15.9802C13.9392 16.1294 13.7136 15.8888 13.8388 15.688L17.267 8.80536C17.3631 8.61266 17.2385 8.37582 17.0414 8.37582H3.7444C1.68498 8.37582 0 6.49104 0 4.18791C0 1.88416 1.68498 6.14715e-07 3.7444 6.14715e-07H21.309C23.3684 -0.00124264 25.0534 1.88353 25.0534 4.18667ZM3.7444 28H21.309C23.3684 28 25.0534 26.1152 25.0534 23.8121C25.0534 21.5083 23.3684 19.6242 21.309 19.6242H8.01203C7.81489 19.6242 7.69029 19.3873 7.78638 19.1946L11.2146 12.312C11.3398 12.1112 11.1142 11.8706 10.9406 12.0198L1.59447 20.3882C0.631713 21.1478 0 22.4016 0 23.8121C0 26.1152 1.68498 28 3.7444 28ZM36.136 20.2825L43.8715 9.11804V9.02107H36.8588V5.97633H48.6431V8.10292L41.0762 19.1225V19.2195H48.764V22.2642H36.136V20.2825V20.2825ZM57.1505 5.97633V15.353C57.1505 18.1559 58.2106 19.5819 60.0909 19.5819C62.0189 19.5819 63.0789 18.2286 63.0789 15.353V5.97633H66.7421V15.1105C66.7421 20.137 64.2116 22.5291 59.97 22.5291C55.8728 22.5291 53.4631 20.2576 53.4631 15.0621V5.97633H57.1505V5.97633ZM72.3333 5.97633H76.0207V19.1704H82.4792V22.2636H72.3333V5.97633ZM90.7454 5.97633V22.2636H87.058V5.97633H90.7454ZM96.3359 6.1939C97.4686 6.00058 99.0593 5.85574 101.3 5.85574C103.566 5.85574 105.181 6.29088 106.265 7.16054C107.301 7.98233 108 9.33561 108 10.9301C108 12.5252 107.47 13.8785 106.506 14.7966C105.253 15.9808 103.397 16.5123 101.228 16.5123C100.747 16.5123 100.313 16.4881 99.9756 16.4396V22.2636H96.3366V6.1939H96.3359ZM99.975 13.5888C100.288 13.6615 100.674 13.6851 101.204 13.6851C103.156 13.6851 104.361 12.6943 104.361 11.0271C104.361 9.52894 103.324 8.63442 101.493 8.63442C100.746 8.63442 100.24 8.70715 99.975 8.77926V13.5888V13.5888Z' fill='white'%3E%3C/path%3E%3C/g%3E%3Cdefs%3E%3CclipPath id='clip0_555_12341234'%3E%3Crect width='108' height='28' fill='white'%3E%3C/rect%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E");
background-repeat: no-repeat;
width: 108px;
}
.top-menu {
position: fixed;
width: 100%;
background: hsl(240deg 100% 10% / 60%);
backdrop-filter: blur(10px);
color: hsl(0deg 0% 100%);
z-index: 10;
overflow: hidden;
transition: background-color 0.3s ease-out;
&:hover {
background: hsl(240deg 100% 10% / 85%);
}
}
.top-menu-container {
/* 1280px + 32px for paddings on the edges */
max-width: 1312px;
margin: 0 auto;
display: flex;
align-items: center;
}
.top-menu-logo {
overflow: hidden;
opacity: 0.85;
/* 14*2 = 28px = height of logo svg */
padding: 14px;
margin: 0 16px;
flex-shrink: 0;
}
.top-menu-items-group-1,
.top-menu-items-group-2 {
display: flex;
}
.top-menu-items-group-1 {
margin-right: 16px;
}
.top-menu-mobile a {
&,
&:hover,
&:focus,
&:visited {
color: hsl(0deg 0% 100% / 80%);
}
}
.top-menu-item {
display: flex;
align-items: center;
flex-shrink: 0;
font-style: normal;
font-weight: 400;
font-size: 20px;
line-height: 28px;
padding: 14px;
user-select: none;
cursor: pointer;
transition-property: background, color;
transition-duration: 0.05s;
transition-timing-function: ease-out;
&,
&:visited {
color: hsl(0deg 0% 100% / 80%);
text-decoration: none;
}
}
.top-menu-item:hover {
color: hsl(0deg 0% 100%);
background: hsl(240deg 100% 10% / 10%);
transition-duration: 0.05s;
}
.top-menu-item:active {
color: hsl(0deg 0% 100%);
background: hsl(240deg 100% 10% / 20%);
}
.top-menu-item.top-menu-tab,
.top-menu-item.top-menu-tab:hover,
.top-menu-item.top-menu-tab:active {
opacity: 1;
background: none;
}
.top-menu-item-spacer {
flex-shrink: 1;
flex-grow: 1;
}
.top-menu-item input[type="radio"],
.top-menu-tab-input-unselect {
display: none;
}
.top-menu-item.top-menu-tab {
padding: 0;
position: relative;
cursor: auto;
}
.top-menu-tab-unselect {
position: absolute;
inset: 0;
z-index: 0;
cursor: pointer;
}
.top-menu-tab-input:checked + label {
transition:
background,
color 0.2s;
background: hsl(240deg 100% 10% / 60%);
color: hsl(0deg 0% 100%);
pointer-events: none;
user-select: none;
}
.top-menu-tab label {
padding: 16px;
cursor: pointer;
transition-property: background, color;
transition-duration: 0.1s;
transition-timing-function: ease-out;
z-index: 5;
color: hsl(0deg 0% 100% / 80%);
/* Override bootstrap */
margin-bottom: 0;
font-size: inherit;
line-height: inherit;
}
.top-menu-tab label::after {
pointer-events: none;
content: " ";
display: inline-block;
vertical-align: middle;
width: 16px;
height: 16px;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(255%2c255%2c255%2c0.8)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round' class='feather feather-chevron-down'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
background-size: 16px;
margin-left: 2px;
margin-right: -8px;
background-repeat: no-repeat;
}
.top-menu-tab .top-menu-tab-input:checked + label::after {
transform: rotate(-180deg);
}
.top-menu-tab .top-menu-tab-input:not(:checked) + label:hover {
background: hsl(240deg 100% 10% / 10%);
color: hsl(0deg 0% 100%);
transition-duration: 0.1s;
}
.top-menu-tab .top-menu-tab-input:not(:checked) + label:active {
background: hsl(240deg 100% 10% / 20%);
color: hsl(0deg 0% 100%);
}
.top-menu-tab label.top-menu-tab-user-label::after {
display: block;
position: absolute;
right: 16px;
top: 50%;
transform: translateY(-45%);
}
.top-menu-tab
.top-menu-tab-input:checked
+ label.top-menu-tab-user-label::after {
transform: translateY(-45%) rotate(-180deg);
}
.top-menu-submenu-backdrop {
background: hsl(240deg 100% 10% / 60%);
opacity: 0;
height: 0;
width: 100%;
transition: 0.05s;
transition-property:
height, opacity; /* stylelint-disable-line plugin/no-low-performance-animation-properties */
}
.top-menu-tab-input-unselect:not(:checked) + .top-menu-submenu-backdrop {
height: 334px;
opacity: 1;
}
.top-menu-tab-label-unselect {
position: absolute;
display: none;
/* To account for scrollbar if any present. */
right: 15px;
top: var(--navbar-height);
width: 44px;
height: 44px;
background-size: 32px;
background-repeat: no-repeat;
/* Close menu icon */
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(255,255,255,1)' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3e%3cline x1='18' y1='6' x2='6' y2='18'%3e%3c/line%3e%3cline x1='6' y1='6' x2='18' y2='18'%3e%3c/line%3e%3c/svg%3e");
background-position: center;
cursor: pointer;
opacity: 0.5;
}
.top-menu-tab-label-unselect:hover {
opacity: 0.8;
}
.top-menu-tab-label-unselect:active {
opacity: 1;
}
.top-menu-tab-input-unselect:not(:checked) ~ .top-menu-tab-label-unselect {
display: block;
}
.top-menu-submenu {
position: absolute;
top: var(--navbar-height);
display: flex;
gap: 16px;
opacity: 0;
visibility: hidden;
transition: 0.2s;
transition-property: opacity, visibility;
}
#case-studies-submenu {
width: 100vw;
position: fixed;
left: 0;
justify-content: center;
@media (width <= 1300px) {
flex-wrap: wrap;
place-content: baseline flex-start;
height: calc(100vh - var(--navbar-height));
overflow-y: auto;
width: 100vw;
padding-left: 10px;
}
& > .top-menu-submenu-column:last-child {
/* Bottom padding provides space for the scroll fade
indicator so no content is obscured at end of scroll. */
padding-bottom: 50px;
}
/* Fade bottom edge to hint at scrollable content. */
mask-image: linear-gradient(
to bottom,
hsl(0deg 0% 0%) calc(100% - 50px),
transparent 100%
);
}
/* Prevent background scrolling when the case studies dropdown
covers the full viewport height on narrower desktop screens.
Lower bound excludes widths where the desktop navbar is hidden. */
@media (width > 1100px) and (width <= 1300px) {
body:has(#top-menu-tab-case-studies:checked) {
overflow: hidden;
}
}
.top-menu-tab .top-menu-tab-user-label {
max-width: 140px;
padding-right: 28px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.top-menu-tab-user-img {
width: 24px;
height: 24px;
display: inline;
vertical-align: middle;
border-radius: 3px;
margin-right: 4px;
}
.top-menu-mobile-user-img {
width: 28px;
height: 28px;
display: inline;
vertical-align: text-top;
border-radius: 3px;
margin-right: 4px;
}
@media (width <= 1400px) {
.top-menu-submenu.signup-column {
justify-content: flex-end;
right: 55px;
}
}
@media (width <= 1024px) {
.top-menu-tab .top-menu-tab-user-label {
max-width: 40px;
}
.top-menu-tab .top-menu-tab-user-label .top-menu-tab-user-name {
display: none;
}
}
.top-menu-tab-input:checked ~ .top-menu-submenu {
opacity: 1;
visibility: visible;
}
.top-menu-submenu-column {
display: flex;
flex-direction: column;
padding-top: 16px;
flex-shrink: 0;
max-width: 420px;
.top-menu-submenu-list {
margin: 0;
padding-left: 0;
list-style: none;
.top-menu-submenu-list-item {
font-size: 17px;
font-weight: 400;
color: hsl(0deg 0% 100% / 90%);
border-radius: 2px;
cursor: pointer;
&:hover {
background: hsl(0deg 0% 100% / 10%);
}
&:active {
background: hsl(0deg 0% 100% / 20%);
}
& a {
display: block;
padding: 6px 16px;
text-decoration: none;
line-height: 125%;
color: hsl(0deg 0% 100% / 90%);
}
}
}
}
.top-menu-mobile .top-menu-mobile-item a {
padding: 10px 10px 10px 53px;
border-radius: 2px;
cursor: pointer;
text-decoration: none;
display: block;
&:hover {
background: hsl(0deg 0% 100% / 10%);
}
&:active {
background: hsl(0deg 0% 100% / 20%);
}
}
.top-menu-submenu-section {
padding-left: 16px;
font-size: 17px;
opacity: 0.6;
margin-bottom: 4px;
letter-spacing: 2px;
}
.top-menu-mobile {
display: none;
background: hsl(240deg 100% 10% / 40%);
backdrop-filter: blur(10px);
z-index: 20;
/* No `top` — defaults to auto, which resolves to the static
position (just below the sticky banner when one is present,
or at the viewport top otherwise). Same trick as .top-menu
above. The [open] state below pins to top: 0 so the expanded
panel covers the full viewport. */
position: fixed;
left: 0;
right: 0;
height: var(--navbar-height);
overflow: auto;
font-style: normal;
font-weight: 500;
font-size: 20px;
line-height: 28px;
letter-spacing: 0.02em;
font-feature-settings:
"pnum" on,
"lnum" on;
color: hsl(0deg 0% 100% / 80%);
overscroll-behavior: contain;
}
.top-menu-mobile[open] {
top: 0;
bottom: 0;
height: 100%;
background: hsl(240deg 100% 5.9% / 80%);
backdrop-filter: blur(20px);
}
/* When a banner is present, keep it visible above the open menu by
pushing the panel down by the banner's actual rendered height.
--announcement-banner-height is set by header.ts only while the menu is open
(via a ResizeObserver scoped to that interval), so the offset
stays accurate when the announcement wraps onto multiple lines on
narrow viewports without paying for an observer the rest of the
time. The 42px fallback covers the first frame before JS runs. */
:has(#navbar-custom-message) .top-menu-mobile[open] {
top: var(--announcement-banner-height, 42px);
height: calc(100% - var(--announcement-banner-height, 42px));
}
.top-menu-mobile-items-group-1,
.top-menu-mobile-items-group-2 {
display: flex;
flex-direction: column;
margin-bottom: 24px;
}
.top-menu-mobile-items-group-1 summary,
.top-menu-mobile-items-group-1 a,
.top-menu-mobile-items-group-2 summary,
.top-menu-mobile-items-group-2 a {
cursor: pointer;
transition: background 0.4s ease-out;
}
.top-menu-mobile-items-group-1 summary,
.top-menu-mobile-items-group-2 summary {
padding: 10px 10px 10px 23px;
user-select: none;
}
.top-menu-mobile-items-group-1 a,
.top-menu-mobile-items-group-2 a {
padding-left: 53px;
}
@media (hover: hover) and (pointer: fine) {
.top-menu-mobile-items-group-1 summary:hover,
.top-menu-mobile-items-group-2 summary:hover {
background: hsl(0deg 0% 100% / 10%);
transition: none;
}
}
.top-menu-mobile-items-group-1 summary:active,
.top-menu-mobile-items-group-2 summary:active {
background: hsl(0deg 0% 100% / 10%);
}
/* We display: block here instead of the default
display: list-item largely for the sake of Safari,
which as of early 2026 still ignores the ::marker
line below. Because we're overriding the default
markers on all browsers, this display mode doesn't
impact other browsers, either. */
.top-menu-mobile-item-summary {
display: block;
}
.top-menu-mobile-item-summary::marker {
content: "";
}
.top-menu-mobile-item-summary::before {
content: "";
display: inline-block;
vertical-align: middle;
margin-top: -4px;
margin-right: 4px;
width: 24px;
height: 24px;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(255%2c255%2c255%2c0.7)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-chevron-down'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
transition: transform 0.12s ease-out;
transform: rotate(-90deg);
}
.top-menu-mobile details[open] > summary::before {
transform: rotate(0deg);
}
.top-menu-mobile-submenu {
display: flex;
flex-direction: column;
font-size: 18px;
margin-bottom: 18px;
.top-menu-submenu-list {
margin: 0;
list-style: none;
.top-menu-submenu-list-item {
font-size: 17px;
font-weight: 400;
line-height: 26px;
color: hsl(0deg 0% 100% / 90%);
border-radius: 2px;
cursor: pointer;
&:hover {
background: hsl(0deg 0% 100% / 10%);
}
&:active {
background: hsl(0deg 0% 100% / 20%);
}
& a {
text-decoration: none;
padding: 5px 0 5px 53px;
display: block;
}
}
}
}
.top-menu-mobile-submenu-section {
letter-spacing: 0.1em;
color: hsl(0deg 0% 100% / 40%);
text-transform: uppercase;
margin-top: 8px;
font-size: 17px;
padding-left: 53px;
user-select: none;
pointer-events: none;
}
.top-menu-mobile-summary {
cursor: pointer;
position: sticky;
top: 0;
z-index: 1;
transition: 0.3s;
transition-property: background, backdrop-filter;
height: var(--navbar-height);
overflow: hidden;
display: flex;
justify-content: space-between;
align-items: center;
}
.top-menu-mobile-summary:active {
background: hsl(229deg 47% 17% / 80%);
backdrop-filter: blur(10px);
}
.top-menu-mobile[open] > .top-menu-mobile-summary {
background: hsl(229deg 47% 17% / 80%);
backdrop-filter: blur(10px);
}
/* For browsers which don't support blur */
@supports not (
(backdrop-filter: blur(10px)) or (-webkit-backdrop-filter: blur(10px))
) {
.top-menu {
background: hsl(240deg 100% 10% / 80%);
}
.top-menu-mobile {
background: hsl(240deg 100% 10% / 80%);
}
.top-menu-mobile[open] {
background: hsl(240deg 100% 5.9% / 100%);
}
.top-menu-mobile-summary:active {
background: hsl(229deg 47% 17% / 95%);
}
.top-menu-mobile[open] > .top-menu-mobile-summary {
background: hsl(229deg 47% 17% / 95%);
}
}
.top-menu-mobile[open] > .top-menu-mobile-summary:active {
background: hsl(229deg 47% 20% / 100%);
}
.top-menu-mobile-summary::marker {
content: "";
display: none;
}
.top-menu-mobile-summary::after {
display: block;
height: 32px;
float: right;
content: "MENU";
line-height: 32px;
color: hsl(0deg 0% 100% / 80%);
padding-right: 38px;
font-size: 20px;
font-weight: 400;
letter-spacing: 2px;
margin-right: 16px;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(255,255,255,0.9)' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3e%3cline x1='3' y1='12' x2='21' y2='12'%3e%3c/line%3e%3cline x1='3' y1='6' x2='21' y2='6'%3e%3c/line%3e%3cline x1='3' y1='18' x2='21' y2='18'%3e%3c/line%3e%3c/svg%3e");
background-repeat: no-repeat;
background-size: 32px;
background-position: right;
transition:
background,
letter-spacing 0.2s; /* stylelint-disable-line plugin/no-low-performance-animation-properties */
}
.top-menu-mobile-summary:active::after {
letter-spacing: 2px;
}
.top-menu-mobile[open] > summary::after {
content: "Close";
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(255,255,255,0.9)' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3e%3cline x1='18' y1='6' x2='6' y2='18'%3e%3c/line%3e%3cline x1='6' y1='6' x2='18' y2='18'%3e%3c/line%3e%3c/svg%3e");
}
@media (hover: hover) and (pointer: fine) {
.top-menu-mobile-summary:hover {
background: hsl(229deg 47% 18% / 100%);
}
.top-menu-mobile[open] > .top-menu-mobile-summary:hover {
background: hsl(229deg 47% 18% / 100%);
}
.top-menu-mobile-summary:hover::after {
letter-spacing: 4px;
}
}
/* menu responsivity */
@media (width <= 1311px) {
.top-menu {
background: hsl(240deg 100% 10% / 50%);
}
.top-menu-logo {
margin-right: 0;
}
.top-menu-item {
padding: 16px 12px;
font-size: 18px;
font-weight: 500;
}
}
@media (width <= 1100px) {
.top-menu {
display: none;
}
.top-menu-logo {
margin-right: unset;
display: inline-block;
}
.top-menu-mobile {
display: block;
}
}

View File

@@ -0,0 +1,14 @@
/* While the portico pages design is in flux,
we need to limit globally scoped CSS like this
to its own file.
The bulk of styles as well as CSS variables are
in `pricing_plans.css`, which gets shared on
non-plans pages such as /for/business/ */
html {
/* Set the background on HTML to the starting
color for the plans-page gradient, so that the
top overscroll is undetectable. */
background: var(--color-background-gradient-start);
}

View File

@@ -0,0 +1,50 @@
/* This file contains the required variables and styles for the modals
to work properly in the portico, billing, upgrade and dev email log pages. */
:root {
/* This is a copy of the neutral icon button variables defined
in app_variables.css required for the modal close button styling. */
--color-text-neutral-icon-button: light-dark(
color-mix(in oklch, hsl(229deg 9% 36%) 70%, transparent),
color-mix(in oklch, hsl(229deg 10% 70%) 70%, transparent)
);
--color-text-neutral-icon-button-hover: light-dark(
hsl(229deg 9% 36%),
hsl(229deg 10% 70%)
);
--color-text-neutral-icon-button-active: light-dark(
color-mix(in oklch, hsl(229deg 9% 36%) 70%, transparent),
color-mix(in oklch, hsl(229deg 10% 70%) 70%, transparent)
);
}
.modal__container {
--base-font-size-px: 16px;
font-size: var(--base-font-size-px);
font-weight: 400;
}
.modal__close {
/* This is a subset of the styling defined for the icon button
component in web/styles/buttons.css. */
display: flex;
justify-content: center;
align-items: center;
aspect-ratio: 1;
font-size: var(--base-font-size-px);
background-color: transparent; /* Override button default background color */
cursor: pointer;
border: none;
border-radius: 4px;
color: var(--color-text-neutral-icon-button);
&:hover,
&:focus-visible {
color: var(--color-text-neutral-icon-button-hover);
}
&:active {
color: var(--color-text-neutral-icon-button-active);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,40 @@
:root {
--color-background-gradient-start: hsl(209deg 40% 50%);
--color-background-gradient-end: hsl(210deg 48% 69%);
/* This is the same color as set on `--color-table-background` in
comparison_table.css. */
--color-background-marketing-page: hsl(0deg 0% 100% / 80%);
/* This is a legacy color previously set on .why-page divs. */
--color-text-marketing-page: hsl(223deg 6% 25%);
--color-background-solid-hero: var(--color-background-gradient-start);
--color-text-hero: hsl(0deg 0% 100%);
--font-ss3: "Source Sans 3 VF", sans-serif;
--font-ops: "Open Sans Variable", sans-serif;
--color-background-box: hsl(210deg 44% 92%);
--color-background-box-muted: hsl(0deg 0% 100% / 50%);
--color-background-box-muted-hover: hsl(0deg 0% 100% / 60%);
--color-plan-tab-header: hsl(223deg 40% 30%);
--color-plan-tab-copy: hsl(223deg 40% 25% / 70%);
--color-box-header: hsl(223deg 43% 25%);
--color-box-copy: hsl(223deg 43% 25%);
--color-box-copy-fine-print: hsl(223deg 43% 55%);
--color-link: hsl(210deg 94% 42%);
--color-link-hover: hsl(210deg 100% 50%);
--color-link-portico-auth: hsl(200deg 100% 40%);
--color-link-portico-auth-hover: hsl(200deg 100% 25%);
--color-link-underline: hsl(210deg 94% 42% / 30%);
--color-link-underline-hover: hsl(210deg 100% 50%);
--color-link-underline-alternate: hsl(210deg 94% 100% / 40%);
--color-link-underline-alternate-hover: hsl(210deg 94% 100% / 80%);
--color-prominent-border: var(--color-link);
--color-action-button: hsl(240deg 66% 60%);
--color-action-button-hover: hsl(240deg 66% 55%);
--color-action-button-active: hsl(240deg 66% 50%);
--color-action-button-subtle-hover: hsl(240deg 66% 97%);
--color-action-button-subtle-active: hsl(240deg 66% 94%);
@media (prefers-color-scheme: dark) {
--color-background-gradient-start: hsl(209deg 63% 40%);
--color-background-gradient-end: hsl(238deg 28% 50%);
}
}

View File

@@ -0,0 +1,874 @@
.portico-pricing {
--icon-left-outward-tab-curve: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' viewBox='0 0 16 16'%3e%3cpath fill='white' d='M0 16h16V0c0 8.837-7.163 16-16 16Z'/%3e%3c/svg%3e");
--icon-right-outward-tab-curve: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' viewBox='0 0 16 16'%3e%3cpath fill='white' d='M16 16H0V0c0 8.837 7.163 16 16 16Z'/%3e%3c/svg%3e");
--icon-question-header-swoosh: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='33' height='56' fill='none' viewBox='0 0 33 56'%3e%3cpath fill='%23E2EBF4' d='M33 37 1 56C1 35-3.5 0 33 0 7.5 3 7.776 37 33 37Z'/%3e%3c/svg%3e");
--icon-current-plan-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='none' viewBox='0 0 22 22'%3e%3cg fill='%230F8544'%3e%3cpath d='M14.396 9.813a.914.914 0 1 0-1.293-1.293l-3.02 3.02-1.186-1.186a.914.914 0 1 0-1.294 1.293l1.834 1.833a.914.914 0 0 0 1.293 0l3.666-3.667Z'/%3e%3cpath fill-rule='evenodd' d='M11 .923a4.58 4.58 0 0 0-3.495 1.62 4.581 4.581 0 0 0-4.961 4.949 4.58 4.58 0 0 0 0 7.016 4.58 4.58 0 0 0 4.96 4.949 4.583 4.583 0 0 0 7.003-.001 4.582 4.582 0 0 0 4.95-4.96 4.583 4.583 0 0 0 0-6.991 4.58 4.58 0 0 0-4.962-4.962A4.581 4.581 0 0 0 11 .923ZM9.678 3.09a2.752 2.752 0 0 1 3.64.932.914.914 0 0 0 .972.4 2.752 2.752 0 0 1 3.289 3.288.914.914 0 0 0 .4.971 2.753 2.753 0 0 1 0 4.638.914.914 0 0 0-.4.97 2.752 2.752 0 0 1-3.283 3.29.914.914 0 0 0-.97.401 2.75 2.75 0 0 1-4.644 0 .914.914 0 0 0-.971-.401 2.752 2.752 0 0 1-3.29-3.283.914.914 0 0 0-.403-.97 2.753 2.753 0 0 1 0-4.652.914.914 0 0 0 .404-.97A2.752 2.752 0 0 1 7.71 4.42a.914.914 0 0 0 .97-.4c.25-.389.592-.709.997-.93Z' clip-rule='evenodd'/%3e%3c/g%3e%3c/svg%3e");
--icon-discount-tooltip-curve: url("data:image/svg+xml,%3Csvg width='35' height='14' viewBox='0 0 35 14' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M17.4814 14C16.3229 6.40469 8.98604 0 0.5 0L34.5 0C26.0153 0 18.4853 6.40469 17.4814 14Z' fill='%23F4F9FE'/%3E%3C/svg%3E");
--width-price-box-min: 240px;
--width-price-box-max: 355px;
--height-discounted-price-box: 98px;
--height-price-details-min: 53px;
padding: 102px 0 58px;
background: linear-gradient(
180deg,
var(--color-background-gradient-start) 0%,
var(--color-background-gradient-end) 100%
);
@media (prefers-color-scheme: dark) {
--color-background-box: hsl(221deg 35% 89%);
background: linear-gradient(
180deg,
var(--color-background-gradient-start) 30%,
var(--color-background-gradient-end) 100%
);
}
ul {
padding: 0;
margin: 0;
}
h1 {
font-weight: 500;
font-size: 58px;
line-height: 110%;
text-align: center;
color: hsl(0deg 0% 100%);
margin: 10px;
}
.h1-subheader {
font-family: var(--font-ops);
font-weight: 400;
font-size: 20px;
line-height: 145%;
text-align: center;
font-feature-settings:
"pnum" on,
"lnum" on;
color: hsl(0deg 0% 100%);
max-width: 560px;
padding: 0 10px;
margin: 0 auto;
/* There's never a button instance here, but we need
this to correct for specificity against the overall
marketing-page link styles.
TODO: Integrate this link style so as to not add a
:not() selector here. */
& a:not(.button) {
color: inherit;
text-decoration: underline;
text-decoration-color: hsl(0deg 0% 100% / 50%);
text-decoration-thickness: 1px;
text-underline-offset: 2px;
transition: text-decoration 0.4s ease-out;
&:hover {
text-decoration-color: hsl(0deg 0% 100%);
text-decoration-thickness: 2px;
color: inherit;
transition: none;
}
}
}
li {
list-style-type: none;
display: flex;
align-items: flex-start;
/* Prevent flexbox from collapsing
whitespace around links and other
inline elements. */
gap: 0.3em;
&::before {
content: "";
height: 6px;
width: 6px;
border-radius: 16px;
/* 0.5333em, roughly 8px given the
15px font-size on list items, will
keep the bullet vertically centered
with the first line of a list item
as the page's text scales. */
margin: 0.5333em 4px 0 0;
/* Prevent "bullets" from being distorted
on a multiline bullet point. */
flex: 0 0 auto;
background: currentcolor;
opacity: 0.3;
}
}
.unlimited-push-notifications {
padding-bottom: 21px;
}
.support-note {
margin-top: 15px;
font-size: 17px;
font-weight: 550;
&::before {
/* Hide the bullet on the support note,
but still keep its width to maintain
lefthand alignment. */
background: transparent;
}
}
a {
color: var(--color-link);
text-decoration: underline;
text-decoration-color: var(--color-link-underline);
text-underline-offset: 4px;
&:hover {
color: var(--color-link-hover);
text-decoration: underline;
text-decoration-color: var(--color-link-underline-hover);
}
}
/* Remove padding introduced in legacy_landing_page.css */
.padded-content {
padding: 0;
}
@media (width <= 768px) {
.pricing-model .padded-content {
padding: 0;
}
}
.pricing-container {
display: grid;
grid-template:
". . cloud-title self-hosted-title . . " auto
"pricing pricing pricing pricing pricing pricing" auto / 56px 1fr minmax(
0,
420px
)
minmax(0, 420px) 1fr 56px;
column-gap: 2px;
margin: 50px auto 38px;
/* We use the plan count on the respective `.showing-` class
to determine a maximum width, beyond which the plans will
grow no wider. */
max-width: calc(
(var(--count-plans) * var(--width-price-box-max)) + 40px
);
@media screen and (width <= 700px) {
grid-template-columns:
40px 1fr minmax(0, 420px)
minmax(0, 420px) 1fr 40px;
}
}
.request-sponsorship {
margin-bottom: 10px;
}
.pricing-pane-scroll-container {
grid-area: pricing;
overflow-x: auto;
scrollbar-width: none;
}
.cloud-plan-title {
grid-area: cloud-title;
}
.self-hosted-plan-title {
grid-area: self-hosted-title;
}
.pricing-tab,
.inactive-pricing-tab {
cursor: pointer;
background: var(--color-background-box);
padding: 12px 12px 0;
border-radius: 16px 16px 0 0;
min-height: 40px;
h2 {
color: var(--color-plan-tab-header);
font-family: var(--font-ops);
font-weight: 550;
font-size: 30px;
line-height: 32px;
margin: 0;
margin-bottom: 8px;
text-align: center;
}
p {
color: var(--color-plan-tab-copy);
font-weight: 400;
font-size: 16px;
line-height: 125%;
margin: 0;
text-align: center;
padding-bottom: 14px;
@media screen and (width <= 700px) {
display: none;
}
}
/* Shared styles to handle tab curvature. */
&::before {
display: none;
width: 16px;
height: 16px;
content: "";
position: absolute;
bottom: 0;
left: -16px;
background-color: var(--color-background-box);
mask-position: center;
mask-repeat: no-repeat;
mask-image: var(--icon-left-outward-tab-curve);
}
&::after {
display: none;
width: 16px;
height: 16px;
content: "";
position: absolute;
bottom: 0;
right: -16px;
background-color: var(--color-background-box);
mask-position: center;
mask-repeat: no-repeat;
mask-image: var(--icon-right-outward-tab-curve);
}
}
.inactive-pricing-tab {
cursor: default;
}
.pricing-pane {
display: grid;
grid-template-columns: repeat(
auto-fit,
minmax(var(--width-price-box-min), 1fr)
);
grid-template-rows: auto;
/* We use the plan count on the respective `.showing-` class
to determine a minimum width, at which point the plans scroll. */
min-width: calc(var(--count-plans) * var(--width-price-box-min));
padding: 20px 5px;
border-radius: 16px;
color: var(--color-box-copy);
background: var(--color-background-box);
margin: 0 16px;
@media screen and (width <= 700px) {
margin: 0 8px;
}
h2 {
font-weight: 550;
font-size: 28px;
line-height: 28px;
padding-left: 14px;
margin: 8px 0 16px;
&.with-fine-print {
display: flex;
/* Keep fine-print to same distance from
the dashed border to the right as the
heading text is to the left. */
padding: 0 14px;
small {
flex: 0 1 min-content;
margin-left: auto;
font-weight: 350;
font-size: 14px;
line-height: 14px;
text-align: right;
color: var(--color-box-copy-fine-print);
}
}
}
ul {
font-family: var(--font-ops);
font-weight: 400;
font-size: 15px;
}
li {
margin-bottom: 10px;
line-height: 21px;
}
.price-box {
height: 100%;
display: flex;
flex-direction: column;
border-right: 1px dashed hsl(223deg 40% 25% / 20%);
&:last-child {
border-right: 0;
}
/* Make sure flexing text-content boxes
occupy their full with, but account
for padding with border-box sizing. */
.text-content {
box-sizing: border-box;
width: 100%;
margin: 0 auto;
}
/* Set right and left padding on the
bottom box so that buttons align
to the text column above. */
.bottom {
padding: 0 16px;
.text-content {
/* But allow buttons and price info
a bit more breathing room. */
padding: 0;
}
}
}
.text-content {
padding: 0 16px;
}
.bottom {
/* Push to the bottom of the price box. */
margin-top: auto;
}
.standard-price-box {
display: flex;
/* Handle a discount line, when
needed. */
flex-wrap: wrap;
align-items: flex-start;
gap: 4px;
margin: 24px 0 6px;
font-weight: 400;
/* For supporting browsers, constrain the height
of the standard price box when it includes a
discount. This keeps discount tooltips on the
same horizontal line, while also maintaining
alignment of non-discounted price box prices. */
&:has(.discount) {
height: var(--height-discounted-price-box);
}
.price {
font-size: 38px;
font-weight: 400;
line-height: 1;
letter-spacing: -1px;
}
.currency-symbol {
opacity: 0.5;
}
.details {
flex: 1 0 0;
p {
margin: 2px 0 0;
font-size: 15px;
line-height: 17px;
}
}
.discount {
/* Keep the discount to its own
line in the wrapping flexbox. */
flex: 0 0 100%;
/* But move it to the top of the
flex area. */
order: -1;
text-align: center;
padding: 9px 0 10px;
margin-bottom: 9px;
border-radius: 30px;
background-color: hsl(210deg 83% 98%);
position: relative;
font-family: var(--font-ops);
font-size: 15px;
/* Use padding to better position
text within its single line. */
line-height: 1;
@media screen and (width <= 1240px) {
/* Pad the discount balloon... */
padding: 8px 5px 9px;
/* But pull out the margins an equal
amount so it can breathe. */
margin: 0 -5px 9px;
}
& b {
font-weight: 650;
}
&::after {
content: "";
background-image: var(--icon-discount-tooltip-curve);
display: block;
width: 35px;
height: 14px;
bottom: -14px;
left: calc(50% - 17.5px);
position: absolute;
}
}
.price,
.details {
/* Preserve a height for allowing
price details to wrap as many
as three lines. */
min-height: var(--height-price-details-min);
}
}
}
.additional-pricing-information {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
grid-template-rows: auto;
column-gap: 32px;
max-width: 912px;
margin: 0 auto;
@media screen and (width <= 810px) {
grid-template-columns: minmax(0, 456px);
justify-content: center;
row-gap: 32px;
}
header {
position: relative;
padding: 0 16px 0 32px;
&::before {
display: block;
content: "";
width: 32px;
height: 56px;
position: absolute;
background-color: var(--color-background-box);
top: 30px;
left: -0.5px;
mask-position: center;
mask-repeat: no-repeat;
mask-image: var(--icon-question-header-swoosh);
}
h2 {
font-family: var(--font-ops);
font-weight: 500;
font-size: 30px;
line-height: 38px;
margin: 0;
}
p {
font-weight: 400;
font-size: 16px;
margin: 0;
margin-bottom: 6px;
}
a {
color: inherit;
text-decoration-color: var(--color-link-underline-alternate);
&:hover {
text-decoration: underline;
text-decoration-color: var(
--color-link-underline-alternate-hover
);
}
}
}
.text-content {
color: var(--color-box-copy);
background: var(--color-background-box);
padding: 20px 32px;
border-radius: 16px;
b {
font-weight: 600;
}
}
h3 {
margin: 0 0 6px;
font-weight: 500;
font-size: 22px;
line-height: 28px;
}
ul {
margin-bottom: 12px;
}
li {
font-size: 15px;
margin-bottom: 4px;
}
p {
margin: 0;
}
.sponsorship-button {
display: block;
font-weight: 550;
font-size: 15px;
line-height: 20px;
text-align: center;
padding: 8px 10px;
margin-bottom: 6px;
}
.contact-note {
font-weight: 400;
font-size: 14px;
}
}
@media screen and (width <= 500px) {
.additional-pricing-information {
margin: 0 8px;
header {
padding-bottom: 29px;
}
p {
display: none;
}
}
}
.button {
display: block;
font-family: var(--font-ss3);
font-weight: 550;
font-size: 15px;
text-align: center;
text-decoration: none;
letter-spacing: 0.06ch;
padding: 8px 10px;
border-radius: 4px;
/* Position relatively for discount tab */
position: relative;
z-index: 5;
}
.button-placeholder {
/* Empty element for maintaining the
layout on plans that otherwise have
no button. */
height: 37.5px;
visibility: hidden;
}
.get-started-button {
color: hsl(0deg 0% 100%);
background-color: hsl(146deg 92% 26%);
&:hover {
text-decoration: none;
color: hsl(0deg 0% 100%);
background-color: hsl(146deg 92% 24%);
}
}
.upgrade-button {
color: hsl(0deg 0% 100%);
background-color: hsl(219deg 62% 54%);
&:hover {
text-decoration: none;
color: hsl(0deg 0% 100%);
background-color: hsl(219deg 62% 50%);
}
}
.current-plan-button,
.current-plan-descriptor {
display: flex;
align-items: center;
justify-content: center;
gap: 0.5ch;
color: hsl(147deg 57% 25%);
background-color: hsl(152deg 79% 24% / 5%);
.current-plan-icon {
width: 22px;
height: 22px;
display: block;
background-color: currentcolor;
mask-position: center;
mask-repeat: no-repeat;
mask-image: var(--icon-current-plan-icon);
}
}
.current-plan-descriptor {
/* Pad and lead the descriptor
to better match other buttons */
padding: 1.75px 0;
line-height: 16px;
background-color: transparent;
border: 1px solid hsl(152deg 79% 24% / 10%);
font-weight: normal;
}
.current-plan-button:hover {
text-decoration: none;
color: hsl(147deg 57% 25%);
background-color: hsl(152deg 79% 24% / 14%);
}
.sponsorship-button {
color: hsl(147deg 57% 25%);
background-color: hsl(152deg 79% 24% / 10%);
&:hover {
text-decoration: none;
color: hsl(147deg 57% 25%);
background-color: hsl(146deg 92% 24% / 15%);
}
}
&.showing-cloud {
--count-plans: 3;
.self-hosted-scroll,
.self-hosted-plan-pricing,
.self-hosted-additional-pricing {
display: none;
}
.cloud-plan-title {
position: relative;
/* Show curvature on cloud tab. */
&::before {
display: block;
}
&::after {
display: block;
}
p {
border-bottom: 1px dashed hsl(223deg 40% 25% / 20%);
margin: 0 10px;
}
@media screen and (width <= 700px) {
h2 {
border-bottom: 1px dashed hsl(223deg 40% 25% / 20%);
height: 100%;
margin: 0 10px;
}
}
@media screen and (width <= 500px) {
h2 {
margin: 0;
}
}
}
.self-hosted-plan-title {
border-radius: 16px 16px 0 14px;
margin-bottom: 2px;
background-color: var(--color-background-box-muted);
&:hover {
background-color: var(--color-background-box-muted-hover);
}
}
}
&.showing-self-hosted {
--count-plans: 4;
.cloud-scroll,
.cloud-plan-pricing,
.cloud-additional-pricing {
display: none;
}
.cloud-plan-title {
border-radius: 16px 16px 14px 0;
margin-bottom: 2px;
background-color: var(--color-background-box-muted);
&:hover {
background-color: var(--color-background-box-muted-hover);
}
}
.self-hosted-plan-title {
position: relative;
/* Show curvature on self-hosted tab. */
&::before {
display: block;
}
&::after {
display: block;
}
p {
border-bottom: 1px dashed hsl(223deg 40% 25% / 20%);
margin: 0 10px;
}
@media screen and (width <= 700px) {
h2 {
border-bottom: 1px dashed hsl(223deg 40% 25% / 20%);
height: 100%;
margin: 0 10px;
}
}
@media screen and (width <= 500px) {
h2 {
margin: 0;
}
}
}
}
.is-self-hosted-realm {
/* Don't display buttons on cloud plans
when viewed by self-hosters. */
.cloud-plan-pricing .bottom .text-content {
display: none;
}
}
@media screen and (width <= 1140px) {
.self-hosted-plan-pricing {
ul {
font-size: 14px;
}
.standard-price-box {
--height-price-details-min: 48px;
--height-discounted-price-box: 91px;
.price {
font-size: 32px;
}
.details {
p {
font-size: 13px;
line-height: 16px;
}
}
.discount {
font-size: 13px;
}
}
}
}
@media screen and (width <= 940px) {
/* This block of styles duplicates the adjustments
to .self-hosted-plan-pricing above; because of the
fewer number of Cloud plans, we don't need these
adjustments until this breakpoint. */
.cloud-plan-pricing {
ul {
font-size: 14px;
}
.standard-price-box {
--height-price-details-min: 48px;
--height-discounted-price-box: 91px;
.price {
font-size: 32px;
}
.details {
p {
font-size: 13px;
line-height: 16px;
}
}
.discount {
font-size: 13px;
}
}
}
h1 {
font-size: 40px;
}
.h1-subheader {
font-size: 16px;
}
.pricing-tab {
h2 {
font-size: 24px;
line-height: 26px;
margin-bottom: 5px;
}
p {
font-size: 14px;
padding-bottom: 10px;
}
}
.pricing-pane {
h2 {
font-size: 22px;
margin: 4px 0 12px;
}
}
}
@media screen and (width <= 920px) {
.additional-pricing-information header h2 {
font-size: 24px;
}
}
@media screen and (width <= 500px) {
.pricing-tab h2 {
font-size: 20px;
}
}
}

View File

@@ -0,0 +1,143 @@
:root.dark-theme {
color-scheme: dark;
}
body {
--base-font-size-px: 16px;
width: 100%;
margin: 0;
padding: 0;
font-size: var(--base-font-size-px);
/* The line-height used in most of the UI should be at least
its legacy value, but should expand with larger base
line-height values. */
line-height: max(
var(--legacy-body-line-height-unitless),
var(--base-line-height-unitless)
);
font-family: "Source Sans 3 VF", sans-serif;
font-weight: unset; /* override value in legacy_portico.css */
color: var(--color-text-default);
background-color: var(--color-background);
}
.showroom-wrapper {
display: flex;
flex-flow: column;
gap: 20px;
padding: 40px 20px;
}
.showroom-controls-section {
display: grid;
grid-template-columns: [control-name-start] max-content [control-name-end control-input-start] 1fr [control-input-end];
gap: 10px;
margin-top: 50px;
width: min(100%, 500px);
}
.showroom-controls {
display: grid;
grid-template-columns: subgrid;
grid-column: control-name-start / control-input-end;
grid-auto-rows: 1fr;
gap: 10px;
background-color: var(--color-background);
border: solid 1px;
padding: 10px;
width: 100%;
}
.showroom-control {
display: grid;
grid-template-columns: subgrid;
grid-column: control-name-start / control-input-end;
gap: 10px;
}
.showroom-control-element {
font-size: inherit;
font-family: inherit;
}
.showroom-control-label {
cursor: pointer;
grid-column: control-name-start / control-name-end;
align-self: center;
}
.showroom-control-setting {
grid-column: control-input-start / control-input-end;
}
.showroom-control-setting-multiple {
display: flex;
align-items: center;
gap: 10px;
}
.hidden {
display: none !important;
}
.showroom-controls-label {
grid-column: control-name-start / control-input-end;
margin-top: 10px;
font-size: 1.4em;
font-weight: 600;
}
.showroom-button-grid {
display: grid;
grid-template-areas: "showroom-component-button-intent-label showroom-component-action-button-group showroom-component-icon-button-group";
grid-template-columns: repeat(3, max-content);
gap: 15px 30px;
}
.showroom-component-button-intent-group {
display: grid;
grid-template-columns: subgrid;
grid-column: 1 / -1;
place-items: center start;
}
.showroom-component-button-intent-label {
grid-area: showroom-component-button-intent-label;
}
.showroom-component-action-button-group,
.showroom-component-action-button-label {
grid-area: showroom-component-action-button-group;
}
.showroom-component-icon-button-group,
.showroom-component-icon-button-label {
grid-area: showroom-component-icon-button-group;
}
.showroom-component-action-button-group,
.showroom-component-icon-button-group {
display: flex;
flex-flow: row wrap;
gap: 15px;
}
.showroom-component-button-intent-label,
.showroom-component-action-button-label,
.showroom-component-icon-button-label {
font-size: 1.1em;
font-weight: 500;
}
#showroom_component_banner_select_intent {
text-transform: capitalize;
}
#showroom_component_banner_default_wrapper {
width: min(100%, 800px);
margin: 0 auto;
}
.showroom-filter-input-container {
width: clamp(200px, 100%, 400px);
}

View File

@@ -0,0 +1,201 @@
body {
font-family: "Source Sans 3 VF", sans-serif !important;
background-color: hsl(0deg 0% 98%);
}
p {
margin-bottom: 0;
}
.app-main {
padding: 0;
width: auto;
max-width: none;
}
.svg-container {
margin: 20px;
}
.center-charts {
margin: 30px;
}
.stats-page .alert,
.analytics-page-header {
text-align: center;
}
.stats-page h1 {
font-weight: 300;
}
.summary-stats {
vertical-align: top;
margin: 0 auto;
padding: 20px;
border: 2px solid hsl(0deg 0% 93%);
background-color: hsl(0deg 0% 100%);
width: 395px;
& h1 {
margin-top: 0;
font-size: 1.5em;
}
}
.flex-container {
display: flex;
flex-flow: wrap;
justify-content: space-around;
}
.chart-container {
vertical-align: top;
margin: 10px 0;
padding: 20px;
border: 2px solid hsl(0deg 0% 93%);
background-color: hsl(0deg 0% 100%);
& button {
position: relative;
z-index: 1;
}
& h1 {
margin-top: 0;
font-size: 1.5em;
}
.button-container {
position: relative;
z-index: 1;
margin-bottom: 5px;
}
}
.analytics-page-header {
margin-left: 10px;
}
.rangeslider-container {
user-select: none;
}
.rangeselector text {
font-weight: 400;
}
.buttons button {
background-color: hsl(0deg 0% 94%);
&.selected {
background-color: hsl(0deg 0% 85%);
}
}
.button {
font-family: "Source Sans 3 VF", sans-serif !important;
border: none;
border-radius: 4px;
outline: none;
padding: 2px 6px;
&:hover {
background-color: hsl(0deg 0% 84%) !important;
}
}
.docs_link a {
font-weight: 300;
color: inherit;
text-decoration: none;
}
.last-update {
margin: 0 0 30px;
font-size: 0.8em;
font-weight: 400;
text-align: center;
color: hsl(0deg 0% 67%);
}
#users_hover_humans,
#read_hover_everyone,
#hover_human {
margin-left: 10px;
}
#id_messages_sent_by_client {
min-height: 100px;
width: 750px;
position: relative;
}
#id_messages_sent_by_message_type {
height: 300px;
width: 750px;
position: relative;
}
#id_messages_sent_over_time,
#id_messages_read_over_time {
height: 400px;
width: 750px;
position: relative;
&[last_value_is_partial="true"] .points path:last-of-type {
opacity: 0.5;
}
}
#id_number_of_users {
height: 370px;
width: 750px;
position: relative;
}
#id_stats_errors {
display: none;
}
#users_hover_info,
#read_hover_info,
#hoverinfo,
#pie_messages_sent_by_type_total {
float: right;
font-size: 0.8em;
font-weight: 400;
}
#users_hover_info,
#read_hover_info,
#hoverinfo {
display: none;
position: relative;
height: 0;
top: -25px;
}
.spinner::before {
content: "";
box-sizing: border-box;
position: absolute;
top: 50%;
left: 50%;
width: 30px;
height: 30px;
margin-top: -15px;
margin-left: -15px;
border-radius: 50%;
border: 1px solid hsl(0deg 0% 80%);
border-top-color: hsl(155deg 93% 42%);
animation: spinner 1s linear infinite;
}
@keyframes spinner {
to {
transform: rotate(360deg);
}
}

File diff suppressed because one or more lines are too long

147
web/styles/print.css Normal file
View File

@@ -0,0 +1,147 @@
/* The container used by info_overlay.print_current_pane() to print
an informational overlay's full scrollable content. Hidden on
screen; the @media print rules below display only it. This rule
must precede them so that they win in the cascade. */
#zulip-print-container {
display: none;
}
@media print {
/* Hide unnecessary blocks. */
#navbar_alerts_wrapper,
#streamlist-toggle,
#left-sidebar-container,
#right-sidebar-container,
.column-left,
.top-messages-logo,
#userlist-toggle,
.message_length_controller,
#loading_older_messages_indicator,
#page_loading_indicator,
#message_feed_errors_container,
#bottom_whitespace,
#mark_read_on_scroll_state_banner,
#mark_read_on_scroll_state_banner_place_holder,
.trailing_bookend,
#compose {
display: none;
}
/* Allow printer to set the margins, and
prevent Safari from using screen-based ones. */
.column-middle {
margin: 0;
}
/* Prevent headers from running on every page. */
#navbar-fixed-container,
.message-feed .message_header {
position: static;
}
/* Save a bit of paper by removing height, which
otherwise creates a blank final page, and padding. */
html,
body {
height: auto;
}
#message_feed_container {
padding-top: 0;
}
/* Hide unnecessary controls, but leave them
in the document flow. */
.search_icon,
#search_exit,
.settings-dropdown-cog,
.recipient_bar_controls {
visibility: hidden;
}
/* Try to keep the message header with the messages
that follow in interleaved views. */
.message_header_stream {
break-after: avoid;
}
/* Don't highlight the selected message. */
.selected_message .messagebox-content {
outline: 0;
}
.messagebox-content {
.message_edit_notice,
.message-time {
/* Firefox seems to have a bug that fuzzes out
small text with opacity; this unsets that
value, and replaces with a matching gray. */
opacity: unset;
color: hsl(0deg 0% 0% / 48%);
}
}
/* Show collapsed content for printing. Note that
CSS Grid does not yet break very intelligently
in all browsers, so longer messages may sometimes
appear at the top of new pages. */
.message_content.collapsed,
.message_content.condensed {
max-height: unset !important;
min-height: unset !important;
overflow: auto !important;
height: auto !important;
mask-image: none;
}
/* Print links in the same color as text, with any
likely full URL values in parentheses. */
.message_content a {
color: inherit;
&[href^="http"]::after {
content: " (" attr(href) ")";
}
}
/* Ensure that emoji print. They are background-images,
which ordinarily do not print, so these properties
should ensure proper printing of inline, status, and
other emoji. */
.emoji {
color-adjust: exact;
print-color-adjust: exact;
}
/* When a print container has been injected, print only it and
hide the rest of the app UI. Use :not() to exclude the
container itself, since the `display: none !important` here
would otherwise beat the container's `display: block` below,
regardless of selector specificity. */
body:has(#zulip-print-container) > *:not(#zulip-print-container) {
display: none !important;
}
#zulip-print-container {
display: block;
/* Print the pane with its on-screen colors. Dark theme is
scoped to `@media screen` (see dark_theme.css), so these
resolve to light-theme colors regardless of the user's
theme. */
color-adjust: exact;
print-color-adjust: exact;
}
/* Keep each row's keys and description together across a page
break. This also avoids a Firefox bug where a row's borders
disappear when it starts at the top of a new printed page. */
#zulip-print-container tr {
break-inside: avoid;
}
.zulip-print-title {
font-size: 1.5714em; /* 22px at 14px/1em */
margin: 0 0 12px;
}
}

View File

@@ -0,0 +1,57 @@
@keyframes progress-bar-stripes {
from {
background-position: 40px 0;
}
to {
background-position: 0 0;
}
}
.progress {
overflow: hidden;
height: 20px;
margin-bottom: 20px;
background: linear-gradient(to bottom, hsl(0deg 0% 96%), hsl(0deg 0% 98%));
background-repeat: repeat-x;
box-shadow: inset 0 1px 2px hsl(0deg 0% 0% / 10%);
border-radius: 4px;
}
.progress .bar {
width: 0%;
height: 100%;
color: hsl(0deg 0% 100%);
float: left;
background: linear-gradient(
to bottom,
hsl(200deg 84% 48%),
hsl(200deg 96% 38%)
);
background-repeat: repeat-x;
box-shadow: inset 0 -1px 2px hsl(0deg 0% 0% / 15%);
box-sizing: border-box;
transition: width 0.6s ease; /* stylelint-disable-line plugin/no-low-performance-animation-properties */
}
.progress.active .bar {
animation: progress-bar-stripes 2s linear infinite;
}
.progress .bar-danger {
background: linear-gradient(
to bottom,
hsl(2deg 81% 65%),
hsl(3deg 57% 49%)
);
background-repeat: repeat-x;
}
.progress .bar-success {
background: linear-gradient(
to bottom,
hsl(120deg 45% 58%),
hsl(120deg 32% 50%)
);
background-repeat: repeat-x;
}

518
web/styles/pygments.css Normal file
View File

@@ -0,0 +1,518 @@
/* stylelint-disable color-no-hex, no-duplicate-selectors */
/* Begin Pygments Default Colors */
%light-theme {
.codehilite .hll {
background-color: #ffc;
}
.codehilite .c {
color: #3d7b7b;
font-style: italic;
} /* Comment */
.codehilite .err {
border: 1px solid #f00;
} /* Error */
.codehilite .k {
color: #008000;
font-weight: bold;
} /* Keyword */
.codehilite .o {
color: #666;
} /* Operator */
.codehilite .ch {
color: #3d7b7b;
font-style: italic;
} /* Comment.Hashbang */
.codehilite .cm {
color: #3d7b7b;
font-style: italic;
} /* Comment.Multiline */
.codehilite .cp {
color: #9c6500;
} /* Comment.Preproc */
.codehilite .cpf {
color: #3d7b7b;
font-style: italic;
} /* Comment.PreprocFile */
.codehilite .c1 {
color: #3d7b7b;
font-style: italic;
} /* Comment.Single */
.codehilite .cs {
color: #3d7b7b;
font-style: italic;
} /* Comment.Special */
.codehilite .gd {
color: #a00000;
} /* Generic.Deleted */
.codehilite .ge {
font-style: italic;
} /* Generic.Emph */
.codehilite .ges {
font-weight: bold;
font-style: italic;
} /* Generic.EmphStrong */
.codehilite .gr {
color: #e40000;
} /* Generic.Error */
.codehilite .gh {
color: #000080;
font-weight: bold;
} /* Generic.Heading */
.codehilite .gi {
color: #008400;
} /* Generic.Inserted */
.codehilite .go {
color: #717171;
} /* Generic.Output */
.codehilite .gp {
color: #000080;
font-weight: bold;
} /* Generic.Prompt */
.codehilite .gs {
font-weight: bold;
} /* Generic.Strong */
.codehilite .gu {
color: #800080;
font-weight: bold;
} /* Generic.Subheading */
.codehilite .gt {
color: #04d;
} /* Generic.Traceback */
.codehilite .kc {
color: #008000;
font-weight: bold;
} /* Keyword.Constant */
.codehilite .kd {
color: #008000;
font-weight: bold;
} /* Keyword.Declaration */
.codehilite .kn {
color: #008000;
font-weight: bold;
} /* Keyword.Namespace */
.codehilite .kp {
color: #008000;
} /* Keyword.Pseudo */
.codehilite .kr {
color: #008000;
font-weight: bold;
} /* Keyword.Reserved */
.codehilite .kt {
color: #b00040;
} /* Keyword.Type */
.codehilite .m {
color: #666;
} /* Literal.Number */
.codehilite .s {
color: #ba2121;
} /* Literal.String */
.codehilite .na {
color: #687822;
} /* Name.Attribute */
.codehilite .nb {
color: #008000;
} /* Name.Builtin */
.codehilite .nc {
color: #00f;
font-weight: bold;
} /* Name.Class */
.codehilite .no {
color: #800;
} /* Name.Constant */
.codehilite .nd {
color: #a2f;
} /* Name.Decorator */
.codehilite .ni {
color: #717171;
font-weight: bold;
} /* Name.Entity */
.codehilite .ne {
color: #cb3f38;
font-weight: bold;
} /* Name.Exception */
.codehilite .nf {
color: #00f;
} /* Name.Function */
.codehilite .nl {
color: #767600;
} /* Name.Label */
.codehilite .nn {
color: #00f;
font-weight: bold;
} /* Name.Namespace */
.codehilite .nt {
color: #008000;
font-weight: bold;
} /* Name.Tag */
.codehilite .nv {
color: #19177c;
} /* Name.Variable */
.codehilite .ow {
color: #a2f;
font-weight: bold;
} /* Operator.Word */
.codehilite .w {
color: #bbb;
} /* Text.Whitespace */
.codehilite .mb {
color: #666;
} /* Literal.Number.Bin */
.codehilite .mf {
color: #666;
} /* Literal.Number.Float */
.codehilite .mh {
color: #666;
} /* Literal.Number.Hex */
.codehilite .mi {
color: #666;
} /* Literal.Number.Integer */
.codehilite .mo {
color: #666;
} /* Literal.Number.Oct */
.codehilite .sa {
color: #ba2121;
} /* Literal.String.Affix */
.codehilite .sb {
color: #ba2121;
} /* Literal.String.Backtick */
.codehilite .sc {
color: #ba2121;
} /* Literal.String.Char */
.codehilite .dl {
color: #ba2121;
} /* Literal.String.Delimiter */
.codehilite .sd {
color: #ba2121;
font-style: italic;
} /* Literal.String.Doc */
.codehilite .s2 {
color: #ba2121;
} /* Literal.String.Double */
.codehilite .se {
color: #aa5d1f;
font-weight: bold;
} /* Literal.String.Escape */
.codehilite .sh {
color: #ba2121;
} /* Literal.String.Heredoc */
.codehilite .si {
color: #a45a77;
font-weight: bold;
} /* Literal.String.Interpol */
.codehilite .sx {
color: #008000;
} /* Literal.String.Other */
.codehilite .sr {
color: #a45a77;
} /* Literal.String.Regex */
.codehilite .s1 {
color: #ba2121;
} /* Literal.String.Single */
.codehilite .ss {
color: #19177c;
} /* Literal.String.Symbol */
.codehilite .bp {
color: #008000;
} /* Name.Builtin.Pseudo */
.codehilite .fm {
color: #00f;
} /* Name.Function.Magic */
.codehilite .vc {
color: #19177c;
} /* Name.Variable.Class */
.codehilite .vg {
color: #19177c;
} /* Name.Variable.Global */
.codehilite .vi {
color: #19177c;
} /* Name.Variable.Instance */
.codehilite .vm {
color: #19177c;
} /* Name.Variable.Magic */
.codehilite .il {
color: #666;
} /* Literal.Number.Integer.Long */
}
/* End Pygments Default Colors */
%dark-theme {
/* Begin Pygments Monokai Styles */
.codehilite .hll {
background-color: #49483e;
}
.codehilite .c {
color: #959077;
} /* Comment */
.codehilite .err {
color: #ed007e;
background-color: #1e0010;
} /* Error */
.codehilite .esc {
color: #f8f8f2;
} /* Escape */
.codehilite .g {
color: #f8f8f2;
} /* Generic */
.codehilite .k {
color: #66d9ef;
} /* Keyword */
.codehilite .l {
color: #ae81ff;
} /* Literal */
.codehilite .n {
color: #f8f8f2;
} /* Name */
.codehilite .o {
color: #ff4689;
} /* Operator */
.codehilite .x {
color: #f8f8f2;
} /* Other */
.codehilite .p {
color: #f8f8f2;
} /* Punctuation */
.codehilite .ch {
color: #959077;
} /* Comment.Hashbang */
.codehilite .cm {
color: #959077;
} /* Comment.Multiline */
.codehilite .cp {
color: #959077;
} /* Comment.Preproc */
.codehilite .cpf {
color: #959077;
} /* Comment.PreprocFile */
.codehilite .c1 {
color: #959077;
} /* Comment.Single */
.codehilite .cs {
color: #959077;
} /* Comment.Special */
.codehilite .gd {
color: #ff4689;
} /* Generic.Deleted */
.codehilite .ge {
color: #f8f8f2;
font-style: italic;
} /* Generic.Emph */
.codehilite .ges {
color: #f8f8f2;
font-weight: bold;
font-style: italic;
} /* Generic.EmphStrong */
.codehilite .gr {
color: #f8f8f2;
} /* Generic.Error */
.codehilite .gh {
color: #f8f8f2;
} /* Generic.Heading */
.codehilite .gi {
color: #a6e22e;
} /* Generic.Inserted */
.codehilite .go {
color: #66d9ef;
} /* Generic.Output */
.codehilite .gp {
color: #ff4689;
font-weight: bold;
} /* Generic.Prompt */
.codehilite .gs {
color: #f8f8f2;
font-weight: bold;
} /* Generic.Strong */
.codehilite .gu {
color: #959077;
} /* Generic.Subheading */
.codehilite .gt {
color: #f8f8f2;
} /* Generic.Traceback */
.codehilite .kc {
color: #66d9ef;
} /* Keyword.Constant */
.codehilite .kd {
color: #66d9ef;
} /* Keyword.Declaration */
.codehilite .kn {
color: #ff4689;
} /* Keyword.Namespace */
.codehilite .kp {
color: #66d9ef;
} /* Keyword.Pseudo */
.codehilite .kr {
color: #66d9ef;
} /* Keyword.Reserved */
.codehilite .kt {
color: #66d9ef;
} /* Keyword.Type */
.codehilite .ld {
color: #e6db74;
} /* Literal.Date */
.codehilite .m {
color: #ae81ff;
} /* Literal.Number */
.codehilite .s {
color: #e6db74;
} /* Literal.String */
.codehilite .na {
color: #a6e22e;
} /* Name.Attribute */
.codehilite .nb {
color: #f8f8f2;
} /* Name.Builtin */
.codehilite .nc {
color: #a6e22e;
} /* Name.Class */
.codehilite .no {
color: #66d9ef;
} /* Name.Constant */
.codehilite .nd {
color: #a6e22e;
} /* Name.Decorator */
.codehilite .ni {
color: #f8f8f2;
} /* Name.Entity */
.codehilite .ne {
color: #a6e22e;
} /* Name.Exception */
.codehilite .nf {
color: #a6e22e;
} /* Name.Function */
.codehilite .nl {
color: #f8f8f2;
} /* Name.Label */
.codehilite .nn {
color: #f8f8f2;
} /* Name.Namespace */
.codehilite .nx {
color: #a6e22e;
} /* Name.Other */
.codehilite .py {
color: #f8f8f2;
} /* Name.Property */
.codehilite .nt {
color: #ff4689;
} /* Name.Tag */
.codehilite .nv {
color: #f8f8f2;
} /* Name.Variable */
.codehilite .ow {
color: #ff4689;
} /* Operator.Word */
.codehilite .pm {
color: #f8f8f2;
} /* Punctuation.Marker */
.codehilite .w {
color: #f8f8f2;
} /* Text.Whitespace */
.codehilite .mb {
color: #ae81ff;
} /* Literal.Number.Bin */
.codehilite .mf {
color: #ae81ff;
} /* Literal.Number.Float */
.codehilite .mh {
color: #ae81ff;
} /* Literal.Number.Hex */
.codehilite .mi {
color: #ae81ff;
} /* Literal.Number.Integer */
.codehilite .mo {
color: #ae81ff;
} /* Literal.Number.Oct */
.codehilite .sa {
color: #e6db74;
} /* Literal.String.Affix */
.codehilite .sb {
color: #e6db74;
} /* Literal.String.Backtick */
.codehilite .sc {
color: #e6db74;
} /* Literal.String.Char */
.codehilite .dl {
color: #e6db74;
} /* Literal.String.Delimiter */
.codehilite .sd {
color: #e6db74;
} /* Literal.String.Doc */
.codehilite .s2 {
color: #e6db74;
} /* Literal.String.Double */
.codehilite .se {
color: #ae81ff;
} /* Literal.String.Escape */
.codehilite .sh {
color: #e6db74;
} /* Literal.String.Heredoc */
.codehilite .si {
color: #e6db74;
} /* Literal.String.Interpol */
.codehilite .sx {
color: #e6db74;
} /* Literal.String.Other */
.codehilite .sr {
color: #e6db74;
} /* Literal.String.Regex */
.codehilite .s1 {
color: #e6db74;
} /* Literal.String.Single */
.codehilite .ss {
color: #e6db74;
} /* Literal.String.Symbol */
.codehilite .bp {
color: #f8f8f2;
} /* Name.Builtin.Pseudo */
.codehilite .fm {
color: #a6e22e;
} /* Name.Function.Magic */
.codehilite .vc {
color: #f8f8f2;
} /* Name.Variable.Class */
.codehilite .vg {
color: #f8f8f2;
} /* Name.Variable.Global */
.codehilite .vi {
color: #f8f8f2;
} /* Name.Variable.Instance */
.codehilite .vm {
color: #f8f8f2;
} /* Name.Variable.Magic */
.codehilite .il {
color: #ae81ff;
} /* Literal.Number.Integer.Long */
}
/* stylelint-enable color-no-hex, no-duplicate-selectors */
@media screen {
:root:not(.color-scheme-automatic, .dark-theme) {
@extend %light-theme;
}
}
@media screen and (not (prefers-color-scheme: dark)) {
:root.color-scheme-automatic {
@extend %light-theme;
}
}
@media not screen {
:root {
@extend %light-theme;
}
}
@media screen {
:root.dark-theme {
@extend %dark-theme;
}
}
@media screen and (prefers-color-scheme: dark) {
:root.color-scheme-automatic {
@extend %dark-theme;
}
}

304
web/styles/reactions.css Normal file
View File

@@ -0,0 +1,304 @@
.message_reactions {
overflow: hidden;
user-select: none;
&:has(.message_reaction) {
margin-bottom: var(--message-box-markdown-aligned-vertical-space);
}
.message_reaction_container {
&.disabled {
cursor: not-allowed;
}
}
.message_reaction {
display: flex;
padding: 2.5px 5px 2.5px 4px;
box-sizing: border-box;
min-width: 44px;
cursor: pointer;
color: var(--color-message-reaction-text);
background-color: var(--color-message-reaction-background);
/* Use an outline around the reaction pill so that
it does not change the dimensions of the reaction-pill
box when going in and out of the .reacted state. */
outline: 1px solid var(--color-message-reaction-border);
outline-offset: -1px;
border-radius: 21px;
align-items: center;
box-shadow: inset 0 0 5px 0 var(--color-message-reaction-shadow-inner);
transition:
transform 100ms linear,
font-weight 100ms linear; /* stylelint-disable-line plugin/no-low-performance-animation-properties */
&.reacted {
color: var(--color-message-reaction-text-reacted);
background-color: var(--color-message-reaction-background-reacted);
outline-color: var(--color-message-reaction-border-reacted);
/* Make this outline thicker by half a pixel,
to make own reactions more prominent. */
outline-width: 1.5px;
outline-offset: -1.5px;
font-weight: var(--font-weight-message-reaction);
box-shadow: none;
}
&.disabled {
pointer-events: none;
}
&:hover {
background-color: var(--color-message-reaction-background-hover);
}
&:active {
transform: scale(var(--scale-message-reaction-active));
}
.emoji {
margin: 1px 3px;
/* 17px at 14px/1em */
height: 1.2143em;
width: 1.2143em;
/* Preserve the emoji's dimensions, no
matter what the flexbox does. */
flex-shrink: 0;
/* Don't inherit position: relative; from
the base .emoji class. */
position: static;
}
.emoji_alt_code {
/* Apply the same margins as on graphical emoji. */
margin: 1px 3px;
font-size: 0.8em;
}
}
.message_reaction_count {
/* 90% works out here to 12.6px */
font-size: 90%;
/* No top and bottom margin; just allow
flexbox to handle the vertical alignment. */
margin: 0 3px;
/* Set the 12.6px text on a 13px line;
the goal is to center correctly on the
vertical with square emoji, resulting in
equal space above and below the reaction
count/name.
13px at 12.6/1em */
line-height: 1.0317em;
}
.message_reaction:hover .message_reaction_count {
color: var(--color-message-reaction-button-text-hover);
}
&:hover .reaction_button {
visibility: visible;
pointer-events: all;
}
.emoji-message-control-button-container {
display: flex;
align-items: center;
}
.reaction_button {
visibility: hidden;
pointer-events: none;
/* Set top/bottom padding to accommodate borders
and padding around reaction pills. */
padding: 4px 6px;
border-radius: 21px;
color: var(--color-message-reaction-button-text);
background-color: var(--color-message-reaction-button-background);
border: 1px solid var(--color-message-reaction-button-border);
& i {
font-size: 1em;
color: var(--color-message-reaction-button-text);
}
&:hover i {
color: var(--color-message-reaction-button-text-hover);
}
&:hover {
color: var(--color-message-reaction-button-text-hover);
background-color: var(
--color-message-reaction-button-background-hover
);
border: 1px solid var(--color-message-reaction-button-border-hover);
border-color: var(--color-message-reaction-button-border-hover);
box-shadow: inset 0 0 5px 0
var(--color-message-reaction-shadow-inner);
cursor: pointer;
opacity: 1;
}
.message_reaction_count {
font-weight: 700;
color: var(--color-message-reaction-button-text);
margin-right: 0;
line-height: 14px;
}
&:hover .message_reaction_count {
color: var(--color-message-reaction-button-text-hover);
}
}
}
.active-emoji-picker-reference,
.active-playground-links-reference {
visibility: visible !important;
pointer-events: all !important;
opacity: 1 !important;
}
.emoji-picker-popover {
padding: 0;
user-select: none;
.emoji-popover {
width: 16.6667em; /* 250px at 15px/em */
.emoji-popover-category-tabs {
/* Flex needed here to work around #7511 (90% zoom issues in firefox) */
display: flex;
background-color: var(--color-background-emoji-picker-popover);
width: 100%;
box-sizing: border-box;
overflow: hidden;
.emoji-popover-tab-item {
font-size: 1.0667em; /* 16px at 15px/em */
display: inline-block;
padding-top: 0.5em; /* 8px at 16px/em */
width: 1.5625em; /* 25px at 16px/em */
height: 1.5625em; /* 25px at 16px/em */
text-align: center;
cursor: pointer;
/* Flex needed here to work around #7511 (90% zoom issues in firefox) */
flex-grow: 1;
&.active {
background-color: var(
--color-background-emoji-picker-popover-tab-item-active
);
}
}
}
.emoji-popover-emoji-map,
.emoji-search-results-container {
padding: 0;
position: relative;
overflow: hidden auto;
display: block;
width: 16.4667em; /* 247px at 15px/em */
padding-left: 0.2em; /* 3px at 15px/em */
}
.emoji-popover-emoji-map {
height: 16.6667em; /* 250px at 15px/em */
.emoji-popover-subheading {
font-weight: 600;
padding: 0.3333em 0.2em; /* 5px 3px at 15px/em */
}
}
.emoji-popover-emoji {
display: inline-block;
margin: 0;
padding: 0.4em; /* 6px at 15px/em */
cursor: pointer;
border-radius: 0.5em;
height: 1.6667em; /* 25px at 15px/em */
width: 1.6667em; /* 25px at 15px/em */
&:focus {
background-color: var(
--color-background-emoji-picker-emoji-focus
);
/* Only dark mode takes a box shadow on
the emoji-picker's focused emoji. */
box-shadow: 0 0 1px
var(--color-box-shadow-emoji-picker-emoji-focus);
outline: none;
}
&.reacted {
background-color: var(
--color-background-emoji-picker-emoji-reacted
);
border-color: var(--color-border-emoji-picker-emoji-reacted);
}
&.reacted:focus {
background-color: var(
--color-background-emoji-picker-emoji-reacted-focus
);
}
&.hide {
display: none;
}
.emoji {
height: 1.6667em; /* 25px at 15px/em */
width: 1.6667em; /* 25px at 15px/em */
}
}
.emoji-search-results-container {
/* Keep it hidden initially to avoid it taking extra height
when the emoji popover is initially rendered which can
cause the popover to render at incorrect position when
the search container is hidden `onMount`. */
display: none;
height: 18.8667em; /* 283px at 15px/em */
.emoji-popover-results-heading {
font-size: 1.1333em; /* 17px at 15px/em */
font-weight: 600;
padding: 0.2941em 0.1765em 0.1765em 0.2941em; /* 5px 3px 3px 5px at 17px/em */
}
}
}
.emoji-showcase-container {
position: relative;
background-color: var(--color-background-emoji-picker-popover);
min-height: 2.9333em; /* 44px at 15px/em */
width: 16.6667em; /* 250px at 15px/em */
border-radius: 0 0 6px 6px;
.emoji-preview {
position: absolute;
width: 2.1333em; /* 32px at 15px/em */
height: 2.1333em; /* 32px at 15px/em */
left: 0.3333em; /* 5px at 15px/em */
top: 0.4em; /* 6px at 15px/em */
margin-top: 0;
}
.emoji-canonical-name {
font-size: 1.0667em; /* 16px at 15px/em */
position: relative;
top: 0.75em; /* 12px at 16px/em */
margin-left: 3.125em; /* 50px at 16px/em */
font-weight: 600;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
}
}
.typeahead .emoji {
top: 2px;
}

1072
web/styles/recent_view.css Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,681 @@
:root {
/* Width of emoji used by user to display status. */
--user-status-emoji-width: 24px;
}
.right-sidebar {
& a:hover {
text-decoration: none;
}
}
.right-sidebar-items {
padding-left: var(--right-sidebar-padding-left);
}
.right-sidebar-title {
color: var(--color-text-sidebar-heading);
opacity: var(--opacity-sidebar-heading);
font-size: inherit;
font-weight: var(--font-weight-sidebar-heading);
letter-spacing: var(--letter-spacing-sidebar-heading);
}
#buddy_list_wrapper {
position: relative;
margin-left: 0;
overflow: auto;
}
.buddy-list-section-toggle.zulip-icon-heading-triangle-right {
transition:
opacity 140ms linear,
rotate 140ms linear;
&.rotate-icon-down {
rotate: 90deg;
}
&.rotate-icon-right {
rotate: 0deg;
}
}
.buddy-list-section-toggle {
grid-area: arrow;
justify-self: center;
color: var(--color-text-sidebar-heading);
opacity: var(--opacity-sidebar-heading-icon);
&:focus {
outline: 0;
}
&:focus-visible {
outline: 2px solid var(--color-outline-focus);
}
}
.buddy-list-section-container {
/* This establishes better visual concord with
the left sidebar's spacing between a last
channel/topic and the header that follows. */
margin-bottom: 3px;
&.no-display {
display: none;
}
}
.buddy-list-section-container.collapsed {
.buddy-list-section,
.buddy-list-user-link {
display: none;
}
}
.buddy-list-section .user_sidebar_entry,
#userlist-header {
.user-list-sidebar-menu-icon {
justify-self: stretch;
display: flex;
align-items: center;
justify-content: center;
margin: 2px 2px 2px 1px;
/* This helps horizontally align the vdots,
given the reduced margin-left above.
Vertical centering looks better with an
extra pixel of top padding in this area,
too. */
padding: 1px 0 0 1px;
border-radius: 3px;
& .zulip-icon-more-vertical {
/* 17px at 16px em */
font-size: 1.0625em;
}
&:focus-visible {
outline: 2px solid var(--color-outline-focus);
}
/*
Hover does not work for touch-based devices like mobile phones.
Hence the icons does not appear, making the user unaware of its
presence on such devices. The following media property displays the
icon by default for such behaviour.
*/
@media (hover: none) {
visibility: visible;
/* Show dots on touchscreens in a less distracting,
lighter shade. */
color: var(--color-vdots-hint);
}
}
&:hover {
.user-list-sidebar-menu-icon {
cursor: pointer;
color: var(--color-vdots-visible);
&:hover {
color: var(--color-vdots-hover);
background-color: var(--color-background-sidebar-action-hover);
}
}
}
}
.buddy-list-section {
margin: 0;
overflow-x: hidden;
list-style-position: inside; /* Draw the bullets inside our box */
line-height: var(--line-height-sidebar-row);
.user-list-sidebar-menu-icon {
visibility: hidden;
}
li:is(:hover, :focus-within, .highlighted_user) {
.user-list-sidebar-menu-icon {
visibility: visible;
}
}
& li {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
list-style-type: none;
border-radius: 4px;
padding: 0;
&:hover,
&:focus-within,
&.narrow-filter:has(a.user-presence-link:focus-visible),
&.highlighted_user {
background-color: var(--color-buddy-list-highlighted-user);
box-shadow: inset 0 0 0 1px var(--color-shadow-sidebar-row-hover);
}
&.narrow-filter:has(a.user-presence-link:focus-visible),
&.highlighted_user {
outline: 2px solid var(--color-outline-focus);
outline-offset: -2px;
}
}
& .narrow-filter {
a.user-presence-link:focus {
outline: none;
text-decoration: none;
}
}
.user-circle {
grid-area: starting-anchor-element;
place-self: center center;
/* Tighten the line-height to match the icon's size... */
line-height: 1;
/* ...which is approximately 8px at 15px/1em in Vlad's design. */
font-size: 0.5333em;
}
.user_sidebar_entry.with_avatar {
.user-profile-picture-container {
/* Establish positioning context for user circle. */
position: relative;
/* TODO: Express this as part of the grid on
.selectable_sidebar_block. */
margin-right: var(--right-sidebar-avatar-right-margin);
&:not(:has(.user-circle-offline)) .user-profile-picture {
/* The over-avatar user circle width is 15.5px at 20px/1em;
we adjust the border radius here to avoid any pixels
from the avatar bleeding through. */
border-bottom-right-radius: 0.3875em; /* 7.75px at 20px/1em */
}
}
.user-profile-picture {
/* Push back on the margin-right usually allotted here;
we put it on .user-profile-picture-container instead,
so that user circles occupy the corner of the image. */
margin-right: 0;
}
.user-circle {
position: absolute;
/* 10px at 16px/1em */
font-size: 0.625em;
bottom: -0.5px;
right: -0.5px;
background-color: var(--color-background);
/* A 1.5px border provides better results than
a 1px border, especially at smaller font sizes. */
border: 1.5px solid var(--color-background);
border-radius: 50%;
&.user-circle-offline {
display: none;
}
}
}
/* Overwrite some stray list rules (including one in left_sidebar.css) to turn color
back to the bootstrap default. */
.view-all-subscribers-link,
.view-all-users-link {
color: var(--color-text-url);
&:hover {
/* Prevent hover styles set on other rows until
the right sidebar matches the action-heading typography
of the left sidebar. */
box-shadow: none;
background-color: inherit;
color: var(--color-text-url-hover);
}
}
}
#left-sidebar-empty-list-message {
list-style-type: none;
padding-top: 10px;
text-align: center;
}
#left-sidebar-empty-list-message .empty-list-message,
.buddy-list-section .empty-list-message {
font-style: italic;
color: var(--color-text-empty-list-message);
/* Overwrite default empty list font size, to look better under the subheaders. */
/* 14px at 16px/1em */
font-size: 0.875em;
/* Override .empty-list-message !important styling */
padding: 0 !important;
text-align: left;
&:hover {
/* Prevent hover styles set on other rows. */
box-shadow: none;
background-color: inherit;
}
}
.buddy-list-subsection-header {
display: grid;
align-items: center;
grid-template:
"arrow row-content scroll-buffer" var(
--line-height-sidebar-row-prominent
)
/ var(--right-sidebar-header-icon-toggle-width) minmax(0, 1fr);
cursor: pointer;
background-color: var(--color-background);
position: sticky;
top: 0;
z-index: 1;
color: var(--color-text-default);
border-radius: 4px;
margin-right: var(--width-simplebar-scroll-hover);
transition: box-shadow 150ms ease-out;
&:hover {
background-color: var(--color-buddy-list-highlighted-user);
box-shadow: inset 0 0 0 1px var(--color-shadow-sidebar-row-hover);
.buddy-list-section-toggle,
.buddy-list-heading {
opacity: var(--opacity-sidebar-heading-hover);
}
/* Combine hover border with drop shadow since CSS box-shadow doesn't stack. */
&.sidebar-header-drop-shadow {
box-shadow:
inset 0 0 0 1px var(--color-shadow-sidebar-row-hover),
var(--box-shadow-sidebar-sticky-header-drop);
}
}
&.sidebar-header-drop-shadow {
box-shadow: var(--box-shadow-sidebar-sticky-header-drop);
}
}
.buddy-list-heading {
user-select: none;
margin: 0;
padding: 5px 5px 5px 0;
color: var(--color-text-sidebar-heading);
font-size: inherit;
font-weight: var(--font-weight-sidebar-heading);
letter-spacing: var(--letter-spacing-sidebar-heading);
opacity: var(--opacity-sidebar-heading);
transition: opacity 140ms linear;
grid-area: row-content;
display: flex;
}
.buddy-list-heading-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 2px;
}
.buddy-list-heading-user-count-with-parens {
opacity: 0.75;
}
.buddy-list-subsection-header.no-display {
display: none;
}
.user-presence-link,
.user_sidebar_entry .selectable_sidebar_block {
overflow: hidden;
color: var(--color-text-sidebar-row);
.user-name {
overflow: hidden;
text-overflow: ellipsis;
}
}
.user_sidebar_entry .selectable_sidebar_block {
grid-area: row-content;
display: grid;
grid-template:
"starting-anchor-element row-content markers-and-controls" var(
--line-height-sidebar-row
)
". row-content ." auto / var(
--right-sidebar-presence-circle-width
)
minmax(0, 1fr)
minmax(0, auto);
align-items: baseline;
}
.user_sidebar_entry.with_avatar {
grid-template:
"row-content" var(--line-height-sidebar-row-with-avatars)
"row-content" auto / minmax(0, 1fr);
.selectable_sidebar_block {
grid-template-rows: auto;
padding: 4px;
padding-left: calc(var(--right-sidebar-header-icon-toggle-width) / 2);
}
.avatar-preload-background {
background-color: var(--color-buddy-list-avatar-loading);
}
.unread_count:not(.hide) {
margin-right: 2px;
}
&.with_status .unread_count {
align-self: baseline;
}
}
.user-presence-link {
user-select: text;
grid-area: row-content;
&:hover,
&:focus {
color: var(--color-text-sidebar-row);
text-decoration: none;
}
}
.information-settings .profile-with-avatar,
.user_sidebar_entry.with_avatar .selectable_sidebar_block {
line-height: var(--line-height-sidebar-row-with-avatars);
display: grid;
grid-template:
"avatar row-content markers-and-controls" var(
--right-sidebar-avatar-width
)
/ auto minmax(0, 1fr) minmax(0, auto);
justify-content: flex-start;
align-items: center;
}
.information-settings .profile-with-avatar {
margin: 5px 0;
}
.my_user_status {
opacity: 0.5;
white-space: nowrap;
}
.selectable_sidebar_block {
cursor: pointer;
}
.user_list_style_values {
.user-name-and-status-emoji {
display: flex;
align-items: center;
width: 100%;
.user-name {
display: inline-block;
max-width: calc(100% - var(--user-status-emoji-width));
overflow-x: hidden;
text-overflow: ellipsis;
}
.status-emoji {
line-height: 20px;
margin-left: 6px;
}
}
.status-text {
overflow-x: hidden;
text-overflow: ellipsis;
}
}
.user_sidebar_entry {
display: grid;
/* We establish a two-row, two-column outer grid so
that controls remain aligned with the username,
even when there is a status line shown below.
The 25px column for the vdots is less than the
30px allotted in the left sidebar, but it holds
the username area constant, so that no ellipsis
appears on the username on hover. The 25px value
is necessary for correct vdots alignment with the
filter row's vdots. */
grid-template:
"row-content ending-anchor-element" var(--line-height-sidebar-row)
"row-content ." auto / minmax(0, 1fr) var(--right-sidebar-vdots-width);
align-content: baseline;
margin-right: var(--width-simplebar-scroll-hover);
/* When both the left circle and three dot menu are present, we want
the space to the left of the circle to be more similar to the space
to the right of the three dot menu. */
&:not(.with_avatar) {
padding-left: calc(var(--right-sidebar-toggle-width-offset) - 2px);
}
&:hover {
cursor: pointer;
}
.user-name-and-status-emoji {
display: flex;
}
.status-text {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
opacity: 0.75;
font-size: 90%;
}
& span.status-text:not(:empty) {
/* Cinch up the status text by one quarter of the
sidebar row's line-height. */
margin-top: calc((var(--line-height-sidebar-row) / 4) * -1);
}
.unread_count {
grid-area: markers-and-controls;
align-self: center;
display: none;
}
.unread_count:not(.hide) {
display: block;
margin-left: 4px;
&:empty {
/* Hide otherwise empty unread count pill
after DMs have been read, but before the
count has been removed from the DOM. */
display: none;
}
}
}
#userlist-toggle {
text-align: center;
vertical-align: middle;
}
#userlist-toggle-button {
text-decoration: none;
color: hsl(0deg 0% 60%);
&:hover {
color: inherit;
}
}
.right-sidebar-items:first-of-type #userlist-header {
border-top: none;
}
#userlist-header {
cursor: pointer;
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
padding-left: var(--right-sidebar-heading-left-spacing);
/* The scrollbar doesn't extend this high, but we want the three-dot
menus to line up. */
padding-right: var(--width-simplebar-scroll-hover);
padding-bottom: var(--sidebar-filter-bottom-spacing);
background-color: var(--color-background);
#buddy-list-menu-icon {
color: var(--color-vdots-visible);
width: var(--right-sidebar-vdots-width);
/* Push back against default right-sidebar
spacing for better vdots alignment. */
padding: 0;
margin: 2px;
&:hover {
color: var(--color-vdots-hover);
background-color: var(--color-background-sidebar-action-hover);
}
}
}
.buddy-list-user-link,
.invite-user-shortcut {
margin-right: var(--width-simplebar-scroll-hover);
border-radius: 4px;
&:hover {
background: var(--color-background-sidebar-action-heading-hover);
box-shadow: inset 0 0 0 1px var(--color-shadow-sidebar-row-hover);
color: var(--color-text-sidebar-action-heading-hover);
}
.right-sidebar-wrappable-text-container {
display: grid;
grid-template-rows: minmax(
var(--line-height-sidebar-row-prominent),
auto
);
align-items: center;
color: var(--color-text-sidebar-action-heading);
.right-sidebar-wrappable-text-inner {
margin: var(--spacing-top-bottom-sidebar-topic-inner) 0;
line-height: 1;
text-decoration: none;
font-size: var(--font-size-sidebar-action-heading);
font-weight: var(--font-weight-sidebar-action-heading);
font-variant: var(--font-variant-sidebar-action-heading);
font-feature-settings: var(
--font-feature-settings-sidebar-action-heading
);
}
text-transform: var(--text-transform-sidebar-action-heading);
}
}
#user-list.with_avatars
.buddy-list-user-link
.right-sidebar-wrappable-text-container,
#user-list.with_avatars .buddy-list-section .empty-list-message {
margin-left: var(--right-sidebar-text-indent-with-avatar);
}
.buddy-list-user-link .right-sidebar-wrappable-text-container,
.buddy-list-section .empty-list-message {
margin-left: var(--right-sidebar-text-indent-without-avatar);
}
.invite-user-shortcut {
/* The margin top is calculated from a legacy 25px height,
from a 20px line of text and 5px of margin top. We calculate
a scaling margin-top by subtracting the em-unit line height
from the legacy value. */
margin-top: calc(25px - (var(--legacy-body-line-height-unitless) * 1em));
margin-bottom: var(--sidebar-bottom-spacing);
.invite-user-link {
/* TODO: We should eventually change the grid to use the correct
--right-sidebar-presence-circle-width with left spacing, and
share that left spacing value here. */
padding-left: calc(var(--right-sidebar-toggle-width-offset) + 0.3em);
text-decoration: none;
}
}
.invite-user-shortcut,
.view-all-users-link,
.view-all-subscribers-link {
&:has(:focus-visible) {
/* Apply focus and active styles to the parent container so the
entire invite-user row receives keyboard focus indication,
matching the behavior of other interactive sidebar rows.
We use :has with :focus-visible so that mouse-clicks
do not trigger the outline. */
outline: 2px solid var(--color-outline-focus);
outline-offset: -2px;
background-color: var(--color-background-hover-narrow-filter);
}
& a:focus {
outline: none;
text-decoration: none;
}
}
#user-list.with_avatars .invite-user-link {
padding-left: calc(var(--right-sidebar-header-icon-toggle-width) / 2);
}
#buddy-list-actions-menu-popover {
.display-style-selector + .invite-user-link-item {
border-top: 1px solid var(--color-border-sidebar-subheader);
}
/* No hover effect on the label */
.display-style-selector-header:hover {
background: inherit;
cursor: default;
}
.popover-menu-link:not(.display-style-selector-header) {
display: grid;
grid-template: "left-icon row-content" auto / 18px minmax(
max-content,
1fr
);
align-items: center;
.popover-menu-icon,
.user_list_style_choice {
grid-area: left-icon;
justify-self: baseline;
}
.popover-menu-label {
grid-area: row-content;
}
}
}
#buddy-list-loading-subscribers {
margin: auto;
padding-right: var(--width-simplebar-scroll-hover);
padding-top: 30px;
}

View File

@@ -0,0 +1,42 @@
#reminders-overlay-container,
#scheduled_messages_overlay_container {
.no-overlay-messages {
display: none;
&:only-child {
display: block;
}
}
}
#scheduled_message_indicator {
display: block;
margin-left: 10px;
font-style: italic;
color: hsl(0deg 0% 53%);
}
#reminders-overlay-container .message_content {
cursor: auto;
}
.schedule-reminder-note {
resize: none;
width: 100%;
min-width: 10em;
padding: var(--schedule-reminder-note-padding);
/* Set the minimum height such that we start out with 2 lines of text. */
min-height: calc(2em * var(--base-line-height-unitless));
/* Set the maximum height of textarea to be enough for 10 lines of text. */
max-height: calc(
(10em * var(--base-line-height-unitless)) +
(2 * var(--schedule-reminder-note-padding))
);
overflow-y: auto;
background-color: var(--background-color-textarea);
font-size: 1em;
line-height: var(--base-line-height-unitless);
font-family: inherit;
}

474
web/styles/search.css Normal file
View File

@@ -0,0 +1,474 @@
#searchbox {
width: 100%;
height: var(--header-height);
.navbar-search {
margin: 0.2187em 0; /* 3.5px at 16px em */
border-radius: 5px;
position: absolute;
overflow: hidden;
/* We bump the z-index to keep the search box
clickable despite position-based layout
adjustments. */
z-index: 1;
}
.navbar-search:has(#searchbox-input-container:focus-visible) {
outline: 2px solid var(--color-outline-focus);
}
.search_icon {
grid-area: search-icon;
align-self: center;
text-decoration: none;
padding: 0 0.4117em; /* 7px at 17px em */
font-size: 1.0625em; /* 17px at 16px em */
border: none;
background-color: transparent;
color: var(--color-search-icons);
opacity: 0.5;
cursor: default;
&:active,
&:focus {
outline: none;
}
&:disabled {
visibility: hidden;
}
}
.search_close_button {
grid-area: search-close;
width: 1.75em; /* 28px at 16px em */
height: 100%;
background: none;
border: none;
opacity: 0.5;
line-height: 0;
border-radius: 4px;
/* Reset iOS button defaults. */
color: var(--color-search-icons);
padding: 0;
outline: none;
font-size: inherit;
&:hover {
opacity: 1;
}
&:focus-visible {
outline: 2px solid var(--color-outline-focus);
outline-offset: -3px;
}
&:disabled {
visibility: hidden;
}
}
.search-input-and-pills {
grid-area: search-pills;
display: flex;
/* Match the pills' single-line centering slack so wrapped rows
get the same breathing room without changing the single-line
box height. */
padding: calc((var(--search-box-height) - var(--height-input-pill)) / 2)
0;
flex-wrap: wrap;
gap: 0.3571em; /* 5px at 14px em, matches pill spacing in typeahead dropdown */
align-self: center;
}
.navbar-search:not(.expanded) {
right: 0;
background-color: var(--color-background-search-collapsed);
.search_close_button {
display: none;
}
#searchbox-input-container {
width: var(--search-box-width);
@media (width < $md_min) {
/* Square off the icon-only presentation. */
width: var(--search-box-height);
grid-template:
"search-icon search-pills" var(--search-box-height)
/ var(--search-box-height) 0;
column-gap: 0;
}
}
.search-input {
border-radius: 5px;
color: var(--color-text-search);
box-shadow: none;
}
&:hover {
cursor: pointer;
@media (width >= $md_min) {
box-shadow:
0 4px 20px var(--color-search-box-hover-shadow),
0 1px 5px var(--color-search-box-hover-shadow);
}
.search_icon {
cursor: pointer;
}
}
@media (width < $md_min) {
.search-input {
opacity: 0;
}
}
@media (height < $short_navbar_cutoff_height) {
#searchbox-input-container .search_icon {
font-size: 1.125em; /* 18px at 16px/em */
}
}
}
.navbar-search.expanded {
width: 100%;
box-shadow:
0 4px 20px var(--color-search-shadow-wide),
0 1px 5px var(--color-search-shadow-tight);
.search-input {
cursor: text;
}
#searchbox-input-container.pill-container {
/* Pill container should display the same background
color as the search typeahead. */
background-color: var(--color-background-search);
}
@media (width < $md_min) {
/* 3px chosen so that the cursor clicks the search button
and close button from the same position. */
width: calc(100% - 3px);
/* z-index to not see the gear icon underneath */
z-index: 1;
}
}
.typeahead.dropdown-menu {
/* Match the typeahead's width to its parent container. */
right: 0;
top: 0;
margin-top: 0;
padding-top: 0;
box-shadow: none;
position: relative;
width: 100%;
background: var(--color-background-search);
color: var(--color-text-search);
border-width: 0;
border-top-width: 1px;
border-top-color: var(--color-search-dropdown-top-border);
border-radius: 0 0 4px 4px;
.active > a {
background-color: var(--color-background-search-option-hover);
background-image: none;
color: var(--color-text-search-hover);
}
.typeahead-menu .simplebar-content {
min-width: unset;
}
.typeahead-menu .simplebar-content > li > a {
max-width: none;
}
}
.input-append {
position: relative;
width: 100%;
box-sizing: border-box;
margin-bottom: 0;
}
.search-input {
/* Avoid massive inheritance chain on font-size. */
font-size: var(--base-font-size-px);
/* override bootstrap padding for input[type="text"] */
padding: 0;
border: none;
outline: none;
border-radius: 0;
font-weight: 400;
background: transparent;
text-overflow: ellipsis;
overflow: hidden;
white-space: pre;
flex-grow: 1;
color: var(--color-text-search);
display: block;
align-self: center;
&:empty::before {
content: attr(data-placeholder-text);
color: var(--color-text-search-placeholder);
}
}
#searchbox-input-container .user-pill-container,
.typeahead-menu .user-pill-container {
border: 1px solid transparent;
> .pill-label {
min-width: fit-content;
white-space: nowrap;
width: fit-content;
/* Replaced by the 5px gap. */
margin-right: 0;
}
.pill {
height: var(--length-user-pill-height);
}
.pill-image {
height: var(--length-search-pill-image);
width: var(--length-search-pill-image);
}
.pill-image-border {
border: none;
}
.pill:not(.deactivated-pill) {
background-color: var(--color-background-user-pill);
}
}
#searchbox-input-container {
display: grid;
/* The next two styles override .input-append style from app_components.js */
letter-spacing: normal;
font-size: 100%;
/* Override style for .pill-container that isn't relevant for search. */
border: none;
grid-template:
"search-icon search-pills search-close" var(--search-box-height)
". search-pills ." auto / var(--search-left-padding) minmax(0, 1fr)
1.75em; /* 28px at 16px em */
align-content: center;
cursor: pointer;
/* Override styles for .pill-container that aren't relevant for search. */
&.pill-container {
background: inherit;
gap: 0;
/* Override padding. */
padding: 0;
}
.pill {
margin: 0;
min-width: unset;
background-color: var(--color-background-input-pill-search);
}
&:not(.focused) {
height: var(--search-box-height);
overflow: hidden;
.search-input-and-pills {
flex-wrap: nowrap;
overflow: hidden;
@media (height < $short_navbar_cutoff_height) {
line-height: var(--base-line-height-unitless);
}
}
}
&.focused .user-pill-container {
flex-flow: row wrap;
}
.user-pill-container {
gap: 2px 5px;
/* Don't enforce a height, as user-pill containers
can contain multiple user pills that wrap onto
new lines. */
height: unset;
/* Not focus-visible, because we want to support mouse+backpace
to delete pills */
&:focus {
/* Unlike regular `.pill` this multi-user pill has a border,
so we use border instead of box-shadow on focus. */
box-shadow: none;
border-color: var(--color-focus-outline-input-pill);
}
> .pill-label {
/* Don't inherit large line-height for user pill labels. */
line-height: 1.1;
}
.pill {
border: none;
/* Match border radius to image */
border-radius: 4px;
max-width: none;
/* Set the minimum width on the pill container;
this accommodates the avatar, a minimum
two-character username, and the closing X.
90px at 20px/1em.
TODO: This would ideally be reworked, as we need to
override it for search suggestion pills (with no X) below.
*/
min-width: 4.5em;
display: grid;
grid-template-columns:
var(--length-search-pill-image) minmax(0, 1fr)
calc(
var(--length-input-pill-exit) +
var(--length-input-pill-exit-margin)
);
align-content: center;
/* Don't inherit large line-height for user pills themselves, either. */
line-height: 1.1;
}
}
}
@media (width >= $md_min) {
.navbar-search {
background: var(--color-background-search);
}
.navbar-search:not(.expanded) {
box-shadow: 0 0 2px var(--color-search-box-hover-shadow);
}
}
@media (width < $md_min) {
.navbar-search:not(.expanded) .search_icon:hover {
opacity: 1;
}
.navbar-search.expanded {
background: var(--color-background-search);
}
}
@media (height < $short_navbar_cutoff_height) {
#searchbox_form:not(.expanded) {
margin: 0;
/* Now that the header is shorter, the search box will take up the whole
height (which looks weird), so add 1px of space above and below it
by manipulating the margin and the height. */
margin-top: 1px;
height: calc(var(--header-height) - 3px);
}
/* It looks fine to fill the navbar when the typeahead is open. */
#searchbox_form.expanded {
margin-top: 1px;
}
}
@media (width < $sm_min) {
#searchbox_form.expanded {
position: fixed;
left: 0;
right: 9px;
width: 100%;
border-radius: 0;
box-shadow: none;
/* To be visible over `.left-sidebar-toggle-unreadcount` */
z-index: 20;
}
}
.typeahead-menu .simplebar-content > li > a {
padding: 3px var(--search-left-padding);
/* Override white-space: nowrap from zulip.css */
white-space: normal;
.search_list_item {
max-width: 100%;
display: flex;
gap: 5px;
align-items: center;
}
.search_list_item .description {
margin: 2px 0;
}
.search_list_item .pill-container {
margin: 2px 0;
/* This contains only one pill, which handles its own border */
border: none;
cursor: pointer;
padding: 0;
max-width: 100%;
}
.pill {
align-items: baseline;
margin: 0;
/* We remove the close button's column space from the grid template
for search suggestions, since there's no exit button.
The min-width here prevents extra space on very short names. */
min-width: 0;
grid-template-columns: var(--length-search-pill-image) minmax(
0,
1fr
);
/* We don't show a border for focused pills
in the search typeahead */
&:focus {
/* Regular pills use box-shadow. */
box-shadow: none;
/* User pills use border */
border-color: var(--color-background-input-pill);
.pill-image-border {
border-color: var(--color-border-input-pill-image);
}
}
.exit {
display: none;
}
}
.user-pill-container {
gap: 5px;
}
.pill-image {
/* Add line-height equal to height to mimic baseline alignment. */
line-height: var(--height-input-pill);
align-self: center;
}
}
/* Break really long words (unlikely to happen). */
.search_list_item span {
overflow-wrap: anywhere;
}
}

2538
web/styles/settings.css Normal file

File diff suppressed because it is too large Load Diff

1777
web/styles/subscriptions.css Normal file

File diff suppressed because it is too large Load Diff

123
web/styles/tooltips.css Normal file
View File

@@ -0,0 +1,123 @@
[data-tippy-root] {
/* Since tooltip elements are sometimes inside elements
* which have different font-family, we force font-family
* for tooltips here.
*/
font-family: "Source Sans 3 VF", sans-serif !important;
overflow-wrap: anywhere;
/* Contains stylistic variant of upper-case character "I" in Source Sans 3 */
font-feature-settings: "ss01" on;
/* Affects all tippy tooltips not using any theme. */
.tippy-box:not([data-theme]) {
background: var(--color-background-tippy-box);
border-radius: 5px;
/* 14px at 14px/1em */
font-size: var(--base-font-size-px);
/* 25px at 14px/1em */
min-height: 1.7857em;
box-sizing: border-box;
.tippy-content {
box-sizing: inherit;
display: flex;
align-items: center;
padding: 5px 10px;
/* 14px at 14px/1em */
font-size: var(--base-font-size-px);
/* 20px at 14px/1em */
line-height: 1.4286em;
/* Don't inherit font-weight when
a Tippy attaches to a parent */
font-weight: normal;
color: hsl(0deg 0% 100%);
}
.tippy-arrow::before {
/* This affects the arrow for tooltips and doesn't have any impact on arrows for popovers.
* The transform property scales down the size of the arrow, giving it a thinner appearance.
* https://atomiks.github.io/tippyjs/v6/themes/#changing-the-arrow-size
*/
transform: scale(0.75);
}
.tooltip-inner-content {
/* 17px at 14px/1em */
line-height: 1.2143em;
}
&[data-placement^="top"] > .tippy-arrow::before {
border-top-color: var(--color-background-tippy-box);
}
&[data-placement^="bottom"] > .tippy-arrow::before {
border-bottom-color: var(--color-background-tippy-box);
}
&[data-placement^="left"] > .tippy-arrow::before {
border-left-color: var(--color-background-tippy-box);
}
&[data-placement^="right"] > .tippy-arrow::before {
border-right-color: var(--color-background-tippy-box);
}
}
.tippy-arrow::before {
/* `.tippy-arrow:before` element sometimes
* inherits the height of the parent, we
* don't want any height here since we
* want it to remain an triangle.
* The bug was only found in Firefox.
*/
height: 0 !important;
}
/* If the text in the tooltips stretches to multiple lines,
* we want the lines to be left-indented and not right-indented
* by default.
*/
text-align: left;
.tooltip-hotkey-hints {
box-sizing: inherit;
display: flex;
align-self: flex-start;
margin: 0 -5px 0 10px;
gap: 4px;
}
& span.tooltip-hotkey-hint {
border: 1px solid hsl(225deg 100% 84%);
border-radius: 3px;
color: hsl(225deg 100% 84%);
padding: 0 5px;
text-align: center;
white-space: nowrap;
}
.view-channel-tooltip {
/* This is needed so that the hotkey hints do not
move to a new line after "View #{channel_name}". */
display: inline-flex;
.view-channel-text {
word-break: break-all;
}
}
/* If the reference of the tooltip is went offscreen while scrolling,
the tooltip will be visible until the scrolling stops. Popper adds
`data-reference-hidden` attribute to those tooltips whole reference is
hidden. Since tooltips have `absolute` position, they can occupy scroll
space offscreen and cause document to have the wrong scroll height.
Setting `display: none` makes sure it doesn't occupy any offscreen space.
NOTE: This data attribute has changed each time in the past 3 releases and
if we upgrade tippy from v6, we should check if this attribute is still valid.
See https://github.com/atomiks/tippyjs/issues/555 for some discussion on this.
*/
[data-reference-hidden]:not(.show-when-reference-hidden) {
display: none;
}
}

262
web/styles/typeahead.css Normal file
View File

@@ -0,0 +1,262 @@
/* CSS for Bootstrap typeahead */
.dropdown-menu {
display: none;
min-width: 160px;
list-style: none;
}
.open > .dropdown-menu {
display: block;
}
.typeahead {
z-index: 1051;
}
.typeahead.dropdown-menu .typeahead-menu .simplebar-content {
min-width: max-content;
& > li {
overflow-wrap: anywhere;
& > a {
display: flex;
align-items: center;
padding: 0.2142em 0.7142em; /* 3px 10px at 14px em */
gap: 0.3571em; /* 5px at 14px em */
font-weight: normal;
/* We want to keep this `max-width` less than 320px. */
max-width: 292px;
line-height: 1.43; /* 20px / 14px */
color: var(--color-dropdown-item);
white-space: nowrap;
@media (width >= $ml_min) {
/* Scale up with font size on larger widths. */
/* 292px / 14px */
max-width: 20.86em;
}
/* hidden text just to maintain line height for a blank option */
strong:empty {
&::after {
content: ".";
visibility: hidden;
}
}
&:hover,
&:focus {
text-decoration: none;
outline: 0;
}
&.topic-typeahead-link {
gap: 5px;
}
&.topic-edit-typeahead {
display: flex;
flex-wrap: wrap;
.typeahead-strong-section {
/* We want to wrap the topic but preserve
original white spaces sequence too. */
white-space: pre-wrap;
}
}
.typeahead-content {
display: flex;
align-items: center;
overflow: hidden;
gap: 0.3571em;
}
.typeahead-text-container {
display: flex;
align-self: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
gap: 3px;
align-items: baseline;
}
.compose-stream-name {
overflow: visible;
gap: 0;
}
}
.stream-to-topic-arrow {
margin-left: 0.375em; /* 6px at 16px em */
cursor: default;
color: var(--color-compose-chevron-arrow);
text-decoration: none;
}
}
.active > a {
&,
&:hover,
&:focus {
color: var(--color-active-dropdown-item);
background-color: var(--background-color-active-typeahead-item);
}
& .user-circle-offline {
/* Ensure better contrast on highlighted
typeahead items. */
color: var(--color-user-circle-offline-typeahead-highlight);
}
}
.pronouns,
.autocomplete_secondary {
align-self: baseline;
opacity: 0.8;
font-size: 85%;
overflow: hidden;
text-overflow: ellipsis;
& > a {
color: var(--color-dropdown-item);
text-decoration: underline 1px;
text-decoration-color: var(--color-dropdown-item-link-underline);
opacity: 0.6;
}
}
.autocomplete_secondary {
flex: 1 1 0;
}
.active .pronouns,
.active .autocomplete_secondary {
opacity: 1;
}
}
.typeahead.dropdown-menu {
.typeahead-menu {
list-style: none;
margin: 4px 0;
max-height: min(248px, 95vh);
overflow-y: auto;
}
.typeahead-footer {
margin: 0;
padding: 4px 10px;
border-top: 1px solid hsl(0deg 0% 0% / 20%);
display: flex;
align-items: center;
}
#typeahead-footer-text {
color: var(--color-dropdown-item);
font-size: 0.8571em; /* 12px at 14px/em */
}
a strong.typeahead-strong-section {
/* Can't use flex to display this item if
we want `text-overflow: ellipsis` to work
which only works on block elements. */
overflow: hidden;
text-overflow: ellipsis;
white-space: pre;
align-items: baseline;
font-weight: 500;
.decorated-channel-name-wrapper {
display: inline-flex;
max-width: 100%;
.decorated-channel-name {
overflow: hidden;
text-overflow: ellipsis;
}
}
}
}
.typeahead-option-label-container {
display: flex !important;
justify-content: space-between;
> strong {
margin-right: 14px;
}
.typeahead-option-label {
color: var(--color-typeahead-option-label);
}
}
.typeahead-image.zulip-icon {
display: flex;
justify-content: center;
align-items: center;
}
/* For FontAwesome icons and zulip icons used in place of images for some users. */
.typeahead-image {
flex: 0 0 auto;
position: relative;
height: 1.3125em; /* 21px at 16px/1em */
width: 1.3125em; /* 21px at 16px/1em */
border-radius: 4px;
.typeahead-image-avatar {
border-radius: 4px;
/* Override bootstrap img */
vertical-align: baseline;
}
.user-circle {
position: absolute;
/* This is smaller than the sidebar because avatars here are smaller */
font-size: 0.4375em; /* 7px at 16px/1em */
line-height: 1;
bottom: -0.5px;
right: -0.5px;
background-color: var(--color-background-dropdown-widget);
padding: 1px;
border-radius: 50%;
&.user-circle-offline {
display: none;
}
}
/* Move the background styles onto the :before element so that the 1px padding
outline still works. */
.user-circle-idle {
background: var(--color-background-dropdown-widget);
}
.user-circle-idle::before {
background: var(--gradient-user-circle-idle);
background-clip: text;
-webkit-text-fill-color: transparent;
border-radius: 50%;
}
}
.typeahead-text-container {
i.zulip-icon-bot {
align-self: center;
}
/*
Negates the extra 3px left margin from `.status-emoji` in zulip.css, which,
combined with .typeahead-text-container's `gap`, created unintended spacing.
The typeahead's spacing is now managed only by `gap` in
.typeahead-text-container.
*/
.status-emoji {
margin-left: 0;
}
}

View File

@@ -0,0 +1,18 @@
#typing_notifications {
display: none;
margin-left: 10px;
font-style: italic;
color: hsl(0deg 0% 53%);
}
#typing_notification_list {
list-style: none;
margin: 0;
}
#mark_as_read_turned_off_banner {
/* override the margin of main-view-banner since it causes
more gap than we want between mark as read banner and typing
notification. */
margin: 5px 0;
}

View File

@@ -0,0 +1,21 @@
.user-circle-active {
color: var(--color-user-circle-active);
}
.user-circle-idle {
background: var(--gradient-user-circle-idle);
background-clip: text;
-webkit-text-fill-color: transparent;
}
.with_avatar .user-circle-idle {
background: var(--gradient-user-circle-idle-avatar);
}
.user-circle-offline {
color: var(--color-user-circle-offline);
}
.user-circle-deactivated {
color: var(--color-user-circle-deactivated);
}

103
web/styles/user_status.css Normal file
View File

@@ -0,0 +1,103 @@
#set-user-status-modal {
/* A narrower width is more attractive for this modal. */
width: 384px;
.user-status-content-wrapper {
display: grid;
grid-template:
"status-icon search-input clear-search" auto / auto minmax(0, 1fr)
auto;
align-items: center;
outline: 1px solid hsl(0deg 0% 0% / 60%);
outline-offset: -1px;
border-radius: 4px;
background-color: var(--color-background-input);
& input.user-status {
grid-area: search-input;
line-height: 1.25em; /* 20px at 16px/em */
padding: 0.5em 0.125em; /* 8px 2px at 16px/1em */
width: 100%;
/* Override default input height */
height: unset;
border: none;
outline: none;
box-shadow: none;
/* Transparent here is to allow the input
background set on the wrapper to show
through. */
background-color: transparent;
margin: 0;
&:focus {
box-shadow: none;
}
}
#clear_status_message_button {
grid-area: clear-search;
padding: 0.5em; /* 8px at 16px/1em */
margin: 0.125em; /* 2px at 16px/1em */
}
.status-emoji-wrapper {
display: flex;
padding: 0.375em; /* 6px at 16px/1em */
margin: 0.125em; /* 2px at 16px/1em */
border-radius: 4px;
cursor: pointer;
.selected-emoji,
.smiley-icon {
width: 1.25em; /* 20px at 16px/em */
height: 1.25em; /* 20px at 16px/em */
cursor: pointer;
}
.smiley-icon {
display: flex;
align-items: center;
justify-content: center;
color: var(--color-modal-selectable-icon);
&:hover {
text-decoration: none;
}
}
&:hover,
&.active-emoji-picker-reference {
background-color: var(
--background-color-modal-selectable-icon-hover
);
.smiley-icon {
color: var(--color-modal-selectable-icon-hover);
}
}
}
}
.user-status-options {
padding-top: 15px;
.user-status-value {
/* We set the gap between the status emoji and text
in the default user status options to match the gap
between the corresponding emoji and text of the
custom user status input.
6px (emoji button padding)
+ 2px (emoji button margin)
+ 2px (input padding) */
gap: 0.625em; /* 10px at 16px/em */
.status-emoji {
/* Size and align status emoji to match
the top line of the modal. */
height: 1.25em; /* 20px at 16px/em */
width: 1.25em; /* 20px at 16px/em */
margin: 0;
}
}
}
}

368
web/styles/widgets.css Normal file
View File

@@ -0,0 +1,368 @@
.widget-content {
margin-bottom: var(--markdown-interelement-space-px);
}
.widget-choices {
& ul {
padding: 3px;
}
& li {
padding: 2px;
list-style: none;
}
& button {
font-weight: 700;
color: hsl(240deg 100% 50%);
font-family: inherit;
font-size: inherit;
}
.widget-choices-heading {
font-weight: 600;
}
}
.todo-widget {
.todo-task-list-title-bar {
flex: 1 1 auto;
display: flex;
/* Ensure controls remain visible on narrower screens. */
flex-flow: row wrap;
gap: 5px;
margin-bottom: var(--markdown-interelement-space-px);
}
.add-task-bar {
display: flex;
/* Ensure controls remain visible on narrower screens. */
flex-flow: row wrap;
gap: 5px;
}
/* For the box-shadow to be visible on the left */
.add-task,
.add-desc {
font-weight: 400;
}
& label.checkbox {
display: flex; /* Arrange that a multi-line description line wraps properly. */
/* Keep checkboxes vertically aligned, including with multi-line tasks. */
align-items: baseline;
/* Reset default label.checkbox styles. */
gap: 5px;
position: static;
min-height: 0;
& input[type="checkbox"] {
~ .custom-checkbox {
display: inline-block;
vertical-align: middle;
position: static;
padding: 2px;
margin: 0;
font-size: 1.3em; /* 18.2px / 14px em */
height: 0.6593em; /* 12px at 18.2px / em */
width: 0.6593em; /* 12px at 18.2px / em */
font-weight: 300;
line-height: 0.8;
text-align: center;
border: 2px solid var(--color-border-todo-checkbox);
border-radius: 4px;
filter: brightness(1);
cursor: pointer;
}
&:checked ~ .custom-checkbox {
background-image: url("../images/checkbox-white.svg");
background-size: 75%;
background-position: 50% 50%;
background-repeat: no-repeat;
background-color: var(--color-background-todo-checkbox-checked);
border-color: var(--color-border-todo-checkbox-checked);
}
&:disabled ~ .custom-checkbox {
opacity: 0.5;
cursor: not-allowed;
}
&:hover ~ .custom-checkbox {
border-color: var(--color-border-todo-checkbox-hover);
}
&:not(:checked):hover ~ .custom-checkbox {
/* We only change the background color on hover,
when the checkbox is not checked. */
background-color: var(--color-background-todo-checkbox-hover);
}
&:focus ~ .custom-checkbox {
outline-color: hsl(0deg 0% 100% / 0%);
}
}
}
}
.todo-widget,
.poll-widget {
.poll-question-header,
.todo-task-list-title-header {
font-size: 1.1em;
font-weight: 600;
/* Zero out the margin inherited from Markdown styles;
we place the margin here on the header-area class,
making alignment of the flex items more predictable. */
margin-bottom: 0;
}
& li {
display: flex;
gap: 5px;
align-items: baseline;
list-style: none;
margin: 0 0 5px;
}
& ul {
margin: 0 0 5px;
padding: 0;
}
& input[type="text"] {
/* Reset from zulip.css */
height: unset;
border: 1px solid hsl(0deg 0% 80%);
box-shadow: inset 0 1px 1px hsl(0deg 0% 0% / 7.5%);
border-radius: 4px;
color: var(--color-text-default);
font-family: inherit;
&:focus {
border-color: hsl(206deg 80% 62% / 80%);
outline: 0;
box-shadow: none;
background-color: var(--color-background-widget-input);
transition:
border-color linear 0.2s,
box-shadow linear 0.2s;
}
}
}
.poll-widget {
.poll-option-bar {
display: flex;
/* Ensure controls remain visible on narrower screens. */
flex-flow: row wrap;
gap: 5px;
}
.poll-option {
font-weight: 400;
}
.poll-option-label {
display: flex;
gap: 5px;
align-items: baseline;
}
.poll-option-text {
font-weight: 600;
/* Start with max-content, but allow options
to shrink, so that voting names wrap comfortably. */
flex: 0 1 max-content;
}
.poll-vote {
color: var(--color-poll-vote);
border-color: var(--color-border-poll-vote);
border-style: solid;
font-weight: 600;
border-radius: 3px;
/* We don't want poll-vote tallies to spill
digits onto second lines in narrow viewports. */
flex-shrink: 0;
min-width: 1.7857em; /* 25px at 14px / em */
height: 1.7857em; /* 25px at 14px / em */
font-size: 0.9285em; /* 13px at 14px / em */
background-color: var(--color-background-widget-button);
font-family: inherit;
&:hover {
color: var(--color-poll-vote-hover);
background-color: var(--color-background-poll-vote-hover);
border-color: var(--color-border-poll-vote-hover);
}
&:focus {
outline: 0;
color: var(--color-poll-vote-focus);
background-color: var(--color-background-poll-vote-focus);
border-color: var(--color-border-poll-vote-focus);
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
.poll-names {
color: var(--color-poll-names);
/* Aim for 50% of the flexbox for voting names,
but also shrink modestly (.5) adjacent a long
option. */
flex: 1 0.5 50%;
}
}
button {
&.add-task,
&.poll-option {
color: hsl(156deg 41% 40%);
border: 1px solid hsl(156deg 28% 70%);
width: max-content;
flex: 0 0 auto;
border-radius: 3px;
background-color: var(--color-background-widget-button);
padding: 4px;
padding-left: 14px;
padding-right: 14px;
font-family: inherit;
font-size: inherit;
&:hover,
&:focus {
outline: 0;
border-color: hsl(156deg 30% 50%);
transition: 0.2s ease;
transition-property: background-color, border-color, color;
}
&:active {
transition: 0.2s ease;
transition-property: background-color, border-color, color;
}
&:disabled {
cursor: not-allowed;
filter: saturate(0);
background-color: var(--color-background-zulip-button-disabled);
color: hsl(0deg 3% 52%);
}
}
}
input {
&.add-task,
&.add-desc,
&.poll-option,
&.poll-question,
&.todo-task-list-title {
flex: 1 0 auto;
padding: var(--input-vertical-padding) var(--input-horizontal-padding);
}
}
.widget-error {
color: hsl(1deg 45% 50%);
font-size: 0.8571em; /* 12px at 14px/em */
display: flex;
align-items: center;
}
.poll-question-check,
.poll-question-remove,
.todo-task-list-title-check,
.todo-task-list-title-remove {
display: flex;
align-items: center;
justify-content: center;
align-self: stretch;
/* TODO: Re-express the 30.5px value here
as part of information density work. */
flex: 0 0 30.5px;
min-height: 30.5px;
border-radius: 3px;
border: 1px solid var(--color-border-zulip-button);
background-color: var(--color-background-zulip-button);
font-size: inherit;
&:hover {
border-color: var(--color-border-zulip-button-interactive);
background-color: var(--color-background-zulip-button-hover);
}
}
.poll-edit-question,
.todo-edit-task-list-title {
padding: 0;
border: none;
background: none;
font-size: inherit;
color: var(--color-message-action-visible);
cursor: pointer;
/* We're going to offset this manually with margin-top,
in ems, so the icon looks good even if a question
or list title spans multiple lines. */
align-self: flex-start;
/* Give the icon its own flexbox, so it is sized
nicely relative to the icon box. */
display: flex;
margin-top: 0.25em; /* 4px at 16px/1em; this is eyeballed, but correct. */
&:hover,
&:focus-visible {
color: var(--color-message-action-interactive);
}
}
.poll-question-bar {
flex: 1 1 auto;
display: flex;
/* Ensure controls remain visible on narrower screens. */
flex-flow: row wrap;
gap: 5px;
/* Reserve space for the focus outline to prevent it from being cut off */
margin-right: 2px;
margin-bottom: var(--markdown-interelement-space-px);
}
.poll-widget-header-area,
.todo-widget-header-area {
display: flex;
align-items: baseline;
gap: 5px;
/* Keep the expected margin under the heading, but place it
here on the header area, rather than the heading itself,
so the flexbox for aligning text and icon button is
unaffected. */
margin-bottom: var(--markdown-interelement-space-px);
}
.current-user-vote {
background-color: hsl(156deg 10% 90% / 90%);
}
.add-task-wrapper {
display: inline;
position: relative;
z-index: 1;
/* Unlike other browsers like Chrome, Microsoft Edge, etc.,
Firefox does not automatically display the "not-allowed"
cursor for disabled elements. The below css ensures that the
correct cursor is shown across all browsers. */
&:hover {
cursor: not-allowed;
}
}

2604
web/styles/zulip.css Normal file

File diff suppressed because it is too large Load Diff