Files
uncensored-send/web/static/style.css
T
2026-09-13 10:49:39 +02:00

329 lines
9.5 KiB
CSS

/* Hand-written, no build step, no framework. */
:root {
--bg: #f6f6f4;
--fg: #1b1b18;
--muted: #6b6b63;
--card: #ffffff;
--line: #dcdcd5;
--accent: #2f6f4f;
--accent-fg: #ffffff;
/* Red reads as text and as a button surface, and one value cannot do both:
see the pair below, kept in step per theme. */
--danger: #9b2c2c;
--danger-bg: #991b1b;
--danger-fg: #ffffff;
--warn-bg: #fdf6e3;
--radius: 10px;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #16161a;
--fg: #e8e8e3;
--muted: #9a9a92;
--card: #1f1f25;
--line: #33333c;
--accent: #5fae86;
--accent-fg: #10241a;
--danger: #e07070;
--danger-bg: #d32f2f;
--danger-fg: #ffffff;
--warn-bg: #2a2418;
}
}
* { box-sizing: border-box; }
body {
margin: 0;
padding: 0 1rem 3rem;
background: var(--bg);
color: var(--fg);
font: 16px/1.5 system-ui, -apple-system, "Segoe UI", sans-serif;
}
header, main, footer { max-width: 42rem; margin: 0 auto; }
header { padding: 1.5rem 0; }
footer { padding-top: 2rem; color: var(--muted); font-size: .875rem; }
.brand {
font-weight: 600;
font-size: 1.125rem;
color: var(--fg);
text-decoration: none;
}
h1 { font-size: 1.375rem; margin: 0 0 .75rem; }
h2 { font-size: 1rem; margin: 0 0 .5rem; }
a { color: var(--accent); }
.card, .limits, .cli {
background: var(--card);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: 1.25rem;
margin-bottom: 1.25rem;
}
.field { display: block; margin-bottom: 1rem; }
.field > span { display: block; font-size: .8125rem; color: var(--muted); margin-bottom: .25rem; }
.field em { font-style: normal; opacity: .7; }
input[type=text], input[type=password] {
width: 100%;
padding: .5rem .625rem;
font: inherit;
color: var(--fg);
background: var(--bg);
border: 1px solid var(--line);
border-radius: 6px;
}
.row { display: flex; gap: 1rem; flex-wrap: wrap; }
.row > .field { flex: 1 1 12rem; }
/* The file input has to be the last part in the submitted body, so it sits at
the bottom of the form's markup and is lifted back to the top here. Only the
painting order moves; the submission order, which the server depends on, is
the markup's. */
#upload { display: flex; flex-direction: column; }
#upload > .drop { order: -1; }
#upload > button { align-self: flex-start; }
/* An author `display` beats the browser's own [hidden] rule, so anything given
a display here has to say what hidden means for it. */
#upload[hidden] { display: none; }
.drop {
border: 2px dashed var(--line);
border-radius: var(--radius);
padding: 1.5rem;
text-align: center;
margin-bottom: 1rem;
transition: border-color .15s, background .15s;
}
.drop.over { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); }
.drop input[type=file] { max-width: 100%; }
button, .button {
display: inline-block;
padding: .5rem 1rem;
font: inherit;
font-weight: 500;
color: var(--accent-fg);
background: var(--accent);
border: 0;
border-radius: 6px;
cursor: pointer;
text-decoration: none;
}
button:disabled { opacity: .55; cursor: default; }
button.danger { background: var(--danger-bg); color: var(--danger-fg); }
.progress .bar {
height: .5rem;
background: var(--line);
border-radius: 999px;
overflow: hidden;
margin-bottom: .375rem;
}
.progress .fill { height: 100%; width: 0; background: var(--accent); transition: width .1s linear; }
.hint { color: var(--muted); font-size: .875rem; margin: .25rem 0 0; }
#error:not([hidden]) { color: var(--danger); }
dl { display: grid; grid-template-columns: auto 1fr; gap: .25rem 1rem; margin: 0; }
dt { color: var(--muted); font-size: .875rem; }
dd { margin: 0; }
.mono, pre, code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .8125rem; }
.wrap { overflow-wrap: anywhere; }
.filename { overflow-wrap: anywhere; }
pre {
margin: 0;
padding: .75rem;
background: var(--bg);
border: 1px solid var(--line);
border-radius: 6px;
overflow-x: auto;
}
.warn {
margin: 1.25rem 0;
padding: 1rem;
background: var(--warn-bg);
border: 1px solid var(--line);
border-radius: var(--radius);
}
.warn p { margin: .25rem 0 .75rem; font-size: .875rem; }
.result .field { margin-top: 1rem; }
.check { display: flex; align-items: center; gap: .5rem; margin-bottom: 1rem; font-size: .875rem; }
.check input { margin: 0; }
.notice {
margin: 0 0 1rem;
padding: .625rem .875rem;
background: var(--card);
border: 1px solid var(--line);
border-left: 3px solid var(--accent);
border-radius: 6px;
font-size: .875rem;
}
.notice .forget-form, .notice form { display: inline; }
button.link {
padding: 0;
background: none;
border: 0;
color: var(--accent);
font: inherit;
text-decoration: underline;
cursor: pointer;
}
/* Result card ------------------------------------------------------------ */
.result .links { margin: 1.25rem 0 0; }
.result .links .field { margin-bottom: 1rem; }
/* A read-only URL with its copy button alongside. */
.copyrow { display: flex; gap: .5rem; align-items: stretch; }
.copyrow input { flex: 1 1 auto; min-width: 0; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .8125rem; }
.copyrow .copy { flex: 0 0 auto; }
/* Keep the detail list clear of the copy buttons above it. */
.result dl { margin-top: 1.75rem; padding-top: 1.25rem; border-top: 1px solid var(--line); }
.result dl dd { margin-bottom: .25rem; }
.result .actions { margin: 1.5rem 0 0; }
@media (max-width: 26rem) {
.copyrow { flex-wrap: wrap; }
.copyrow input { flex-basis: 100%; }
}
/* Administration --------------------------------------------------------- */
header { display: flex; align-items: baseline; gap: 1rem; flex-wrap: wrap; }
header nav {
margin-left: auto;
display: flex;
align-items: baseline;
gap: .875rem;
font-size: .875rem;
}
header nav form { display: inline; }
header nav .who { color: var(--muted); }
header nav .who::before { content: "\1F511\00a0"; }
/* A listing needs far more room than the reading measure a form wants. */
body.wide header,
body.wide main,
body.wide footer { max-width: 72rem; }
dl.stats { grid-template-columns: auto 1fr; }
dl.stats em { font-style: normal; color: var(--muted); }
table.admin { border-collapse: collapse; width: 100%; font-size: .8125rem; }
table.admin th,
table.admin td {
padding: .45rem .5rem;
text-align: left;
vertical-align: top;
border-bottom: 1px solid var(--line);
}
table.admin th { color: var(--muted); font-weight: 500; white-space: nowrap; }
table.admin th a { color: inherit; text-decoration: none; }
table.admin th a:hover { color: var(--accent); text-decoration: underline; }
table.admin tr:last-child td { border-bottom: 0; }
table.admin .num { text-align: right; }
table.admin .nowrap { white-space: nowrap; }
table.admin form { margin: 0; }
table.admin .actions-cell { text-align: right; width: 1%; }
/* The filename carries the link and the identifier sits under it, which saves
a whole column of UUID. */
table.admin .filecell a { overflow-wrap: anywhere; font-weight: 500; }
table.admin .filecell .id {
display: block;
font-size: .6875rem;
color: var(--muted);
overflow-wrap: anywhere;
}
button.small { padding: .2rem .5rem; font-size: .75rem; }
.visually-hidden {
position: absolute;
width: 1px; height: 1px;
margin: -1px; padding: 0; border: 0;
clip-path: inset(50%);
overflow: hidden;
}
/* Narrow screens get one card per row rather than a horizontal scrollbar,
which is unusable for picking a row and pressing a button in it. */
@media (max-width: 46rem) {
table.admin,
table.admin tbody,
table.admin tr,
table.admin td { display: block; width: 100%; }
table.admin thead { display: none; }
table.admin tr {
padding: .625rem 0;
border-bottom: 1px solid var(--line);
}
table.admin td { border: 0; padding: .1rem 0; }
table.admin td[data-label]::before {
content: attr(data-label) ": ";
color: var(--muted);
}
table.admin .num,
table.admin .actions-cell { text-align: left; width: auto; }
table.admin .actions-cell { padding-top: .5rem; }
table.admin .filecell::before { content: none; }
}
.cli code, .hint code { background: var(--bg); padding: .1rem .3rem; border-radius: 4px; }
/* Disclosure panels: the one-off token on the upload form, "Remove this file"
on the info page. Closed, a panel is a single line and needs nothing; open,
it needs an edge, or its contents read as loose page furniture and there is
no telling where it ends. */
details.panel {
border: 1px solid var(--line);
border-radius: var(--radius);
margin-bottom: 1rem;
}
details.panel > summary {
padding: .625rem .75rem;
font-weight: 400;
font-size: .875rem;
color: var(--muted);
cursor: pointer;
}
details.panel[open] > summary {
border-bottom: 1px solid var(--line);
margin-bottom: .75rem;
}
/* The summary carries the panel's padding itself; everything else is inset to
match, and the panel closes the gap under the last of them. */
details.panel > :not(summary) { padding-left: .75rem; padding-right: .75rem; }
details.panel[open] { padding-bottom: .75rem; }
details.panel > :last-child { margin-bottom: 0; }
details.panel .field { max-width: 24rem; }
/* A hint that introduces a control is an instruction for what follows, not a
caption for what came before, so it must not sit against it. */
.hint + form, .hint + .field { margin-top: .75rem; }
.limits .hint { margin-top: .75rem; }
.limits dl, .card dl { row-gap: .375rem; }
/* Actions that mix a link-button with a form-button. */
.actions { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
.actions form { margin: 0; }