layout: visual interface improvements
ci/woodpecker/pr/woodpecker Pipeline was successful Details
ci/woodpecker/push/woodpecker Pipeline was successful Details

- writing a new message is now presented in full-screen, so that
  there are no distractions, i.e. other posts
- added back button and listen to esc key to close new message
- on portrait mode the navigation buttons are now positioned at
  the bottom of the screen
- write new message botton (bubble) is also positioned bottomright
- replies now use a line to the last reply instead of indentation,
  better use of available space, especially on small screen
- ignore newlines at the end of a post
- added subtile growin effect to the multiline textfield, to hint
  that the textarea is growing with more content
pull/13/head
OFF0 2 years ago
parent 3ab815c30e
commit 4576355b03
Signed by: offbyn
GPG Key ID: 94A2F643C51F37FA

@ -1,26 +1,40 @@
/* https://developer.mozilla.org/en-US/docs/Web/CSS/Layout_cookbook/Media_objects */ /* https://developer.mozilla.org/en-US/docs/Web/CSS/Layout_cookbook/Media_objects */
.mbox { .mbox {
--profileimg-size: 5rem;
align-items: center; align-items: center;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
flex-wrap: nowrap; flex-wrap: wrap;
margin-bottom: 1rem; margin-bottom: 1rem;
padding: 0 calc(.25 * var(--gap));
}
.mbox:last-child {
margin-bottom: 0;
}
.mbox .mbox {
padding: 0;
} }
.mbox-img { .mbox-img {
--size: 5rem;
align-self: start; align-self: start;
flex-basis: var(--size); background-color: var(--bgcolor-textinput);
height: var(--size); border-radius: var(--profileimg-size);
border: 1px solid transparent;
flex-basis: var(--profileimg-size);
height: var(--profileimg-size);
margin-right: 1rem; margin-right: 1rem;
margin-top: .5ch; /* padding-top: .5ch; */
max-width: var(--size); max-width: var(--profileimg-size);
max-width: var(--size); max-width: var(--profileimg-size);
outline: .5rem solid var(--bgcolor);
overflow: hidden;
position: relative;
z-index: 2;
} }
.mbox-updated-contact .mbox-img, .mbox-updated-contact .mbox-img,
.mbox-recommend-server .mbox-img { .mbox-recommend-server .mbox-img {
--size: 4.5ch; --profileimg-size: 4.5ch;
margin-left: 3ch; margin-left: 3ch;
margin-right: 3.5ch; margin-right: 3.5ch;
} }
@ -62,3 +76,28 @@
padding: 0 0 1rem 0; padding: 0 0 1rem 0;
margin: 0; margin: 0;
} }
.mbox {
overflow: hidden;
}
.mbox .mbox {
overflow: visible;
position: relative;
}
.mobx-replies {
flex-grow: 1;
position: relative;
}
.mbox .mbox::before,
.mobx-replies::before {
background-color: var(--bgcolor-inactive);
border: none;
content: "";
display: block;
height: 100vh;
left: calc(.5 * var(--profileimg-size));
margin-left: -.2rem;
position: absolute;
top: -100vh;
width: .4rem;
}

