mirror of
https://github.com/zulip/zulip.git
synced 2026-07-03 21:10:12 +08:00
The row of buttons is placed using CSS grid template areas so that visually it is now inside the bottom edge of the textbox. The color of the buttons row and individual buttons is changed to match the color of the textbox. All textbox border / box shadow properties are now applied to its parent instead which is extended under the buttons' row, so that its border snuggly fits around the buttons row too. Notable side effects: - In dark mode the textbox in focused state now has a light border which does not match the recipient input's current border which doesn't change when focused. Likely, the recipient input should be updated to match the textbox's border color. - The dividers in the formatting buttons row are not vertically centered now. This should be figured out soon. Fixes: #28702.
719 lines
20 KiB
CSS
719 lines
20 KiB
CSS
.message_row {
|
|
display: grid;
|
|
/* Prevent the messagebox column from overflowing the 1fr
|
|
space allotted by specifying `minmax(0,1fr)`, which
|
|
sets 0 as the minimum size, not the grid default of
|
|
`auto`. */
|
|
grid-template: auto auto / 2px minmax(0, 1fr);
|
|
grid-template-areas:
|
|
"date_unread_marker date_row "
|
|
"message_unread_marker messagebox";
|
|
|
|
border-left: 1px solid var(--color-message-list-border);
|
|
border-right: 1px solid var(--color-message-list-border);
|
|
background-color: var(--color-background-stream-message-content);
|
|
|
|
&.private-message {
|
|
background-color: var(--color-background-private-message-content);
|
|
|
|
.date_row {
|
|
background-color: var(--color-background-private-message-content);
|
|
}
|
|
|
|
.data-container::after {
|
|
background: linear-gradient(
|
|
0deg,
|
|
var(--color-background-private-message-content),
|
|
transparent 100%
|
|
);
|
|
}
|
|
}
|
|
|
|
&.direct_mention {
|
|
background-color: var(--color-background-direct-mention);
|
|
|
|
.message_embed .data-container::after {
|
|
background: linear-gradient(
|
|
0deg,
|
|
var(--color-background-direct-mention),
|
|
transparent 100%
|
|
);
|
|
}
|
|
}
|
|
|
|
&.group_mention {
|
|
background-color: var(--color-background-group-mention);
|
|
|
|
.message_embed .data-container::after {
|
|
background: linear-gradient(
|
|
0deg,
|
|
var(--color-background-group-mention),
|
|
transparent 100%
|
|
);
|
|
}
|
|
}
|
|
|
|
&.sender_info_hovered {
|
|
.sender_name {
|
|
cursor: pointer;
|
|
/* TODO: Refactor the teetering nested classes
|
|
above so that !important can be removed. */
|
|
color: var(--color-text-sender-name-hover) !important;
|
|
}
|
|
|
|
.message-avatar {
|
|
cursor: pointer;
|
|
}
|
|
}
|
|
}
|
|
|
|
.messagebox {
|
|
grid-area: messagebox;
|
|
word-wrap: break-word;
|
|
cursor: pointer;
|
|
border: none;
|
|
/* The left padding value accounts for a 2px
|
|
unread marker, ensuring a uniform 5px of
|
|
padding on either side of the message box. */
|
|
padding: 0 5px 0 3px;
|
|
|
|
&:hover .message_controls,
|
|
&:hover .message_failed {
|
|
.empty-star:hover {
|
|
cursor: pointer;
|
|
}
|
|
|
|
> div {
|
|
opacity: 1;
|
|
visibility: visible;
|
|
}
|
|
}
|
|
}
|
|
|
|
.messagebox-content {
|
|
display: grid;
|
|
align-items: baseline;
|
|
padding-left: 10px;
|
|
/* Prevent the message column from overflowing the 1fr
|
|
space allotted by specifying `minmax(0,1fr)`, which
|
|
sets 0 as the minimum size, not the grid default of
|
|
`auto`.
|
|
|
|
The calculated `--message-box-timestamp-column-width`
|
|
should match `max-content` exactly, but `max-content`
|
|
ensures the timestamp will always have enough space
|
|
in the column. */
|
|
grid-template:
|
|
var(--message-box-icon-height) repeat(3, auto) /
|
|
var(--message-box-avatar-column-width) minmax(0, 1fr) calc(
|
|
3 * var(--message-box-icon-width)
|
|
)
|
|
8px minmax(var(--message-box-timestamp-column-width), max-content);
|
|
/* Named grid areas provide flexibility for positioning grid items
|
|
reliably, even if the row or column definitions of the grid change. */
|
|
grid-template-areas:
|
|
"edited message controls . time"
|
|
". message . . . "
|
|
". more . . . "
|
|
". reactions . . . ";
|
|
|
|
&.content_edit_mode {
|
|
cursor: default;
|
|
/* Set the controls area to 0 to give more space
|
|
to the edit/source view area. */
|
|
grid-template-columns:
|
|
var(--message-box-avatar-column-width) minmax(0, 1fr)
|
|
0 8px minmax(
|
|
var(--message-box-timestamp-column-width),
|
|
max-content
|
|
);
|
|
}
|
|
|
|
@media (width < $sm_min), ((width >= $md_min) and (width < $mc_min)) {
|
|
grid-template-columns:
|
|
var(--message-box-avatar-column-width) minmax(0, 1fr) calc(
|
|
2 * var(--message-box-icon-width)
|
|
)
|
|
8px minmax(var(--message-box-timestamp-column-width), max-content);
|
|
}
|
|
|
|
.message_controls {
|
|
grid-area: controls;
|
|
|
|
@media (width < $sm_min), ((width >= $md_min) and (width < $mc_min)) {
|
|
/* This is intended to target the first message_controls child
|
|
when there are 3 displayed. 4 = 3 + hidden message_failed element. */
|
|
.message_control_button:nth-last-child(4) {
|
|
display: none;
|
|
}
|
|
}
|
|
}
|
|
|
|
.message_edit_notice {
|
|
grid-area: edited;
|
|
}
|
|
|
|
.slow-send-spinner {
|
|
display: none;
|
|
justify-self: end;
|
|
margin-right: 10px;
|
|
text-align: end;
|
|
grid-area: time;
|
|
}
|
|
|
|
.message_content {
|
|
grid-area: message;
|
|
/*
|
|
Space between two single line messages is kept in
|
|
lockstep with adjacent Markdown elements.
|
|
*/
|
|
padding: var(--message-box-markdown-aligned-vertical-space) 0 0;
|
|
color: var(--color-text-message-default);
|
|
/* We explicitly set line-height here so that
|
|
the message area is not beholden to the legacy
|
|
`max()` declaration on `body`. */
|
|
line-height: var(--base-line-height-unitless);
|
|
min-height: 17px;
|
|
display: block;
|
|
position: relative;
|
|
overflow: hidden;
|
|
|
|
&:empty {
|
|
display: none;
|
|
}
|
|
|
|
&.condensed {
|
|
max-height: 8.5em;
|
|
min-height: 0;
|
|
overflow: hidden;
|
|
mask-image: linear-gradient(
|
|
to top,
|
|
hsl(0deg 0% 100% / 0%) 0%,
|
|
hsl(0deg 0% 100%) 60px
|
|
);
|
|
mask-size: cover;
|
|
}
|
|
|
|
&.collapsed {
|
|
padding: 0;
|
|
max-height: 0;
|
|
min-height: 0;
|
|
overflow: hidden;
|
|
}
|
|
}
|
|
|
|
.message_reactions {
|
|
grid-area: reactions;
|
|
display: flex;
|
|
/* Allow reactions to wrap in mobile viewports */
|
|
flex-wrap: wrap;
|
|
/* Keep reactions aligned to start (top), so that the
|
|
margin on the reaction button doesn't appear to
|
|
stretch the reactions taller. */
|
|
align-items: flex-start;
|
|
/* Control reaction spacing with a gap */
|
|
gap: 4px;
|
|
}
|
|
|
|
.message_edit {
|
|
grid-area: message;
|
|
/* Align self to start, rather than baseline, so the baseline
|
|
is preserved from the message itself--keeping the time,
|
|
edit message, and controls at the same vertical alignment. */
|
|
align-self: start;
|
|
margin-top: var(--message-box-vertical-margin);
|
|
}
|
|
|
|
.message_length_controller {
|
|
grid-area: more;
|
|
}
|
|
|
|
.collapsed ~ .message_length_controller {
|
|
/* When content is collapsed, the length controller
|
|
should occupy the space ordinarily assigned to
|
|
the message. */
|
|
grid-area: message;
|
|
/* However, don't let the SHOW MORE button participate
|
|
in the baseline group. */
|
|
align-self: start;
|
|
|
|
.message_length_toggle {
|
|
/* Ensure that a collapsed message maintains the
|
|
same space around the toggle button as any other
|
|
message-row content. */
|
|
margin: 4px 0;
|
|
}
|
|
}
|
|
|
|
&.content_edit_mode .message_length_controller {
|
|
/* If entering edit mode on a collapsed message,
|
|
just hide the controller area. This preserves
|
|
the collapsed state of the message, which need
|
|
not be touched. We just need to hide the button.
|
|
This works for condensed messages, too. */
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
.messagebox-includes-sender {
|
|
.messagebox-content {
|
|
grid-template-areas:
|
|
"avatar sender controls . time"
|
|
"avatar message . . . "
|
|
". more . . . "
|
|
". reactions . . . ";
|
|
|
|
.message_edit {
|
|
/* No top margin when there's a sender row */
|
|
margin-top: 0;
|
|
}
|
|
|
|
&.is-me-message {
|
|
grid-template-areas:
|
|
"avatar sender controls . time"
|
|
"avatar sender . . . "
|
|
". more . . . "
|
|
". reactions . . . ";
|
|
|
|
.message-avatar {
|
|
/* Set the line-height to match the height of the avatar image
|
|
to center me-messages within the baseline group. */
|
|
line-height: var(--message-box-avatar-height);
|
|
align-self: baseline;
|
|
}
|
|
|
|
.message_sender {
|
|
/* Don't display message sender as flexbox for me-messages. */
|
|
display: block;
|
|
/* Set the same line-height as on message content for
|
|
me-messages. */
|
|
line-height: var(--base-line-height-unitless);
|
|
/* Ensure the same spacing below messages as for any other
|
|
paragraph. */
|
|
padding-bottom: var(
|
|
--message-box-markdown-aligned-vertical-space
|
|
);
|
|
|
|
/* Display message components inline, with wrapping white-space,
|
|
so the sender name and message display as a continuous line
|
|
of wrapping text. */
|
|
.sender_name {
|
|
white-space: normal;
|
|
display: inline;
|
|
}
|
|
|
|
.sender_name_text {
|
|
max-width: 100%;
|
|
}
|
|
}
|
|
|
|
&.content_edit_mode {
|
|
.message_sender {
|
|
/* Keep the me-message participating in the baseline */
|
|
visibility: hidden;
|
|
}
|
|
|
|
.message_edit {
|
|
/* Use the sender grid area for the me-message edit/view-source box */
|
|
grid-area: sender;
|
|
margin-top: var(--message-box-vertical-margin);
|
|
}
|
|
}
|
|
}
|
|
|
|
.message-avatar {
|
|
grid-area: avatar;
|
|
/* The picture should not participate in the baseline group. */
|
|
align-self: start;
|
|
/* Because the avatar may be the tallest element in a
|
|
single-line me message, this margin preserves the
|
|
overall height of the message box. */
|
|
margin: var(--message-box-vertical-margin) 0;
|
|
}
|
|
|
|
.status-message {
|
|
font-weight: normal;
|
|
}
|
|
|
|
.message_content {
|
|
/* Top padding is reduced when there is a sender line. */
|
|
padding-top: 3px;
|
|
grid-area: message;
|
|
}
|
|
|
|
.slow-send-spinner {
|
|
align-self: center;
|
|
position: unset;
|
|
margin-top: 1px;
|
|
}
|
|
|
|
.message_edit_notice {
|
|
/* When the edit notice is inline, as on edited me-messages,
|
|
the inline-block and accompanying vertical-align styles will
|
|
apply */
|
|
display: inline-block;
|
|
vertical-align: baseline;
|
|
/* A bit of margin here helps these not look associated with the name. */
|
|
margin-left: 4px;
|
|
}
|
|
|
|
.message_sender {
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
grid-area: sender;
|
|
display: flex;
|
|
/* A generous line-height on the sender name is necessary
|
|
for internationalization and non-Western Latin character
|
|
sets. */
|
|
line-height: var(--message-box-sender-line-height);
|
|
/* Ensure flexed sender info (name, status emoji, inline EDITED)
|
|
forms a baseline group, which will participate with the
|
|
message-box grid's baseline group, too. */
|
|
align-items: baseline;
|
|
|
|
.zulip-icon.zulip-icon-bot {
|
|
font-size: 12px;
|
|
align-self: center;
|
|
padding: 0 0 0 5px;
|
|
}
|
|
|
|
.sender_name {
|
|
display: inline-flex;
|
|
font-weight: 700;
|
|
color: var(--color-text-sender-name);
|
|
column-gap: 3px;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
outline: none;
|
|
align-self: baseline;
|
|
|
|
.sender_name_text {
|
|
overflow: hidden;
|
|
white-space: nowrap;
|
|
display: inline-flex;
|
|
|
|
.user-name {
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.unread_marker {
|
|
margin-left: var(--unread-marker-left);
|
|
opacity: 0;
|
|
transition: all 0.3s ease-out;
|
|
|
|
&.slow_fade {
|
|
transition: all 2s ease-out;
|
|
}
|
|
|
|
&.fast_fade {
|
|
transition: all 0.3s ease-out;
|
|
}
|
|
|
|
&.date_unread_marker {
|
|
grid-area: date_unread_marker;
|
|
|
|
.unread-marker-fill {
|
|
border-radius: 0 !important;
|
|
height: 100% !important;
|
|
}
|
|
}
|
|
|
|
&.message_unread_marker {
|
|
grid-area: message_unread_marker;
|
|
}
|
|
}
|
|
|
|
.unread-marker-fill {
|
|
height: 100%;
|
|
width: 2px;
|
|
background: linear-gradient(
|
|
90deg,
|
|
var(--color-unread-marker) 30%,
|
|
hsl(217deg 64% 59% / 0%)
|
|
);
|
|
}
|
|
|
|
.unread .unread_marker {
|
|
transition: all 0.3s ease-out;
|
|
opacity: 1;
|
|
}
|
|
|
|
.message-time {
|
|
justify-self: end;
|
|
padding-right: 5px;
|
|
/* Maintain first-line baseline regardless of
|
|
what happens in the message-content area (e.g., a
|
|
collapsed message, or source/edit view). This
|
|
line-height also keeps EDITED/MOVED messages
|
|
in place, care of their baseline-group participation
|
|
within the grid. */
|
|
line-height: var(--message-box-sender-line-height);
|
|
grid-area: time;
|
|
display: block;
|
|
/* 13px at 14px/1em */
|
|
font-size: 0.9286em;
|
|
font-weight: 350;
|
|
text-align: end;
|
|
opacity: 0.8;
|
|
color: var(--color-text-default);
|
|
letter-spacing: 0.02em;
|
|
/* Disable blue link styling for the message timestamp link. */
|
|
&:hover {
|
|
text-decoration: none;
|
|
color: var(--color-message-action-interactive);
|
|
}
|
|
|
|
&.notvisible {
|
|
/* This happens when message failed to send. We don't want to
|
|
display time but still want it to occupy space. */
|
|
width: 45px !important;
|
|
position: unset !important;
|
|
}
|
|
}
|
|
|
|
/* Message-editing styles */
|
|
.message-edit-buttons-and-timer {
|
|
margin-top: 5px;
|
|
display: flex;
|
|
gap: 5px;
|
|
/* Allow items to occupy full height.
|
|
This is especially important for
|
|
buttons that are flex items. */
|
|
align-items: stretch;
|
|
}
|
|
|
|
.message-actions-button {
|
|
box-sizing: border-box;
|
|
/* Display the actions buttons as
|
|
flex containers for positioning
|
|
text and spinners. */
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 28px;
|
|
padding: 0 10px;
|
|
border-radius: 4px;
|
|
border: 0;
|
|
line-height: 1;
|
|
}
|
|
|
|
.message_edit_save {
|
|
/* Match Save button's basic colors to
|
|
the compose box Send button. */
|
|
color: var(--color-compose-send-button-icon-color);
|
|
background-color: var(--color-compose-send-button-background);
|
|
|
|
&:hover {
|
|
background-color: var(
|
|
--color-compose-send-button-background-interactive
|
|
);
|
|
}
|
|
|
|
&:disabled {
|
|
/* Replicate the `.new-style` disabled values,
|
|
without any color shifts. */
|
|
cursor: not-allowed;
|
|
opacity: 0.5;
|
|
}
|
|
}
|
|
|
|
.message_edit_cancel,
|
|
.message_edit_close {
|
|
color: var(--color-exit-button-text);
|
|
background: var(--color-exit-button-background);
|
|
|
|
&:hover {
|
|
background: var(--color-exit-button-background-interactive);
|
|
}
|
|
}
|
|
|
|
.edit-content-container {
|
|
border-radius: 4px;
|
|
border: 1px solid var(--color-message-content-container-border);
|
|
transition: border 0.2s ease;
|
|
|
|
&:has(.message_edit_content:focus) {
|
|
border-color: var(--color-message-content-container-border-focus);
|
|
}
|
|
}
|
|
|
|
.message_edit_content {
|
|
max-height: 24em;
|
|
width: 100%;
|
|
min-width: 206px;
|
|
min-height: 52px;
|
|
box-sizing: border-box;
|
|
/* Setting resize as none hides the bottom right diagonal box
|
|
(which even has a background color of its own!). */
|
|
resize: none !important;
|
|
color: hsl(0deg 0% 33%);
|
|
background-color: hsl(0deg 0% 100%);
|
|
border-radius: 3px;
|
|
vertical-align: middle;
|
|
border: none;
|
|
/* Match textarea padding to compose box and
|
|
message-edit preview area. */
|
|
padding: 5px;
|
|
margin: 0;
|
|
|
|
box-shadow: none;
|
|
|
|
&:focus {
|
|
outline: 0;
|
|
}
|
|
|
|
&:read-only,
|
|
&:disabled {
|
|
cursor: not-allowed;
|
|
}
|
|
}
|
|
|
|
/* The selector needs to be written this way to override the specificity
|
|
of the base style defined for a read-only textarea in dark mode. */
|
|
.message_edit_form .edit-controls textarea.message_edit_content:read-only {
|
|
opacity: 1;
|
|
}
|
|
|
|
.message_edit_countdown_timer {
|
|
text-align: right;
|
|
display: inline;
|
|
color: hsl(0deg 0% 63%);
|
|
|
|
&.expired {
|
|
color: hsl(4deg 58% 33%);
|
|
}
|
|
}
|
|
|
|
.message-edit-timer {
|
|
display: none;
|
|
/* Center vertically relative to
|
|
buttons. */
|
|
align-self: center;
|
|
/* Use flexbox to position to far right of
|
|
the Save and Cancel buttons. */
|
|
margin-left: auto;
|
|
}
|
|
|
|
.message-edit-feature-group {
|
|
display: flex;
|
|
align-items: baseline;
|
|
border-radius: 0 0 3px 3px;
|
|
background-color: var(--color-message-formatting-controls-container);
|
|
/* margin on either side to let the border of
|
|
.edit-content-container show through. */
|
|
margin: 0 1px;
|
|
}
|
|
|
|
.message_edit_save .loader {
|
|
display: none;
|
|
height: 28px;
|
|
width: 28px;
|
|
}
|
|
|
|
.edit-controls {
|
|
.btn-wrapper {
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.disable-btn {
|
|
pointer-events: none;
|
|
}
|
|
}
|
|
|
|
.message_edit {
|
|
display: none;
|
|
}
|
|
|
|
/* Reduce some of the heavy padding from Bootstrap. */
|
|
.message_edit_form {
|
|
margin-bottom: 10px;
|
|
cursor: default;
|
|
|
|
.edit-controls {
|
|
margin-left: 0;
|
|
margin-top: 0;
|
|
}
|
|
}
|
|
|
|
.message_edit_notice {
|
|
font-size: 10px;
|
|
opacity: 0.5;
|
|
user-select: none;
|
|
white-space: nowrap;
|
|
overflow-x: hidden;
|
|
overflow-x: clip;
|
|
}
|
|
|
|
.fade-in-message {
|
|
animation-name: fadeInMessage;
|
|
animation-duration: 1s;
|
|
animation-iteration-count: 1;
|
|
|
|
.message_edit_notice {
|
|
animation-name: fadeInEditNotice;
|
|
animation-duration: 1s;
|
|
animation-iteration-count: 1;
|
|
}
|
|
}
|
|
|
|
.message_edit_notice_hover:hover {
|
|
opacity: 1;
|
|
}
|
|
|
|
/* Locally echoed messages. */
|
|
.locally-echoed .message-time {
|
|
opacity: 0;
|
|
/* Don't show pointer when message-time doesn't have a link. */
|
|
cursor: default;
|
|
}
|
|
|
|
/* Show the spinner only for messages that are still locally echoed. */
|
|
.locally-echoed .slow-send-spinner {
|
|
display: unset !important;
|
|
cursor: default;
|
|
}
|
|
|
|
.recipient_row {
|
|
/* See https://stackoverflow.com/questions/2717480/css-selector-for-first-element-with-class/8539107#8539107
|
|
for details on how this works */
|
|
.message_row.unread {
|
|
.date_unread_marker {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
/* Select all but the first .message_row.unread,
|
|
and remove the properties set from the previous rule. */
|
|
.message_row.unread ~ .message_row.unread {
|
|
.date_unread_marker {
|
|
display: block;
|
|
}
|
|
}
|
|
}
|
|
|
|
.date_row {
|
|
grid-area: date_row;
|
|
|
|
& .timerender-content {
|
|
display: block;
|
|
overflow: hidden;
|
|
text-transform: uppercase;
|
|
font-size: calc(12em / 14);
|
|
font-style: normal;
|
|
font-weight: 600;
|
|
line-height: 17px; /* identical to box height, or 131% */
|
|
text-align: right;
|
|
letter-spacing: 0.04em;
|
|
color: var(--color-date);
|
|
/* Right padding matches time in message row and date in recipient row. */
|
|
padding: 8px 6px 8px 4px;
|
|
}
|
|
}
|