Files
margin-mail/src/screens/compose.css
T
2026-10-03 21:49:47 +05:30

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; }