@ -32,6 +32,7 @@ export function elem(name = 'div', {data, ...props} = {}, children = []) {
*/ */
export function multilineText(string) { export function multilineText(string) {
return string return string
.trimRight()
.split('\n') .split('\n')
.reduce((acc, next, i) => acc.concat(i === 0 ? next : [elem('br'), next]), []); .reduce((acc, next, i) => acc.concat(i === 0 ? next : [elem('br'), next]), []);
} }

@ -1,30 +1,46 @@
:root {
--transition-duration: .25s;
--transition-timing-function: ease-out;
}
form, form,
.form { .form {
--padding: 1.2rem;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: var(--gap);
}
fieldset {
/* ignore this container */
border: none;
display: contents;
}
legend {
display: none;
width: 100%;
}
#newMessage legend {
display: block;
} }
input, input,
textarea { textarea {
color: var(--color); color: var(--color);
font-size: 1.6rem; font-size: 1.6rem;
margin-bottom: 1.2rem;
padding: 1.3rem 1.8rem;
} }
button, button,
label { label {
color: var(--color);
cursor: pointer; cursor: pointer;
display: block; display: block;
font-size: 1.6rem; font-size: 1.6rem;
margin-bottom: 0; margin-bottom: 0;
padding: 1.3rem 1.8rem; padding: var(--padding);
text-indent: 0; text-indent: 0;
transition: background-color .25s; transition: background-color var(--transition-duration);
}
label {
color: var(--color-accent);
} }
input[type="password"], input[type="password"],
@ -34,7 +50,8 @@ textarea {
border: .2rem solid #b7b7b7; border: .2rem solid #b7b7b7;
border-radius: .2rem; border-radius: .2rem;
display: block; display: block;
margin: 0; margin: 0 0 1.2rem 0;
padding: var(--padding);
} }
input[type="password"]:focus, input[type="password"]:focus,
input[type="text"]:focus, input[type="text"]:focus,
@ -44,27 +61,41 @@ textarea:focus {
outline: var(--focus-outline); outline: var(--focus-outline);
} }
textarea { textarea {
max-height: 50vh; /* max-height: 64vh; */
min-height: 20px; min-height: 20px;
resize: none; resize: none;
transition: min-height .1s ease-out, height .1s ease-out; transition: min-height var(--transition-duration) var(--transition-timing-function),
height var(--transition-duration) var(--transition-timing-function);
} }
textarea:focus { textarea:focus {
min-height: 3.5rem; min-height: 3.5rem;
} }
#newMessage textarea {
min-height: 10rem;
}
#newMessage textarea:focus {
min-height: 18rem;
}
.buttons { .buttons {
align-items: center; align-items: center;
display: flex; display: flex;
flex-basis: 100%;
justify-content: flex-end; justify-content: flex-end;
gap: var(--gap);
margin-top: 2rem; margin-top: 2rem;
min-height: 3.2rem; min-height: 3.2rem;
} }
.form-inline .buttons {
flex-basis: fit-content;
margin-top: 0;
}
button { button {
background-color: var(--bgcolor-accent); --bg-color: var(--bgcolor-accent);
border: none; --border-color: var(--bgcolor-accent);
background-color: var(--bg-color);
border: 0.2rem solid var(--border-color);
border-radius: .2rem; border-radius: .2rem;
cursor: pointer; cursor: pointer;
outline-offset: 1px; outline-offset: 1px;
@ -75,6 +106,7 @@ button:focus {
} }
.btn-inline { .btn-inline {
--border-color: transparent;
align-items: center; align-items: center;
background: transparent; background: transparent;
color: var(--color); color: var(--color);
@ -100,7 +132,8 @@ button:focus {
} }
button:disabled { button:disabled {
background-color: var(--bgcolor-inactive); --bg-color: var(--bgcolor-inactive);
--border-color: var(--bgcolor-inactive);
cursor: default; cursor: default;
} }
@ -110,15 +143,19 @@ button:disabled {
} }
.form-status { .form-status {
flex-basis: 100%;
flex-grow: 1; flex-grow: 1;
padding: 1rem 1.8rem; padding: 1rem 1.8rem;
} }
.form-inline { .form-inline {
--padding: 1.2rem 1.3rem;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
flex-grow: 1; flex-grow: 1;
gap: 1rem; flex-wrap: wrap;
gap: 0 var(--gap);
padding: 0;
} }
.cards .form-inline button, .cards .form-inline button,
.cards .form-inline input[type="text"], .cards .form-inline input[type="text"],
@ -128,7 +165,10 @@ button:disabled {
.form-inline input[type="text"], .form-inline input[type="text"],
.form-inline textarea { .form-inline textarea {
flex-basis: 50%;
flex-grow: 1; flex-grow: 1;
flex-shrink: 1;
min-width: 100px;
margin-bottom: 0; margin-bottom: 0;
} }
@ -136,10 +176,36 @@ button:disabled {
flex-grow: 0; flex-grow: 0;
} }
.form-inline button#publish { button#publish {
align-self: end;
order: 2;
}
button[name="back"] {
display: none;
}
#newMessage button[name="back"] {
align-self: end; align-self: end;
display: inherit;
}
#sendstatus {
order: 1;
} }
.focus-active { .focus-active {
} }
.shrink-out {
animation-duration: var(--transition-duration);
animation-name: lineInserted;
transition: max-height var(--transition-duration) var(--transition-timing-function);
}
@keyframes lineInserted {
from {
max-height: 50px;
}
to {
max-height: 0px;
}
}

