// spec.sr.ht stylesheet.
//
// Mirrors the SourceHut service pattern (see paste.sr.ht/scss/main.scss and
// compare.sr.ht/scss/main.scss): pull in the shared `base` partial (Bootstrap 4
// plus the SourceHut chrome — contrast, variables, nav, events, highlight,
// dark) and then add only what is unique to the document views.
//
// Build: `make css`, which is `sassc -I /usr/share/sourcehut/scss scss/main.scss`
// plus minify plus a content-hashed filename. The shared partials come from
// core.sr.ht's `make install`; dart-sass works too for a local build.
//
// The output is EMBEDDED INTO THE BINARY (web/templates.go's //go:embed static),
// so `make css` alone does not restyle a running daemon: build the CSS, then
// `go build`, then restart.
@import "base";
// Let scheme-aware widgets follow prefers-color-scheme, as compare.sr.ht does.
:root {
color-scheme: light dark;
}
// ---- Rendered document ----------------------------------------------------
.spec-doc {
// Prose, not code: a comfortable measure matters more than filling the
// column. The sidebar already takes a quarter of the width, so this only
// bites on very wide screens.
max-width: 46rem;
h1, h2, h3, h4, h5, h6 {
margin-top: 1.5rem;
}
// The first heading sits directly under the page title; the extra top margin
// reads as a gap rather than as structure.
> :first-child {
margin-top: 0;
}
blockquote {
padding-left: 0.75rem;
border-left: 3px solid $gray-400;
color: $gray-700;
@media (prefers-color-scheme: dark) {
border-left-color: $gray-700;
color: $gray-300;
}
}
table {
@extend .table;
width: auto;
}
img {
max-width: 100%;
}
}
// A wikilink that resolved to nothing. The renderer emits it as a span rather
// than dropping it, so the read plane doubles as a link checker — which only
// works if it is visibly different from a link that resolved.
.wikilink-missing {
color: $danger;
text-decoration: line-through dotted;
}
// ---- Search ---------------------------------------------------------------
.search-hit {
margin-bottom: 0.75rem;
}
.search-snippet {
color: $gray-700;
// bleve wraps matched terms in <mark>; the browser default is a yellow that
// is unreadable on the dark chrome.
mark {
padding: 0;
background: transparent;
color: inherit;
font-weight: 700;
}
@media (prefers-color-scheme: dark) {
color: $gray-300;
}
}
// ---- Proposal review ------------------------------------------------------
.proposal-actions {
margin: 1rem 0;
form {
margin-right: 0.5rem;
}
}
.proposal-doc {
margin-top: 1.5rem;
padding-top: 1rem;
border-top: 1px solid $gray-300;
@media (prefers-color-scheme: dark) {
border-top-color: $gray-700;
}
}
// ---- Prose diff -----------------------------------------------------------
//
// The renderer (web/diff.go) emits this markup: a hunk per heading path, a
// block per change, and inline <del>/<ins> spans or a two-column view depending
// on how badly the block was rewritten. Colours follow Bootstrap's success and
// danger so added and removed read the same here as everywhere else.
//
// Every block of the proposed document is on the page, because every block can
// be commented on — so the visual weight is what keeps this a diff rather than a
// document dump. A changed block is bordered, tinted and full size; an unchanged
// one is a collapsed, dimmed, borderless line. That contrast is load-bearing:
// remove it and a reviewer can no longer see at a glance what the agent did.
.prosediff {
font-size: 0.95rem;
.ph-hunk {
margin-bottom: 1rem;
}
.ph-path {
font-family: $font-family-monospace;
font-size: 0.8rem;
color: $gray-600;
padding: 0.15rem 0;
@media (prefers-color-scheme: dark) {
color: $gray-400;
}
}
.ph-block {
padding: 0.35rem 0.6rem;
margin: 0.2rem 0;
border-left: 3px solid transparent;
border-radius: 2px;
}
.ph-label {
display: block;
font-size: 0.72rem;
text-transform: uppercase;
letter-spacing: 0.03em;
color: $gray-600;
margin-bottom: 0.15rem;
@media (prefers-color-scheme: dark) {
color: $gray-400;
}
}
.ph-insert {
border-left-color: $success;
background: rgba($success, 0.08);
}
.ph-delete {
border-left-color: $danger;
background: rgba($danger, 0.08);
}
.ph-modify {
border-left-color: $gray-400;
background: rgba($gray-500, 0.06);
}
.ph-move {
border-left-color: $info;
color: $gray-600;
font-style: italic;
}
// Unchanged text: present, addressable, and deliberately quiet. It is a
// <details> collapsed to one line, so a long untouched section costs a few
// dim rows rather than screens of prose between the changes.
.ph-context {
padding: 0;
margin: 0;
border-left-color: transparent;
color: $gray-600;
@media (prefers-color-scheme: dark) {
color: $gray-500;
}
> .ph-context-label {
padding: 0.1rem 0.6rem;
font-size: 0.85rem;
cursor: pointer;
list-style-position: outside;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
// Expanded, it indents under its own summary and stays uncoloured: it is
// still context, even while being read.
> .ph-body,
> .ph-code,
> .ph-threads,
> .ph-form {
margin-left: 1.2rem;
}
&[open] > .ph-context-label {
color: $gray-700;
@media (prefers-color-scheme: dark) {
color: $gray-400;
}
}
}
// Inline marks: a deletion is struck through in danger, an insertion is
// underlined in success. Both keep a faint background so a one-word change is
// visible without reading the colour.
del {
text-decoration: line-through;
color: $danger;
background: rgba($danger, 0.12);
text-decoration-thickness: 1px;
}
ins {
text-decoration: none;
color: darken($success, 8%);
background: rgba($success, 0.14);
@media (prefers-color-scheme: dark) {
color: lighten($success, 8%);
}
}
// The two-column fallback for shredded blocks: old on the left, new on the
// right, stacking on a narrow screen so it never overflows the page.
.ph-cols {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
}
.ph-col {
flex: 1 1 20rem;
min-width: 0;
padding: 0.4rem 0.6rem;
border-radius: 2px;
}
.ph-old {
background: rgba($danger, 0.06);
}
.ph-new {
background: rgba($success, 0.06);
}
// Code / frontmatter line diffs keep their wrapping and are read line by line.
.ph-code {
margin: 0;
padding: 0.25rem 0;
background: transparent;
white-space: pre-wrap;
word-break: break-word;
.ph-line-del {
display: block;
background: rgba($danger, 0.1);
}
.ph-line-ins {
display: block;
background: rgba($success, 0.1);
}
}
}
// ---- Review threads -------------------------------------------------------
//
// Rendered both inside a diff block (web/diff.go executes "blockthreads" into
// the block's markup) and, for comments whose anchor is lost, in the page's own
// area — so these styles are outside .prosediff and apply in both places.
.ph-threads {
margin: 0.35rem 0 0.15rem;
}
.ph-thread {
margin: 0.35rem 0;
padding: 0.4rem 0.6rem;
border-left: 3px solid $info;
background: rgba($info, 0.06);
border-radius: 2px;
// A resolved thread is history: it stays readable and stays where it was, but
// stops asking for attention the way an open one does.
&.ph-thread-resolved {
border-left-color: $gray-400;
background: transparent;
opacity: 0.7;
}
}
.ph-thread-head {
font-size: 0.8rem;
margin-bottom: 0.2rem;
.ph-author {
font-weight: 700;
}
.ph-when {
color: $gray-600;
margin-left: 0.35rem;
@media (prefers-color-scheme: dark) {
color: $gray-400;
}
}
.badge {
margin-left: 0.35rem;
}
}
// A comment is prose someone typed, newlines included; the server escapes it and
// the browser must not then reflow it into one paragraph.
.ph-comment-body {
white-space: pre-wrap;
word-break: break-word;
}
.ph-reply {
margin-top: 0.4rem;
padding-left: 0.6rem;
border-left: 2px solid $gray-400;
@media (prefers-color-scheme: dark) {
border-left-color: $gray-700;
}
}
.ph-thread-actions {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-top: 0.4rem;
}
// The compose and reply forms fold away behind a summary: the review page shows
// one control per block, and a textarea per block would bury the diff.
.ph-form {
font-size: 0.85rem;
> summary {
cursor: pointer;
color: $gray-600;
@media (prefers-color-scheme: dark) {
color: $gray-400;
}
}
textarea {
margin: 0.3rem 0;
}
}
.ph-compose {
margin: 0.2rem 0 0.4rem;
}
// Comments whose block is gone. The warning border is the point: they are shown
// because dropping them would hide a critique, and marked because they no longer
// describe anything on this page.
.ph-lost {
border-top-color: $warning;
.ph-thread {
border-left-color: $warning;
background: rgba($warning, 0.06);
}
}