mirror of
https://github.com/priyanshujain/margin-mail.git
synced 2026-10-05 04:27:05 +00:00
409 lines
8.2 KiB
CSS
409 lines
8.2 KiB
CSS
/* The compose card and the reply box in the thread.
|
|
*
|
|
* Two shapes of one thing. The card floats over the stage at the bottom right and can take the
|
|
* whole window; the box sits in the thread at the thread's own measure, under what it answers. The
|
|
* fields, the chips, the foot and the editor are shared, so they are written once here and the two
|
|
* containers differ only in where they are and what holds them up.
|
|
*
|
|
* A draft's attachments carry `.attachment` and `.attachments`, which pane.css already styles for
|
|
* the ones on a received message. That is deliberate reuse and not a collision: a file on the way
|
|
* out and a file that arrived are the same chip, and giving them two rules is how they stop being. */
|
|
|
|
.compose {
|
|
position: fixed;
|
|
right: 20px;
|
|
bottom: 20px;
|
|
z-index: 30;
|
|
width: var(--compose-w);
|
|
max-height: var(--compose-max-h);
|
|
display: flex;
|
|
flex-direction: column;
|
|
background: var(--paper);
|
|
border: 1px solid var(--line-strong);
|
|
border-radius: var(--r-lg);
|
|
box-shadow: var(--shadow-page);
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* The whole window, which is Cmd+Shift+P. Inset rather than edge to edge: a card that touched the
|
|
frame would stop reading as something in front of the mailbox. */
|
|
.compose[data-expanded] {
|
|
top: calc(var(--titlebar-h) + 16px);
|
|
right: 16px;
|
|
bottom: 16px;
|
|
left: 16px;
|
|
width: auto;
|
|
max-height: none;
|
|
}
|
|
|
|
.compose-head {
|
|
flex: none;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 8px 8px 8px 16px;
|
|
border-bottom: 1px solid var(--line);
|
|
background: var(--shell);
|
|
}
|
|
|
|
.compose-head h2 {
|
|
margin: 0;
|
|
flex: 1;
|
|
font-family: var(--font-heading);
|
|
font-weight: 500;
|
|
font-size: var(--t-4);
|
|
}
|
|
|
|
/* One row of the head: a label, what is in it, and whatever hangs off the right. */
|
|
.compose-field {
|
|
position: relative;
|
|
flex: none;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
min-height: 36px;
|
|
padding: 4px 16px;
|
|
border-bottom: 1px solid var(--line);
|
|
font-size: var(--t-3);
|
|
}
|
|
|
|
.compose-field .lab {
|
|
flex: none;
|
|
align-self: flex-start;
|
|
padding-top: 6px;
|
|
width: 52px;
|
|
color: var(--ink-faint);
|
|
font-size: var(--t-2);
|
|
}
|
|
|
|
.compose-field .chips {
|
|
flex: 1;
|
|
min-width: 0;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 6px;
|
|
align-items: center;
|
|
cursor: text;
|
|
}
|
|
|
|
.compose-field .more {
|
|
flex: none;
|
|
align-self: flex-start;
|
|
padding: 6px 0 0;
|
|
color: var(--ink-faint);
|
|
font-size: var(--t-2);
|
|
}
|
|
|
|
.compose-field .more:hover {
|
|
color: var(--ink);
|
|
}
|
|
|
|
.chip {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
height: 24px;
|
|
padding: 0 6px 0 3px;
|
|
border: 0;
|
|
border-radius: var(--r-pill);
|
|
background: var(--accent-wash);
|
|
color: var(--ink);
|
|
font-size: var(--t-2);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.chip-off {
|
|
display: grid;
|
|
place-items: center;
|
|
color: var(--ink-faint);
|
|
}
|
|
|
|
.chip-off:hover {
|
|
color: var(--ink);
|
|
}
|
|
|
|
/* The field the caret is in, which grows with what is typed rather than scrolling a fixed box. */
|
|
.chip-input {
|
|
flex: 1;
|
|
min-width: 140px;
|
|
height: 24px;
|
|
border: 0;
|
|
background: none;
|
|
color: var(--ink);
|
|
font-family: inherit;
|
|
font-size: var(--t-3);
|
|
}
|
|
|
|
.chip-input:focus {
|
|
outline: none;
|
|
}
|
|
|
|
.compose-subject {
|
|
flex: 1;
|
|
min-width: 0;
|
|
height: 28px;
|
|
border: 0;
|
|
background: none;
|
|
color: var(--ink);
|
|
font-family: inherit;
|
|
font-size: var(--t-3);
|
|
}
|
|
|
|
.compose-subject:focus {
|
|
outline: none;
|
|
}
|
|
|
|
/* Autocomplete, hanging under the field it is about. It is not a Popover: a popover is anchored in
|
|
fixed coordinates and closes on a pointer down anywhere else, and this has to stay open while the
|
|
caret keeps typing into the field above it. */
|
|
.suggest {
|
|
position: absolute;
|
|
z-index: 2;
|
|
left: 62px;
|
|
right: 16px;
|
|
top: 100%;
|
|
margin: 2px 0 0;
|
|
padding: 4px;
|
|
list-style: none;
|
|
border: 1px solid var(--line-strong);
|
|
border-radius: var(--r-md);
|
|
background: var(--paper);
|
|
box-shadow: var(--shadow-pop);
|
|
}
|
|
|
|
.suggest-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
width: 100%;
|
|
padding: 6px 8px;
|
|
border-radius: var(--r-sm);
|
|
text-align: left;
|
|
font-size: var(--t-2);
|
|
}
|
|
|
|
.suggest-row[data-on],
|
|
.suggest-row:hover {
|
|
background: var(--accent-wash);
|
|
}
|
|
|
|
.suggest-name {
|
|
color: var(--ink);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.suggest-addr {
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
color: var(--ink-faint);
|
|
}
|
|
|
|
.from-list {
|
|
margin: 0;
|
|
padding: 4px;
|
|
list-style: none;
|
|
}
|
|
|
|
.from-option {
|
|
display: block;
|
|
width: 100%;
|
|
padding: 7px 9px;
|
|
border-radius: var(--r-sm);
|
|
text-align: left;
|
|
font-size: var(--t-3);
|
|
}
|
|
|
|
.from-option:hover,
|
|
.from-option[data-on] {
|
|
background: var(--accent-wash);
|
|
}
|
|
|
|
/* The body. It scrolls rather than the card, so the head and the foot stay where the hand left
|
|
them however long the message gets. */
|
|
.compose-body {
|
|
flex: 1;
|
|
min-height: var(--compose-body-h);
|
|
overflow-y: auto;
|
|
padding: 14px 16px;
|
|
}
|
|
|
|
.compose-foot {
|
|
flex: none;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 8px 10px 8px 12px;
|
|
border-top: 1px solid var(--line);
|
|
background: var(--raised);
|
|
}
|
|
|
|
.compose-foot .spacer {
|
|
flex: 1;
|
|
}
|
|
|
|
.compose-delay {
|
|
color: var(--ink-faint);
|
|
font-size: var(--t-1);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* Said before the send rather than after it, which is the whole point of the provider's limit
|
|
being checked on every save. */
|
|
.compose-warn {
|
|
color: var(--danger-ink);
|
|
font-size: var(--t-1);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.compose-date {
|
|
height: 26px;
|
|
padding: 0 6px;
|
|
border: 1px solid var(--line-strong);
|
|
border-radius: var(--r-sm);
|
|
background: var(--paper);
|
|
color: var(--ink);
|
|
font-family: inherit;
|
|
font-size: var(--t-2);
|
|
}
|
|
|
|
/* The reply box, at the foot of the thread. */
|
|
|
|
.reply {
|
|
margin-top: 16px;
|
|
border: 1px solid var(--line-strong);
|
|
border-radius: var(--r-lg);
|
|
background: var(--raised);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.reply-head {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 10px 12px 10px 14px;
|
|
border-bottom: 1px solid var(--line);
|
|
color: var(--ink-soft);
|
|
font-size: var(--t-2);
|
|
}
|
|
|
|
.reply-who {
|
|
flex: 1;
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.reply-head b {
|
|
color: var(--ink);
|
|
font-weight: 500;
|
|
}
|
|
|
|
/* Reply all is a switch rather than a second box, and it prints the key that does the same thing
|
|
from the thread. */
|
|
.reply-all {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 3px 8px 3px 4px;
|
|
border-radius: var(--r-pill);
|
|
color: var(--ink-faint);
|
|
font-size: var(--t-2);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.reply-all:hover {
|
|
color: var(--ink);
|
|
}
|
|
|
|
.reply-all[data-on] {
|
|
background: var(--accent-wash);
|
|
color: var(--ink);
|
|
}
|
|
|
|
.reply-close {
|
|
display: grid;
|
|
place-items: center;
|
|
color: var(--ink-faint);
|
|
}
|
|
|
|
.reply-close:hover {
|
|
color: var(--ink);
|
|
}
|
|
|
|
.reply .compose-field {
|
|
padding-left: 14px;
|
|
padding-right: 12px;
|
|
background: var(--paper);
|
|
}
|
|
|
|
.reply .suggest {
|
|
left: 60px;
|
|
right: 12px;
|
|
}
|
|
|
|
.reply-body {
|
|
min-height: var(--reply-body-h);
|
|
padding: 12px 14px;
|
|
background: var(--paper);
|
|
}
|
|
|
|
.reply .compose-foot {
|
|
background: var(--raised);
|
|
}
|
|
|
|
/* Waiting to send: the one line a thread carries while its own message is still in the outbox. */
|
|
.sending {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
margin-top: 16px;
|
|
padding: 10px 14px;
|
|
border: 1px solid var(--line);
|
|
border-radius: var(--r-md);
|
|
background: var(--raised);
|
|
color: var(--ink-soft);
|
|
font-size: var(--t-3);
|
|
}
|
|
|
|
.sending .icon {
|
|
flex: none;
|
|
color: var(--ink-faint);
|
|
}
|
|
|
|
.sending-now {
|
|
margin-left: auto;
|
|
color: var(--ink);
|
|
font-size: var(--t-2);
|
|
text-decoration: underline;
|
|
text-underline-offset: 3px;
|
|
}
|
|
|
|
.sending-now:disabled {
|
|
color: var(--ink-faint);
|
|
text-decoration: none;
|
|
cursor: default;
|
|
}
|
|
|
|
/* On a phone the card is the window, because there is no room for it to be anything else. */
|
|
:root[data-phone] .compose {
|
|
top: calc(var(--safe-top) + var(--phonebar-h));
|
|
right: 0;
|
|
bottom: 0;
|
|
left: 0;
|
|
width: auto;
|
|
max-height: none;
|
|
border-radius: 0;
|
|
border-width: 1px 0 0;
|
|
}
|
|
|
|
:root[data-phone] .chip-input,
|
|
:root[data-phone] .compose-subject {
|
|
font-size: var(--t-5);
|
|
}
|
|
|
|
.draft-card { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 12px; border: 1px solid var(--line); border-radius: 6px; margin: 8px 0; }
|
|
.draft-card > span { flex: 1; min-width: 0; }
|