@ -8,6 +8,8 @@
</head> </head>
<body> <body>
<main class="tabbed"> <main class="tabbed">
<input type="radio" name="maintabs" id="settings" class="tab">
<label for="settings">profile</label>
<input type="radio" name="maintabs" id="feed" class="tab" checked> <input type="radio" name="maintabs" id="feed" class="tab" checked>
<label for="feed">feed</label> <label for="feed">feed</label>
<!-- <input type="radio" name="maintabs" id="trending" class="tab"> <!-- <input type="radio" name="maintabs" id="trending" class="tab">
@ -16,21 +18,27 @@
<label for="direct">direct</label> <label for="direct">direct</label>
<input type="radio" name="maintabs" id="chat" class="tab"> <input type="radio" name="maintabs" id="chat" class="tab">
<label for="chat">chat</label> --> <label for="chat">chat</label> -->
<input type="radio" name="maintabs" id="settings" class="tab">
<label for="settings">profile</label>
<div class="tabs"> <div class="tabs">
<div class="tab-content"> <div class="tab-content">
<article class="mbox"> <artcile>
<img class="mbox-img" id="bubble" src="assets/comment.svg" alt=""> <svg id="bubble" xmlns="http://www.w3.org/2000/svg" width="24" height="21" viewBox="0 0 80.035 70.031">
<div class="mbox-body" id="newMessage"> <path fill="var(--bgcolor-textinput)" stroke="currentColor" stroke-width="4.927" d="M2.463 31.824q0-4.789 1.893-9.248 1.892-4.46 5.361-8.087 3.47-3.626 8.07-6.333 4.598-2.707 10.324-4.2 5.727-1.493 11.836-1.493 6.107 0 11.834 1.492 5.725 1.494 10.325 4.2 4.599 2.708 8.07 6.334 3.47 3.627 5.362 8.087 1.891 4.46 1.891 9.248 0 5.97-2.967 11.384-2.967 5.414-7.982 9.336-5.015 3.922-11.957 6.248-6.94 2.325-14.576 2.325-7.463 0-14.334-2.221l-8.537 6.038q-4.789 3.02-6.733 1.752-1.943-1.266-.867-7.13l1.77-8.886q-4.2-3.887-6.49-8.71-2.29-4.825-2.29-10.136Z"/>
<form action="#" class="form-inline" id="writeForm"> </svg>
<div id="newMessage" hidden>
<form action="#" id="writeForm" class="form-inline">
<fieldset>
<legend>write a new post</legend>
<textarea name="message" rows="1"></textarea> <textarea name="message" rows="1"></textarea>
<div class="buttons">
<button type="submit" id="publish" disabled>send</button> <button type="submit" id="publish" disabled>send</button>
</form> <button type="button" name="back">back</button>
</div>
<small id="sendstatus" class="form-status"></small> <small id="sendstatus" class="form-status"></small>
</fieldset>
</form>
</div> </div>
</article> </artcile>
<div class="cards" id="homefeed"></div> <div class="cards" id="homefeed"></div>
</div> </div>

