[chore] Refactor HTML templates and CSS (#2480)

* [chore] Refactor HTML templates and CSS

* eslint

* ignore "Local"

* rss tests

* fiddle with OG just a tiny bit

* dick around with polls a bit more so SR stops saying "clickable"

* remove break

* oh lord

* don't lazy load avatar

* fix ogmeta tests

* clean up some cruft

* catch remaining calls to c.HTML

* fix error rendering + stack overflow in tag

* allow templating attributes

* fix indent

* set aria-hidden on status complementary content, since it's already present in the label anyway

* tidy up templating calls a little

* try to make styling a bit more consistent + readable

* fix up some remaining CSS issues

* fix up reports
This commit is contained in:
tobi
2023-12-27 11:23:52 +01:00
committed by GitHub
parent 97a1fd9a29
commit 0ff52b71f2
77 changed files with 3262 additions and 1736 deletions

View File

@ -1 +1,3 @@
node_modules
node_modules
prism.js
prism.css

View File

@ -82,11 +82,11 @@ $button-danger-bg: $error3;
$button-danger-fg: $white1;
$button-danger-hover-bg: $error2;
$toot-bg: $gray3;
$toot-info-bg: $gray2;
$status-bg: $gray3;
$status-info-bg: $gray2;
$toot-focus-bg: $gray5;
$toot-focus-info-bg: $gray4;
$status-focus-bg: $gray5;
$status-focus-info-bg: $gray4;
$no-img-desc-bg: $orange1;
$no-img-desc-fg: $gray1;

39
web/source/css/about.css Normal file
View File

@ -0,0 +1,39 @@
/*
GoToSocial
Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
.about {
display: flex;
flex-direction: column;
gap: 2rem;
padding: 2rem;
background: $bg-accent;
box-shadow: $boxshadow;
border: $boxshadow-border;
border-radius: $br;
.about-section {
ul, ol {
margin-top: 0;
}
h3, h4 {
margin-top: 0;
}
}
}

View File

@ -20,35 +20,52 @@
/* noto-sans-regular - latin */
@font-face {
font-family: "Noto Sans";
font-weight: 400;
font-display: swap;
font-style: normal;
src: url('../fonts/noto-sans-v27-latin-regular.woff2') format('woff2'),
url('../fonts/noto-sans-v27-latin-regular.woff') format('woff');
font-family: "Noto Sans";
font-weight: 400;
font-display: swap;
font-style: normal;
src: url('../fonts/noto-sans-v27-latin-regular.woff2') format('woff2'),
url('../fonts/noto-sans-v27-latin-regular.woff') format('woff');
}
/* noto-sans-700 - latin */
@font-face {
font-family: "Noto Sans";
font-weight: 700;
font-display: swap;
font-style: normal;
src: url('../fonts/noto-sans-v27-latin-700.woff2') format('woff2'),
url('../fonts/noto-sans-v27-latin-700.woff') format('woff');
font-family: "Noto Sans";
font-weight: 700;
font-display: swap;
font-style: normal;
src: url('../fonts/noto-sans-v27-latin-700.woff2') format('woff2'),
url('../fonts/noto-sans-v27-latin-700.woff') format('woff');
}
/* standard border radius for nice squircles */
/*************************************
***** SECTION 1: HANDY VARIABLES *****
**************************************/
/*
Standard border radius
for nice squircles.
*/
$br: 0.4rem;
/* border radius for items that are framed/bordered
inside something with $br, eg avatar, header img */
/*
Border radius for items that
are framed/bordered inside
something with $br, eg avatar,
header img, etc.
*/
$br-inner: 0.2rem;
/* Fork-Awesome 'fa-fw' fixed icon width
keep in sync with https://github.com/ForkAwesome/Fork-Awesome/blob/a99579ae3e735ee70e51ed62dfcee3172b5b2db7/css/fork-awesome.css#L50
/*
Fork-Awesome 'fa-fw' fixed icon width;
keep in sync with https://github.com/ForkAwesome/Fork-Awesome/blob/a99579ae3e735ee70e51ed62dfcee3172b5b2db7/css/fork-awesome.css#L50
*/
$fa-fw: 1.28571429em;
/******************************************
***** SECTION 2: BASIC GLOBAL STYLING *****
*******************************************/
html, body {
padding: 0;
margin: 0;
@ -63,90 +80,28 @@ body {
position: relative;
}
.hidden {
display: none;
}
.page {
display: grid;
min-height: 100vh;
grid-template-columns: 1fr minmax(auto, 50rem) 1fr;
grid-template-columns: 1fr min(92%, 50rem) 1fr;
grid-template-rows: auto 1fr auto;
}
h1 {
margin: 0;
line-height: 2.4rem;
}
a {
color: $link-fg;
}
header, footer {
grid-column: 1 / span 3;
}
.content {
grid-column: 2;
align-self: start;
}
header {
display: flex;
justify-content: center;
}
header a {
display: flex;
flex-wrap: wrap;
margin: 1.5rem;
gap: 1rem;
justify-content: center;
img {
align-self: center;
height: 3rem;
}
h1 {
flex-grow: 1;
align-self: center;
text-align: center;
font-size: 1.5rem;
word-wrap: anywhere;
color: $fg;
}
}
.excerpt-top {
margin-bottom: 2rem;
font-style: italic;
font-weight: normal;
text-align: center;
font-size: 1.2rem;
.count {
font-weight: bold;
color: $fg-accent;
}
}
/*
Normalize margins of first and last children.
We generally don't want to open a paragraph or
paragraph-like element with a top margin or
close it with a bottom margin.
*/
main {
p:first-child {
p:first-child, ol:first-child, ul:first-child {
margin-top: 0;
}
p:last-child {
p:last-child, ol:last-child, ul:last-child {
margin-bottom: 0;
}
}
.button, button {
border-radius: 0.2rem;
border-radius: $br-inner;
color: $button-fg;
background: $button-bg;
box-shadow: $boxshadow;
@ -184,6 +139,166 @@ main {
}
}
/*
Form styling - used in settings frontend as well.
*/
input, select, textarea, .input {
box-sizing: border-box;
border: 0.15rem solid $input-border;
border-radius: 0.1rem;
color: $fg;
background: $input-bg;
width: 100%;
font-family: 'Noto Sans', sans-serif;
font-size: 1rem;
padding: 0.3rem;
&:focus, &:active {
border-color: $input-focus-border;
}
&:invalid, .invalid & {
border-color: $input-error-border;
}
&:disabled {
background: transparent;
}
&::placeholder {
opacity: 1;
color: $fg-reduced
}
}
/*
Squeeze emojis so they fit inline in text.
*/
.emoji {
width: 1.45em;
height: 1.45em;
margin: -0.2em 0.02em 0;
object-fit: contain;
vertical-align: middle;
transition: 0.1s;
/*
Enlarge emojis on hover to give
viewer a good look at them.
*/
&:hover, &:active {
transform: scale(2);
background-color: $bg;
box-shadow: $boxshadow;
border: $boxshadow-border;
border-radius: $br-inner;
}
}
/*
Restyle unordered lists; outdent
and replace dot with orange dot.
*/
ul {
padding-left: 2.5rem;
list-style: none;
li::before {
content: "\2022";
color: $border-accent;
font-weight: bold;
display: inline-block;
width: 1.5rem;
margin-left: -1.5rem;
}
}
/*
Mirror the same styling a little bit
for ordered lists by making marker bold.
*/
ol {
padding-left: 2.5rem;
li::marker {
font-weight: bold;
}
}
/*
Outdent block quotes a bit; use
orange stripe for left border.
*/
blockquote {
padding: 0.5rem 0 0.5rem 0.5rem;
border-left: 0.2rem solid $border-accent;
margin: 0;
font-style: italic;
}
/*
Nice dashed orange line
for horizontal rules.
*/
hr {
border: 0;
border-top: 1px dashed $border-accent;
}
/*
Don't indent definition
lists and definitions.
*/
dl {
margin: 0;
dd {
margin-left: 0;
}
}
label {
cursor: pointer;
}
/*************************************
***** SECTION 3: UTILITY CLASSES *****
**************************************/
/*
Column header that appears at the top
of threads, at the top of sections of
profiles (About, Pinned Posts, etc).
*/
.col-header {
display: grid;
grid-template-columns: auto 1fr;
gap: 1rem;
justify-content: start;
align-items: center;
margin: 0;
background: $profile-bg;
border-top-left-radius: $br;
border-top-right-radius: $br;
padding: 0.75rem;
a {
justify-self: end;
}
h1, h2, h3, h4 {
font-size: 1.2rem;
line-height: 1.3rem;
margin: 0;
}
}
.hidden {
display: none;
}
.nounderline {
text-decoration: none;
}
@ -192,57 +307,37 @@ main {
color: $acc1;
}
.logo {
justify-self: center;
img {
height: 30vh;
.text-cutoff {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
/*
Class for lists that don't
want the orange dot.
*/
.nodot {
li::before {
content: initial;
}
}
section.apps {
align-self: start;
/***********************************
***** SECTION 4: SHAMEFUL MESS *****
************************************/
.applist {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 0.5rem;
align-content: start;
/*
EVERYTHING BELOW THIS POINT:
Should be moved somewhere else
to avoid cluttering up this file.
*/
.entry {
display: grid;
grid-template-columns: 25% 1fr;
gap: 1.5rem;
padding: 0.5rem;
background: $bg-accent;
border-radius: 0.5rem;
.logo {
align-self: center;
width: 100%;
object-fit: contain;
flex: 1 1 auto;
}
.logo.redraw {
fill: $fg;
stroke: $fg;
}
a {
font-weight: bold;
}
div {
padding: 0;
h3 {
margin-top: 0;
}
}
}
}
}
section.login {
/*
Below section stylings are used
in transient/error templates.
*/
section.sign-in {
form {
display: flex;
flex-direction: column;
@ -291,98 +386,11 @@ section.oob-token {
}
}
.error-text {
color: $error1;
background: $error2;
border-radius: 0.1rem;
font-weight: bold;
}
input, select, textarea, .input {
box-sizing: border-box;
border: 0.15rem solid $input-border;
border-radius: 0.1rem;
color: $fg;
background: $input-bg;
width: 100%;
font-family: 'Noto Sans', sans-serif;
font-size: 1rem;
padding: 0.3rem;
&:focus, &:active {
border-color: $input-focus-border;
}
&:invalid, .invalid & {
border-color: $input-error-border;
}
&:disabled {
background: transparent;
}
}
::placeholder {
opacity: 1;
color: $fg-reduced
}
hr {
color: transparent;
width: 100%;
border-bottom: 0.02rem solid $border-accent;
}
footer {
align-self: end;
padding: 2rem 0 1rem 0;
display: flex;
flex-wrap: wrap;
justify-content: center;
div {
text-align: center;
padding: 1rem;
flex-grow: 1;
}
a {
font-weight: bold;
}
}
@media screen and (max-width: 600px) {
header {
text-align: center;
}
footer {
grid-template-columns: 1fr;
div {
text-align: initial;
width: 100%;
}
}
section.apps .applist {
grid-template-columns: 1fr;
}
}
.emoji {
width: 1.45em;
height: 1.45em;
margin: -0.2em 0.02em 0;
object-fit: contain;
vertical-align: middle;
}
.monospace {
font-family: monospace;
}
/*
TODO: This is only used in the "finalize"
template for new signups; move this elsewhere
when that stuff is finished up.
*/
.callout {
margin: 1.5rem 0;
border: .05rem solid $border-accent;
@ -397,22 +405,11 @@ footer {
}
}
label {
cursor: pointer;
}
@media (prefers-reduced-motion) {
.fa-spin {
animation: none;
}
}
.text-cutoff {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
/*
TODO: list and blocklist are only used
in settings panel and on blocklist page;
consider moving them somewhere else.
*/
.list {
display: flex;
flex-direction: column;
@ -495,21 +492,18 @@ label {
}
}
.about {
display: flex;
flex-direction: column;
gap: 1rem;
h2 {
margin: 0.5rem 0;
@media screen and (max-width: 30rem) {
.domain-blocklist .entry {
grid-template-columns: 1fr;
gap: 0;
}
ul {
margin-bottom: 0;
}
}
/*
TODO: this is only used on About
page and in settings application;
consider moving it somewhere else.
*/
.account-card {
display: inline-grid;
grid-template-columns: auto 1fr;
@ -541,61 +535,3 @@ label {
grid-row: 1 / span 2;
}
}
.instance-rules {
list-style-position: inside;
margin: 0;
padding: 0;
a.rule {
display: grid;
grid-template-columns: 1fr auto;
align-items: center;
color: $fg;
text-decoration: none;
background: $toot-bg;
padding: 1rem;
margin: 0.5rem 0;
border-radius: $br;
line-height: 2rem;
position: relative;
&:hover {
color: $fg-accent;
.edit-icon {
display: inline;
}
}
.edit-icon {
display: none;
font-size: 1rem;
line-height: 1.5rem;
}
li {
font-size: 1.75rem;
padding: 0;
margin: 0;
h2 {
margin: 0;
margin-top: 0 !important;
display: inline-block;
font-size: 1.5rem;
}
}
span {
color: $fg-reduced;
}
}
}
@media screen and (max-width: 30rem) {
.domain-blocklist .entry {
grid-template-columns: 1fr;
gap: 0;
}
}

View File

@ -16,26 +16,85 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
header a {
margin: 2rem;
gap: 2rem;
img {
height: 6rem;
}
h1 {
font-size: 2rem;
}
/*
Render instance title a
bit bigger on index page.
*/
.page-header a h1 {
font-size: 2rem;
line-height: 2rem;
}
main {
section {
/*
Reuse about styling, but rework it
to separate sections a bit more.
*/
.about {
display: flex;
flex-direction: column;
gap: 2rem;
padding: 0;
background: initial;
box-shadow: initial;
border: initial;
border-radius: initial;
.about-section {
padding: 2rem;
background: $bg-accent;
box-shadow: $boxshadow;
border: $boxshadow-border;
border-radius: $br;
padding: 2rem;
margin-bottom: 2rem;
}
}
}
.apps {
align-self: start;
.applist {
margin: 0;
padding: 0;
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 0.5rem;
align-content: start;
.applist-entry {
display: grid;
grid-template-columns: 25% 1fr;
grid-template-areas: "logo text";
gap: 1.5rem;
padding: 0.5rem;
.applist-logo {
grid-area: logo;
align-self: center;
justify-self: center;
width: 100%;
object-fit: contain;
flex: 1 1 auto;
}
.applist-logo.redraw {
fill: $fg;
stroke: $fg;
}
.applist-text {
grid-area: text;
a {
font-weight: bold;
}
}
}
}
}
@media screen and (max-width: 600px) {
.apps .applist {
grid-template-columns: 1fr;
}
}

107
web/source/css/page.css Normal file
View File

@ -0,0 +1,107 @@
/*
GoToSocial
Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
.page {
display: grid;
min-height: 100vh;
grid-template-columns: 1fr minmax(auto, 50rem) 1fr;
grid-template-columns: 1fr min(92%, 50rem) 1fr;
grid-template-rows: auto 1fr auto;
}
.page-header, .page-footer {
grid-column: 1 / span 3;
}
.page-content {
grid-column: 2;
align-self: start;
}
.page-header {
display: flex;
flex-direction: column;
justify-content: center;
padding: 1.5rem;
gap: 1rem;
a {
display: flex;
flex-wrap: wrap;
gap: 1rem;
justify-content: center;
img {
align-self: center;
}
h1 {
align-self: center;
text-align: center;
font-size: 1.5rem;
line-height: 1.5rem;
word-wrap: anywhere;
color: $fg;
}
}
aside {
margin: 0;
font-style: italic;
font-weight: normal;
text-align: center;
font-size: 1.2rem;
.count {
font-weight: bold;
color: $fg-accent;
}
}
}
.page-footer {
align-self: end;
nav ul {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
/* Override list styling */
list-style-type: none;
padding-left: 0;
li {
text-align: center;
padding: 1rem;
flex-grow: 1;
a {
font-weight: bold;
}
}
}
}
@media screen and (max-width: 600px) {
.page-header {
text-align: center;
}
}

5
web/source/css/prism.css Normal file
View File

@ -0,0 +1,5 @@
/* PrismJS 1.29.0
https://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+css+clike+javascript+bash+c+csharp+cpp+docker+elixir+erlang+go+go-module+ini+java+json+kotlin+lua+makefile+markup-templating+nginx+nix+perl+php+promql+python+r+jsx+tsx+ruby+rust+scala+sql+swift+typescript&plugins=show-invisibles+show-language+toolbar+copy-to-clipboard */
code[class*=language-],pre[class*=language-]{color:#ccc;background:0 0;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#999}.token.punctuation{color:#ccc}.token.attr-name,.token.deleted,.token.namespace,.token.tag{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.function,.token.number{color:#f08d49}.token.class-name,.token.constant,.token.property,.token.symbol{color:#f8c555}.token.atrule,.token.builtin,.token.important,.token.keyword,.token.selector{color:#cc99cd}.token.attr-value,.token.char,.token.regex,.token.string,.token.variable{color:#7ec699}.token.entity,.token.operator,.token.url{color:#67cdcc}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:green}
.token.cr,.token.lf,.token.space,.token.tab:not(:empty){position:relative}.token.cr:before,.token.lf:before,.token.space:before,.token.tab:not(:empty):before{color:grey;opacity:.6;position:absolute}.token.tab:not(:empty):before{content:'\21E5'}.token.cr:before{content:'\240D'}.token.crlf:before{content:'\240D\240A'}.token.lf:before{content:'\240A'}.token.space:before{content:'\00B7'}
div.code-toolbar{position:relative}div.code-toolbar>.toolbar{position:absolute;z-index:10;top:.3em;right:.2em;transition:opacity .3s ease-in-out;opacity:0}div.code-toolbar:hover>.toolbar{opacity:1}div.code-toolbar:focus-within>.toolbar{opacity:1}div.code-toolbar>.toolbar>.toolbar-item{display:inline-block}div.code-toolbar>.toolbar>.toolbar-item>a{cursor:pointer}div.code-toolbar>.toolbar>.toolbar-item>button{background:0 0;border:0;color:inherit;font:inherit;line-height:normal;overflow:visible;padding:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}div.code-toolbar>.toolbar>.toolbar-item>a,div.code-toolbar>.toolbar>.toolbar-item>button,div.code-toolbar>.toolbar>.toolbar-item>span{color:#bbb;font-size:.8em;padding:0 .5em;background:#f5f2f0;background:rgba(224,224,224,.2);box-shadow:0 2px 0 0 rgba(0,0,0,.2);border-radius:.5em}div.code-toolbar>.toolbar>.toolbar-item>a:focus,div.code-toolbar>.toolbar>.toolbar-item>a:hover,div.code-toolbar>.toolbar>.toolbar-item>button:focus,div.code-toolbar>.toolbar>.toolbar-item>button:hover,div.code-toolbar>.toolbar>.toolbar-item>span:focus,div.code-toolbar>.toolbar>.toolbar-item>span:hover{color:inherit;text-decoration:none}

View File

@ -17,28 +17,27 @@
*/
.page {
grid-template-columns: 1fr minmax(auto, 60rem) 1fr; /* fallback for lack of min() support */
/*
Profile page can be a little wider than default
page, since we're using a side-by-side column view.
*/
grid-template-columns: 1fr minmax(auto, 60rem) 1fr;
grid-template-columns: 1fr min(92%, 65rem) 1fr;
}
.profile {
padding: 0.5rem;
border-radius: $br;
.column-split {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.profile .column-split {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.profile .header {
.profile .profile-header {
background: $profile-bg;
border-radius: $br;
overflow: hidden;
margin-bottom: 1rem;
.header-image {
.header-image-wrapper {
position: relative;
padding-top: 33.33%; /* aspect-ratio 1/3 */
@ -55,12 +54,11 @@
/*
Basic info container has the user's avatar, display- and username, and role
It's partially overlapped over the header image, by a negative margin-top
It's partially overlapped over the header image, by a negative margin-top.
*/
$avatar-size: 8.5rem;
$name-size: 3rem;
$username-size: 2rem;
$overlap: calc($avatar-size - $name-size - $username-size);
.basic-info {
@ -71,8 +69,8 @@
grid-template-rows: $overlap $name-size auto;
grid-template-areas:
"avatar . ."
"avatar displayname displayname"
"avatar username role";
"avatar namerole namerole"
"avatar namerole namerole";
margin: 1rem;
margin-top: calc(-1 * $overlap);
@ -93,131 +91,119 @@
}
}
.displayname {
grid-area: displayname;
line-height: $name-size;
font-size: 1.5rem;
font-weight: bold;
}
.namerole {
grid-area: namerole;
.username {
min-width: 0;
grid-area: username;
line-height: $username-size;
display: grid;
gap: 0 1rem;
box-sizing: border-box;
grid-template-columns: auto 1fr;
grid-template-rows: $name-size auto;
grid-template-areas:
"displayname displayname"
"username role";
font-size: 1rem;
font-weight: bold;
color: $fg-accent;
user-select: all;
}
.role {
background: $bg;
color: $fg;
border: 0.13rem solid $bg;
grid-area: role;
align-self: center;
justify-self: start;
border-radius: $br;
padding: 0.3rem;
line-height: 1.1rem;
font-size: 0.9rem;
font-variant: small-caps;
font-weight: bold;
&.admin {
color: $role-admin;
border-color: $role-admin;
.displayname {
grid-area: displayname;
line-height: $name-size;
font-size: 1.5rem;
font-weight: bold;
}
&.moderator {
color: $role-mod;
border-color: $role-mod;
.username {
min-width: 0;
grid-area: username;
line-height: $username-size;
font-size: 1rem;
font-weight: bold;
color: $fg-accent;
user-select: all;
}
.role {
background: $bg;
color: $fg;
border: 0.13rem solid $bg;
grid-area: role;
align-self: center;
justify-self: start;
border-radius: $br;
padding: 0.3rem;
line-height: 1.1rem;
font-size: 0.9rem;
font-variant: small-caps;
font-weight: bold;
&.admin {
color: $role-admin;
border-color: $role-admin;
}
&.moderator {
color: $role-mod;
border-color: $role-mod;
}
}
}
}
}
@media screen and (max-width: 750px) {
.profile .header {
.profile .profile-header {
.basic-info {
grid-template-columns: auto 1fr;
grid-template-rows: $avatar-size $name-size auto;
grid-template-areas:
"avatar avatar"
"displayname displayname"
"username role";
.displayname {
font-size: 1.4rem;
"namerole namerole"
"namerole namerole";
.namerole {
grid-template-columns: 1fr;
grid-template-rows: $name-size auto;
grid-template-areas:
"displayname displayname"
"username role";
.displayname {
font-size: 1.4rem;
}
}
}
}
}
.profile .col-header {
display: flex;
justify-content: start;
gap: 2rem;
align-items: center;
margin: 0;
background: $profile-bg;
border-top-left-radius: $br;
border-top-right-radius: $br;
padding: 0.75rem;
h1, h2 {
font-size: 1.2rem;
line-height: 1.3rem;
margin: 0;
}
}
.profile .toots {
.profile .statuses-wrapper {
flex: 65 25rem;
display: flex;
flex-direction: column;
gap: 0.4rem;
min-width: 0%;
}
.col-header {
display: grid;
grid-template-columns: auto 1fr;
gap: 1rem;
.profile .statuses {
display: flex;
flex-direction: column;
gap: 0.4rem;
a {
justify-self: end;
}
.rss-icon {
display: block;
margin: -0.25rem 0;
.fa {
font-size: 2rem;
object-fit: contain;
vertical-align: middle;
color: $orange2;
/* can't size a single-color background, so we use a linear-gradient that's effectively white */
background: linear-gradient(to right, $white1 100%, transparent 0) no-repeat center center;
background-size: 1.2rem 1.4rem;
}
}
}
.toot {
border-radius: 0;
.info {
padding: 0.3rem 0.75rem;
}
&:last-child {
border-bottom-left-radius: $br;
border-bottom-right-radius: $br;
.rss-icon {
display: block;
margin: -0.25rem 0;
.fa {
font-size: 2rem;
object-fit: contain;
vertical-align: middle;
color: $orange2;
/*
Can't size a single-color background, so we use
a linear-gradient that's effectively white.
*/
background: linear-gradient(to right, $white1 100%, transparent 0) no-repeat center center;
background-size: 1.2rem 1.4rem;
}
}
@ -240,6 +226,10 @@
margin-bottom: -0.25rem;
}
dt {
font-weight: bold;
}
.fields {
background: $profile-bg;
display: flex;

View File

@ -19,25 +19,19 @@
@import "photoswipe/dist/photoswipe.css";
@import "photoswipe-dynamic-caption-plugin/photoswipe-dynamic-caption-plugin.css";
@import "plyr/dist/plyr.css";
@import "./prism.css";
main {
background: transparent;
grid-auto-rows: auto;
}
.thread {
display: flex;
flex-direction: column;
border-radius: $br;
}
.toot {
background: $toot-bg;
.status {
background: $status-bg;
box-shadow: $boxshadow;
border: $boxshadow-border;
border-radius: $br;
position: relative;
margin-bottom: $br;
padding-top: 0.75rem;
a {
@ -47,66 +41,75 @@ main {
text-decoration: none;
}
.author > a {
padding: 0 0.75rem;
display: grid;
grid-template-columns: 3.5rem 1fr auto;
grid-template-rows: auto auto;
grid-template-areas:
"avatar display date"
"avatar user .";
gap: 0 0.5rem;
.status-header > address {
/*
Avoid stretching so wide that user
can't click on open thread link that's
behind the status header link.
*/
width: fit-content;
.avatar {
grid-area: avatar;
height: 3.5rem;
width: 3.5rem;
object-fit: cover;
border: 0.15rem solid $avatar-border;
border-radius: $br;
overflow: hidden; /* hides corners from img overflowing */
img {
height: 100%;
width: 100%;
object-fit: cover;
background: $bg;
}
}
.displayname, .username {
justify-self: start;
align-self: start;
max-width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
> a {
padding: 0 0.75rem;
display: grid;
grid-template-columns: 3.5rem 1fr auto;
grid-template-rows: auto auto;
grid-template-areas:
"avatar author-strap author-strap"
"avatar author-strap author-strap";
gap: 0 0.5rem;
font-style: normal;
.displayname {
grid-area: display;
font-weight: bold;
font-size: 1rem;
line-height: 1.3rem;
/* margin-top: -0.5rem; */
}
.username {
grid-area: user;
color: $link-fg;
font-size: 1rem;
line-height: 1.3rem;
}
.timestamp {
grid-area: date;
color: $fg-reduced;
.avatar {
grid-area: avatar;
height: 3.5rem;
width: 3.5rem;
object-fit: cover;
border: 0.15rem solid $avatar-border;
border-radius: $br;
overflow: hidden; /* hides corners from img overflowing */
img {
height: 100%;
width: 100%;
object-fit: cover;
background: $bg;
}
}
.author-strap {
grid-area: author-strap;
display: grid;
grid-template-columns: 1fr auto;
grid-template-rows: auto;
grid-template-areas:
"display display"
"user user";
gap: 0 0.5rem;
.displayname, .username {
justify-self: start;
align-self: start;
max-width: 100%;
font-size: 1rem;
line-height: 1.3rem;
}
.displayname {
grid-area: display;
font-weight: bold;
}
.username {
grid-area: user;
color: $link-fg;
}
}
}
}
.body {
.status-body {
padding: 0.5rem 0.75rem;
display: flex;
flex-direction: column;
@ -157,6 +160,10 @@ main {
line-height: 1.6rem;
width: 100%;
/*
Normalize header sizes to fit better
with the line-height we use for statuses.
*/
h1 {
margin: 0;
font-size: 1.8rem;
@ -187,35 +194,63 @@ main {
line-height: initial;
}
blockquote {
padding: 0.5rem 0 0.5rem 0.5rem;
border-left: 0.2rem solid $border-accent;
margin: 0;
font-style: italic;
}
hr {
border: 1px dashed $border-accent;
}
pre, code {
background-color: $gray2;
}
/*
Just code on its own inside status
content, ie, `here is some code`.
*/
code {
padding: 0.25rem;
border-radius: $br-inner;
white-space: pre-wrap;
}
pre {
/*
Restyle Prism code highlighting toolbar
plugin buttons to our own button style.
*/
.code-toolbar .toolbar {
margin-right: 0.5rem;
display: flex;
gap: 0.25rem;
.toolbar-item {
span, button {
color: $button-fg;
background: $button-bg;
font-weight: bold;
}
.copy-to-clipboard-button, span {
box-shadow: $boxshadow;
}
.copy-to-clipboard-button:hover, .copy-to-clipboard-button:hover span {
background: $button-hover-bg;
}
}
}
pre, pre[class*="language-"] {
border-radius: $br;
padding: 0.5rem;
white-space: pre;
overflow-x: auto;
code {
padding: 0.5rem;
/*
Code inside a pre block, ie.,
```
here is some code
```
*/
code {
width: 100%;
padding: 0;
white-space: pre;
border-radius: 0;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
@ -230,18 +265,6 @@ main {
}
}
.emoji {
transition: 0.1s;
}
.emoji:hover, .emoji:active {
transform: scale(2);
background-color: $bg;
box-shadow: $boxshadow;
border: $boxshadow-border;
border-radius: $br-inner;
}
.poll {
background-color: $gray2;
z-index: 2;
@ -451,41 +474,41 @@ main {
}
}
.info {
display: flex;
background: $toot-info-bg;
.status-info {
background: $status-info-bg;
color: $fg-reduced;
border-top: 0.15rem solid $toot-info-border;
border-top: 0.15rem solid $status-info-border;
padding: 0.5rem 0.75rem;
time {
padding-right: 1rem;
}
.stats {
display: inline-flex;
flex: 1;
.status-stats {
display: flex;
gap: 1rem;
.stats-grouping {
display: flex;
flex-wrap: wrap;
column-gap: 1rem;
}
.stats-item {
span {
white-space: nowrap;
}
display: flex;
gap: 0.4rem;
}
.stats-item:not(.published-at) {
z-index: 1;
user-select: none;
}
.language {
margin-left: auto;
z-index: 1;
cursor: pointer;
user-select: none;
}
}
grid-column: span 3;
flex-wrap: wrap;
}
.toot-link {
.status-link {
top: 0;
right: 0;
bottom: 0;
@ -508,15 +531,12 @@ main {
/* bottom left, bottom right */
border-bottom-left-radius: $br;
border-bottom-right-radius: $br;
margin-bottom: 0;
}
&.expanded {
background: $toot-focus-bg;
padding-bottom: 0;
.info {
background: $toot-focus-info-bg;
background: $status-focus-bg;
.status-info {
background: $status-focus-info-bg;
}
}
}

56
web/source/css/thread.css Normal file
View File

@ -0,0 +1,56 @@
/*
GoToSocial
Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
.thread {
display: flex;
flex-direction: column;
gap: 0.4rem;
/*
This column header might contain
quite some info, so let it wrap.
*/
.col-header {
display: flex;
flex-direction: row;
flex-wrap: wrap;
column-gap: 1rem;
row-gap: 0.5rem;
box-shadow: $boxshadow;
border: $boxshadow-border;
h2 {
margin-right: auto;
}
}
.status {
border-radius: 0;
&:last-child {
border-bottom-left-radius: $br;
border-bottom-right-radius: $br;
.status-info {
border-bottom-left-radius: $br;
border-bottom-right-radius: $br;
}
}
}
}

View File

@ -21,6 +21,10 @@ const Photoswipe = require("photoswipe/dist/umd/photoswipe.umd.min.js");
const PhotoswipeLightbox = require("photoswipe/dist/umd/photoswipe-lightbox.umd.min.js");
const PhotoswipeCaptionPlugin = require("photoswipe-dynamic-caption-plugin").default;
const Plyr = require("plyr");
const Prism = require("./prism.js");
Prism.manual = true;
Prism.highlightAll();
let [_, _user, type, id] = window.location.pathname.split("/");
if (type == "statuses") {

File diff suppressed because one or more lines are too long

View File

@ -140,18 +140,23 @@ function ReportedToot({ toot }) {
const account = toot.account;
return (
<article className="toot expanded">
<section className="author">
<a>
<img className="avatar" src={account.avatar} alt="" />
<span className="displayname">
{account.display_name.trim().length > 0 ? account.display_name : account.username}
<span className="sr-only">.</span>
</span>
<span className="username">@{account.username}</span>
</a>
</section>
<section className="body">
<article className="status expanded">
<header className="status-header">
<address>
<a style={{margin: 0}}>
<img className="avatar" src={account.avatar} alt="" />
<dl className="author-strap">
<dt className="sr-only">Display name</dt>
<dd className="displayname text-cutoff">
{account.display_name.trim().length > 0 ? account.display_name : account.username}
</dd>
<dt className="sr-only">Username</dt>
<dd className="username text-cutoff">@{account.username}</dd>
</dl>
</a>
</address>
</header>
<section className="status-body">
<div className="text">
<div className="content">
{toot.spoiler_text?.length > 0
@ -164,8 +169,17 @@ function ReportedToot({ toot }) {
<TootMedia media={toot.media_attachments} sensitive={toot.sensitive} />
}
</section>
<aside className="info">
<time dateTime={toot.created_at}>{new Date(toot.created_at).toLocaleString()}</time>
<aside className="status-info">
<dl class="status-stats">
<div class="stats-grouping">
<div class="stats-item published-at text-cutoff">
<dt class="sr-only">Published</dt>
<dd>
<time dateTime={toot.created_at}>{new Date(toot.created_at).toLocaleString()}</time>
</dd>
</div>
</div>
</dl>
</aside>
</article>
);

View File

@ -83,7 +83,7 @@ function ReportEntry({ report }) {
<div className="usernames">
<Username user={from} link={false} /> reported <Username user={target} link={false} />
</div>
<h3 className="status">
<h3 className="report-status">
{report.action_taken ? "Resolved" : "Open"}
</h3>
</div>

View File

@ -22,24 +22,29 @@ const React = require("react");
module.exports = function FakeProfile({ avatar, header, display_name, username, role }) {
return ( // Keep in sync with web/template/profile.tmpl
<div className="profile">
<div className="header">
<div className="header-image">
<div className="profile-header">
<div className="header-image-wrapper">
<img src={header} alt={header ? `header image for ${username}` : "None set"} />
</div>
<div className="basic-info" aria-hidden="true">
<a className="avatar" href={avatar}>
<img src={avatar} alt={avatar ? `avatar image for ${username}` : "None set"} />
</a>
<span className="displayname text-cutoff">
{display_name.trim().length > 0 ? display_name : username}
<span className="sr-only">.</span>
</span>
<span className="username text-cutoff">@{username}</span>
{(role && role.name != "user") &&
<div className={`role ${role.name}`}>
<span className="sr-only">Role: </span>{role.name}
</div>
}
<dl className="namerole">
<dt className="sr-only">Display name</dt>
<dd className="displayname text-cutoff">{display_name.trim().length > 0 ? display_name : username}</dd>
<dt className="sr-only">Username</dt>
<dd className="username text-cutoff">@{username}</dd>
<dt className="sr-only">Role</dt>
{
(role && role.name != "user") ?
<>
<dd className="sr-only">Role</dd>
<dt className={`role ${role.name}`}>{role.name}</dt>
</>
: null
}
</dl>
</div>
</div>
</div>

View File

@ -29,20 +29,27 @@ module.exports = function FakeToot({ children }) {
} } = query.useVerifyCredentialsQuery();
return (
<article className="toot expanded">
<section className="author">
<a>
<img className="avatar" src={account.avatar} alt="" />
<span className="displayname">
{account.display_name.trim().length > 0 ? account.display_name : account.username}
<span className="sr-only">.</span>
</span>
<span className="username">@{account.username}</span>
</a>
</section>
<section className="body">
<article className="status expanded">
<header className="status-header">
<address>
<a style={{margin: 0}}>
<img className="avatar" src={account.avatar} alt="" />
<dl className="author-strap">
<dt className="sr-only">Display name</dt>
<dd className="displayname text-cutoff">
{account.display_name.trim().length > 0 ? account.display_name : account.username}
</dd>
<dt className="sr-only">Username</dt>
<dd className="username text-cutoff">@{account.username}</dd>
</dl>
</a>
</address>
</header>
<section className="status-body">
<div className="text">
{children}
<div className="content">
{children}
</div>
</div>
</section>
</article>

View File

@ -20,26 +20,14 @@ body {
grid-template-rows: auto 1fr;
}
.content {
.page-content {
grid-column: 1 / span 3; /* stretch entire width, to fit panel + sidebar nav */
width: 100%;
}
header {
justify-content: start;
a {
margin: 1.5rem;
gap: 1rem;
h1 {
font-size: 1.5rem;
}
img {
height: 3rem;
}
}
/* Don't inherit orange dot from base.css. */
ul li::before {
content: initial;
}
#root {
@ -1007,7 +995,7 @@ button.with-padding {
grid-template-columns: 1fr auto;
gap: 0.5rem;
.status {
.report-status {
color: $border-accent;
}
}
@ -1029,7 +1017,7 @@ button.with-padding {
color: $fg-reduced;
border-left: 0.4rem solid $bg;
.byline .status {
.byline .report-status {
color: $fg-reduced;
}
@ -1141,11 +1129,62 @@ button.with-padding {
}
}
.instance-rules {
list-style-position: inside;
margin: 0;
padding: 0;
a.rule {
display: grid;
grid-template-columns: 1fr auto;
align-items: center;
color: $fg;
text-decoration: none;
background: $status-bg;
padding: 1rem;
margin: 0.5rem 0;
border-radius: $br;
line-height: 2rem;
position: relative;
&:hover {
color: $fg-accent;
.edit-icon {
display: inline;
}
}
.edit-icon {
display: none;
font-size: 1rem;
line-height: 1.5rem;
}
li {
font-size: 1.75rem;
padding: 0;
margin: 0;
h2 {
margin: 0;
margin-top: 0 !important;
display: inline-block;
font-size: 1.5rem;
}
}
span {
color: $fg-reduced;
}
}
}
@media screen and (orientation: portrait) {
.reports .report .byline {
grid-template-columns: 1fr;
.status {
.report-status {
grid-row: 1;
}
}
@ -1162,4 +1201,14 @@ button.with-padding {
to {
opacity: 0;
}
}
}
@media (prefers-reduced-motion) {
.fa-spin {
animation: none;
}
}
.monospace {
font-family: monospace;
}