@ -1,6 +1,7 @@
@import "tabs.css"; @import "tabs.css";
@import "cards.css"; @import "cards.css";
@import "form.css"; @import "form.css";
@import "write.css";
:root { :root {
/* 5px auto Highlight */ /* 5px auto Highlight */
@ -12,6 +13,7 @@
--focus-outline-width: 2px; --focus-outline-width: 2px;
--focus-outline: var(--focus-outline-width) var(--focus-outline-style) var(--focus-outline-color); --focus-outline: var(--focus-outline-width) var(--focus-outline-style) var(--focus-outline-color);
--font-small: 1.2rem; --font-small: 1.2rem;
--gap: 2.4rem;
} }
::selection { ::selection {
@ -26,7 +28,7 @@
@media (prefers-color-scheme: light) { @media (prefers-color-scheme: light) {
html { html {
--bgcolor: #fdfefa; --bgcolor: #fdfefa;
--bgcolor-accent: #37ff1d; --bgcolor-accent: #7badfc;
--bgcolor-inactive: #bababa; --bgcolor-inactive: #bababa;
--bgcolor-textinput: #fff; --bgcolor-textinput: #fff;
--color: rgb(68 68 68); --color: rgb(68 68 68);
@ -39,7 +41,7 @@
html { html {
--bgcolor: #191919; --bgcolor: #191919;
--bgcolor-accent: #1e437d; --bgcolor-accent: #1e437d;
--bgcolor-inactive: #333333; --bgcolor-inactive: #434343;
--bgcolor-textinput: #0e0e0e; --bgcolor-textinput: #0e0e0e;
--color: #fff; --color: #fff;
--color-accent: #bbb;; --color-accent: #bbb;;
@ -65,6 +67,7 @@ body {
color: var(--color); color: var(--color);
font-size: 1.6rem; font-size: 1.6rem;
line-height: 1.5; line-height: 1.5;
margin: 0;
} }
body, body,

@ -3,8 +3,8 @@ import {elem, multilineText} from './domutil.js';
import {dateTime, formatTime} from './timeutil.js'; import {dateTime, formatTime} from './timeutil.js';
// curl -H 'accept: application/nostr+json' https://nostr.x1ddos.ch // curl -H 'accept: application/nostr+json' https://nostr.x1ddos.ch
const pool = relayPool(); const pool = relayPool();
// pool.addRelay('wss://relay.nostr.info', {read: true, write: true}); pool.addRelay('wss://relay.nostr.info', {read: true, write: true});
// pool.addRelay('wss://relay.damus.io', {read: true, write: true}); pool.addRelay('wss://relay.damus.io', {read: true, write: true});
pool.addRelay('wss://nostr.x1ddos.ch', {read: true, write: true}); pool.addRelay('wss://nostr.x1ddos.ch', {read: true, write: true});
// pool.addRelay('wss://nostr.openchain.fr', {read: true, write: true}); // pool.addRelay('wss://nostr.openchain.fr', {read: true, write: true});
// pool.addRelay('wss://nostr.bitcoiner.social/', {read: true, write: true}); // pool.addRelay('wss://nostr.bitcoiner.social/', {read: true, write: true});
@ -112,7 +112,7 @@ function handleReaction(evt, relay) {
if (evt.pubkey === pubkey) { if (evt.pubkey === pubkey) {
const star = button.querySelector('img[src$="star.svg"]'); const star = button.querySelector('img[src$="star.svg"]');
star.setAttribute('src', 'assets/star-fill.svg'); star.setAttribute('src', 'assets/star-fill.svg');
star.setAttribute('title', reactionMap[eventId]) star.setAttribute('title', reactionMap[eventId]);
} }
} }
} }
@ -155,7 +155,7 @@ setInterval(() => {
function createTextNote(evt, relay) { function createTextNote(evt, relay) {
const {host, img, isReply, replies, time, userName} = getMetadata(evt, relay); const {host, img, isReply, replies, time, userName} = getMetadata(evt, relay);
const isLongContent = evt.content.length > 280; const isLongContent = evt.content.trimRight().length > 280;
const content = isLongContent ? `${evt.content.slice(0, 280)}` : evt.content; const content = isLongContent ? `${evt.content.slice(0, 280)}` : evt.content;
const hasReactions = reactionMap[evt.id]?.length > 0; const hasReactions = reactionMap[evt.id]?.length > 0;
const didReact = hasReactions && !!reactionMap[evt.id].find(reaction => reaction.pubkey === pubkey); const didReact = hasReactions && !!reactionMap[evt.id].find(reaction => reaction.pubkey === pubkey);
@ -190,10 +190,16 @@ function createTextNote(evt, relay) {
className: 'btn-inline', name: 'reply', type: 'button', className: 'btn-inline', name: 'reply', type: 'button',
data: {'eventId': evt.id, relay}, data: {'eventId': evt.id, relay},
}, [elem('img', {height: 24, width: 24, src: 'assets/comment.svg'})]), }, [elem('img', {height: 24, width: 24, src: 'assets/comment.svg'})]),
// replies[0] ? elem('div', {className: 'mobx-replies'}, replyFeed.reverse()) : '',
]);
if (restoredReplyTo === evt.id) {
appendReplyForm(body.querySelector('button[name="reply"]'));
requestAnimationFrame(() => updateElemHeight(writeInput));
}
return rendernArticle([
elem('div', {className: 'mbox-img'}, [img]), body,
replies[0] ? elem('div', {className: 'mobx-replies'}, replyFeed.reverse()) : '', replies[0] ? elem('div', {className: 'mobx-replies'}, replyFeed.reverse()) : '',
]); ]);
if (restoredReplyTo === evt.id) appendReplyForm(body.querySelector('button[name="reply"]'));
return rendernArticle([img, body]);
} }
function handleReply(evt, relay) { function handleReply(evt, relay) {
@ -214,7 +220,7 @@ function renderReply(evt, relay) {
let replyContainer = article.querySelector('.mobx-replies'); let replyContainer = article.querySelector('.mobx-replies');
if (!replyContainer) { if (!replyContainer) {
replyContainer = elem('div', {className: 'mobx-replies'}); replyContainer = elem('div', {className: 'mobx-replies'});
article.querySelector('.mbox-body').append(replyContainer); article.append(replyContainer);
} }
const reply = createTextNote(evt, relay); const reply = createTextNote(evt, relay);
replyContainer.append(reply); replyContainer.append(reply);
@ -291,7 +297,7 @@ function renderRecommendServer(evt, relay) {
]), ]),
` recommends server: ${evt.content}`, ` recommends server: ${evt.content}`,
]); ]);
return rendernArticle([img, body], {className: 'mbox-recommend-server', data: {relay: evt.content}}); return rendernArticle([elem('div', {className: 'mbox-img'}, [img]), body], {className: 'mbox-recommend-server', data: {relay: evt.content}});
} }
function rendernArticle(content, props = {}) { function rendernArticle(content, props = {}) {
@ -349,7 +355,6 @@ function getMetadata(evt, relay) {
const userName = user?.metadata[relay]?.name || evt.pubkey.slice(0, 8); const userName = user?.metadata[relay]?.name || evt.pubkey.slice(0, 8);
const userAbout = user?.metadata[relay]?.about || ''; const userAbout = user?.metadata[relay]?.about || '';
const img = elem('img', { const img = elem('img', {
className: 'mbox-img',
src: userImg, src: userImg,
alt: `${userName} ${host}`, alt: `${userName} ${host}`,
title: `${userName} on ${host} ${userAbout}`, title: `${userName} on ${host} ${userAbout}`,
@ -378,19 +383,45 @@ const writeForm = document.querySelector('#writeForm');
const writeInput = document.querySelector('textarea[name="message"]'); const writeInput = document.querySelector('textarea[name="message"]');
function appendReplyForm(el) { function appendReplyForm(el) {
const shrink = elem('div', {className: 'shrink-out'});
shrink.style.height = `${writeInput.style.height || writeInput.getBoundingClientRect().height}px`;
shrink.addEventListener('animationend', () => shrink.remove());
writeForm.before(shrink);
writeInput.blur();
writeInput.style.removeProperty('height');
el.after(writeForm); el.after(writeForm);
el.after(sendStatus); if (writeInput.value && !writeInput.value.trimRight()) {
writeInput.focus(); writeInput.value = '';
} else {
requestAnimationFrame(() => updateElemHeight(writeInput));
}
requestAnimationFrame(() => writeInput.focus());
} }
const newMessageDiv = document.querySelector('#newMessage'); const newMessageDiv = document.querySelector('#newMessage');
document.querySelector('#bubble').addEventListener('click', (e) => { document.querySelector('#bubble').addEventListener('click', (e) => {
localStorage.removeItem('reply_to'); localStorage.removeItem('reply_to'); // should it forget old replyto context?
newMessageDiv.prepend(writeForm); newMessageDiv.prepend(writeForm);
newMessageDiv.append(sendStatus); hideNewMessage(false);
writeInput.focus(); writeInput.focus();
if (writeInput.value.trimRight()) {
writeInput.style.removeProperty('height');
}
document.body.style.overflow = 'hidden';
requestAnimationFrame(() => updateElemHeight(writeInput));
});
document.body.addEventListener('keyup', (e) => {
if (e.key === 'Escape') {
hideNewMessage(true);
}
}); });
function hideNewMessage(hide) {
document.body.style.removeProperty('overflow');
newMessageDiv.hidden = hide;
}
async function upvote(eventId, relay) { async function upvote(eventId, relay) {
const privatekey = localStorage.getItem('private_key'); const privatekey = localStorage.getItem('private_key');
const newReaction = { const newReaction = {
@ -415,10 +446,7 @@ async function upvote(eventId, relay) {
// send // send
const sendStatus = document.querySelector('#sendstatus'); const sendStatus = document.querySelector('#sendstatus');
const onSendError = err => { const onSendError = err => sendStatus.textContent = err.message;
sendStatus.textContent = err.message;
sendStatus.hidden = false;
};
const publish = document.querySelector('#publish'); const publish = document.querySelector('#publish');
writeForm.addEventListener('submit', async (e) => { writeForm.addEventListener('submit', async (e) => {
e.preventDefault(); e.preventDefault();
@ -447,14 +475,13 @@ writeForm.addEventListener('submit', async (e) => {
console.info(`publish request sent to ${url}`); console.info(`publish request sent to ${url}`);
} }
if (status === 1) { if (status === 1) {
sendStatus.hidden = true; sendStatus.textContent = '';
writeInput.value = ''; writeInput.value = '';
writeInput.style.removeProperty('height'); writeInput.style.removeProperty('height');
publish.disabled = true; publish.disabled = true;
if (replyTo) { if (replyTo) {
localStorage.removeItem('reply_to'); localStorage.removeItem('reply_to');
newMessageDiv.append(writeForm); newMessageDiv.append(writeForm);
newMessageDiv.append(sendStatus);
} }
// console.info(`event published by ${url}`, ev); // console.info(`event published by ${url}`, ev);
} }
@ -547,4 +574,8 @@ document.body.addEventListener('click', (e) => {
delete append.dataset.append; delete append.dataset.append;
return; return;
} }
const back = e.target.closest('[name="back"]')
if (back) {
hideNewMessage(true);
}
}); });

@ -1,4 +1,7 @@
.tabs { margin-top: 4rem; } .tabs {
flex-basis: 100%;
margin-top: 4rem;
}
.tabs .tab-content { display: none; } .tabs .tab-content { display: none; }
#feed:checked ~ .tabs .tab-content:nth-child(1), #feed:checked ~ .tabs .tab-content:nth-child(1),
#trending:checked ~ .tabs .tab-content:nth-child(2), #trending:checked ~ .tabs .tab-content:nth-child(2),
@ -15,13 +18,17 @@ input[type="radio"].tab {
} }
.tab + label { .tab + label {
background-color: var(--bgcolor-textinput);
border: none; border: none;
color: var(--color); color: var(--color);
display: inline-block; display: inline-block;
margin-left: var(--gap);
margin-top: var(--gap);
outline: 2px solid var(--bgcolor-accent); outline: 2px solid var(--bgcolor-accent);
padding: 1rem 1.5em; padding: 1rem 1.5em;
position: relative; position: relative;
top: 1px; top: 1px;
z-index: 11;
} }
input[type="radio"]:checked + label { input[type="radio"]:checked + label {
background: var(--bgcolor-accent); background: var(--bgcolor-accent);
@ -38,18 +45,34 @@ input[type="radio"]:checked + label {
.tab-content { .tab-content {
max-width: 96ch; max-width: 96ch;
min-height: 200px; min-height: 200px;
padding: calc(.5 * var(--gap)) 0 100px 0;
} }
.tabbed {
/* align-items: start;
display: flex;
flex-wrap: wrap;
.tab {
float: left;
} }
@media (orientation: portrait) {
.tab > label { .tabbed {
align-items: start;
display: flex;
flex-direction: row-reverse;
flex-wrap: wrap;
justify-content: start;
} }
.tabs {
height: 100vh;
margin-top: 0;
order: 1;
overflow: scroll;
width: 100vw;
}
.tab + label {
margin-top: calc(-3 * var(--gap));
margin-left: var(--gap);
order: 2;
}
.cards {
}
*/ }

@ -0,0 +1,51 @@
#bubble {
bottom: 4rem;
height: 10rem;
padding: 0;
position: fixed;
right: 5rem;
width: 10rem;
z-index: 12;
}
@media (orientation: portrait) {
#bubble {
bottom: calc(2 * var(--gap));
right: var(--gap);
}
}
#newMessage {
align-items: center;
display: flex;
height: 100vh;
justify-content: center;
position: fixed;
top: 0;
width: 100vw;
z-index: 20;
}
#newMessage #writeForm {
align-items: start;
background-color: var(--bgcolor);
display: flex;
flex-direction: row;
flex-grow: 1;
flex-wrap: wrap;
gap: 0;
justify-content: end;
max-height: 100vh;
min-height: 64vh;
outline: 100vh solid var(--bgcolor);
overflow-y: auto;
padding: 2rem;
}
#newMessage .form-inline textarea {
flex-basis: 100%;
margin: var(--gap) 0;
}
#newMessage .buttons {
align-self: end;
}
Loading…
Cancel
Save