diff --git a/site/index.html b/site/index.html index df203d6..dcdf1b6 100644 --- a/site/index.html +++ b/site/index.html @@ -6,12 +6,12 @@ verdande — self-hosted tasks, shared - + @@ -76,10 +76,10 @@ } .theme-toggle { - position: fixed; - top: 1rem; - right: 1rem; - z-index: 10; + /* In the bar now rather than floating over the corner. Fixed, it sat + on top of whatever happened to scroll under it — on a narrow window + that was the first screenshot's own corner. */ + flex: none; width: 2.25rem; height: 2.25rem; display: grid; @@ -141,10 +141,86 @@ padding: 0 1.5rem; } + /* --- the bar that stays ------------------------------------------- + + A page this long had its only way out at the very top: somebody who + read as far as "What it will not do" had to scroll back past nine + screenshots to reach the documentation. The bar is the way out from + wherever you are. + + `sticky` and not `fixed`: sticky keeps its space in the flow, so + nothing below has to be pushed down by a number that then has to be + kept in step with the bar's own height. + + Full width, with the wrap *inside* it. Put the other way round, the + background would end where the column ends and the page would scroll + past it on both sides. */ + .topbar { + position: sticky; + top: 0; + z-index: 20; + background: var(--ground); + border-bottom: 1px solid var(--line); + } + + .bar { + display: flex; + align-items: center; + gap: 1.25rem; + padding: 0.625rem 1.5rem; + } + + .bar-brand { + display: flex; + align-items: center; + gap: 0.5rem; + color: var(--ink); + text-decoration: none; + font-weight: 560; + letter-spacing: -0.01em; + } + + .bar-brand .rune { + color: var(--accent); + font-size: 1.125rem; + line-height: 1; + } + + .bar-links { + display: flex; + align-items: center; + gap: 1.25rem; + margin-left: auto; + font-size: 0.875rem; + } + + .bar-links a { + color: var(--ink-muted); + text-decoration: none; + } + + .bar-links a:hover { + color: var(--ink); + } + + /* The three in-page links go first on a narrow screen: they are the ones + a phone can reach by scrolling anyway. Documentation and Source cannot + be reached any other way, so they stay. */ + @media (max-width: 640px) { + .bar-links .inpage { + display: none; + } + } + + /* Or the bar covers the heading you just jumped to. */ + :target { + scroll-margin-top: 4.5rem; + } + /* --- hero --- */ - header { - padding: 6rem 0 4rem; + .hero { + padding: 5rem 0 3.5rem; text-align: center; } @@ -172,7 +248,7 @@ } .norn { - margin: 2rem auto 0; + margin: 0 auto 1.75rem; max-width: 34rem; font-size: 0.9375rem; color: var(--ink-faint); @@ -289,44 +365,104 @@ /* --- features --- */ + /* A hairline between sections rather than under every heading: the page + is one column, and the line says where one subject stops. */ section { - padding: 3rem 0; + padding: 3.5rem 0; + border-top: 1px solid var(--line); } h2 { + display: flex; + align-items: baseline; + gap: 0.75rem; font-size: 1.5rem; font-weight: 560; letter-spacing: -0.02em; margin: 0 0 2rem; } + /* A short accent rule in front of every h2. The sections had nothing to + mark them but their own words, and at a glance the page read as one + long run of paragraphs. */ + h2::before { + content: ''; + flex: none; + width: 1.125rem; + height: 2px; + border-radius: 1px; + background: var(--accent); + transform: translateY(-0.35em); + } + + /* Three groups rather than eighteen cards in a row. Grouped, the list is + read as three answers — what it does, what it talks to, what it + promises — and any of the three can be skipped. */ + .group-label { + margin: 2.5rem 0 1rem; + font-size: 0.75rem; + font-weight: 560; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--ink-faint); + } + + .group-label:first-of-type { + margin-top: 0; + } + + /* Cards with their own edges, not cells in one table. + The table shared a hairline grid, which meant the last row had to be + full or it was a hole in the drawing — and it was a hole, for as long + as there were ten of them. A card that stands alone on its row is a + card; an empty cell is a mistake. */ .grid { display: grid; - grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); - gap: 1px; - background: var(--line); - border: 1px solid var(--line); - border-radius: var(--radius-lg); - overflow: hidden; + grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); + gap: 0.875rem; } .cell { - background: var(--ground); - padding: 1.5rem; + background: var(--surface); + border: 1px solid var(--line); + border-radius: var(--radius-lg); + padding: 1.125rem 1.25rem 1.25rem; + transition: border-color 150ms ease; + } + + .cell:hover { + border-color: var(--line-strong); } .cell h3 { - margin: 0 0 0.5rem; - font-size: 1rem; + margin: 0 0 0.4375rem; + font-size: 0.9375rem; font-weight: 560; } .cell p { margin: 0; - font-size: 0.9375rem; + font-size: 0.875rem; color: var(--ink-muted); } + .cell code { + font-family: var(--font-mono); + font-size: 0.8125em; + color: var(--ink); + } + + /* A card that has something to admit rather than something to offer. + It is not a footnote and it is not fine print: it is the same size as + everything else, and it says the same thing the README says. */ + .cell .caveat { + margin: 0.5rem 0 0; + padding-top: 0.5rem; + border-top: 1px solid var(--line); + font-size: 0.8125rem; + color: var(--ink-faint); + } + /* --- install --- */ pre { @@ -374,6 +510,109 @@ color: var(--ink-muted); } + /* Four facts under the buttons, in the place where a landing page usually + puts logos it has not earned. They are the four questions somebody + self-hosting asks before anything else. */ + ul.facts { + list-style: none; + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 0.5rem 1.25rem; + margin: 2rem 0 0; + padding: 0; + font-size: 0.8125rem; + color: var(--ink-faint); + } + + ul.facts li { + display: flex; + align-items: center; + gap: 1.25rem; + } + + /* The separator belongs between two facts, not after the last one — so it + is drawn by the item that follows it. */ + ul.facts li + li::before { + content: ''; + width: 3px; + height: 3px; + border-radius: 50%; + background: var(--line-strong); + } + + /* On a phone the row wraps, and a separator drawn by the item that follows + it lands at the *start* of the new line — a dot with nothing before it. + No separators there; the wrap is the separation. */ + @media (max-width: 640px) { + ul.facts { + gap: 0.375rem 1.125rem; + } + + ul.facts li + li::before { + display: none; + } + } + + /* Two ways to run it, side by side, because they are alternatives rather + than steps. Stacked, the second read as what you do after the first. */ + .panels { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr)); + gap: 0.875rem; + } + + .panel { + background: var(--surface); + border: 1px solid var(--line); + border-radius: var(--radius-lg); + padding: 1.25rem; + } + + .panel h3 { + margin: 0 0 0.5rem; + font-size: 0.9375rem; + font-weight: 560; + } + + /* Sunk rather than raised: inside a card, a block in --surface would be + the same colour as the card it sits in. */ + .panel pre { + background: var(--ground); + margin: 1rem 0 0; + } + + .panel p { + margin: 0; + font-size: 0.875rem; + color: var(--ink-muted); + } + + /* The non-goals were one paragraph of seven sentences, which is the shape + nobody reads. Each is its own line now, and they can be counted. */ + ul.nogo { + list-style: none; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); + gap: 0.5rem 1.75rem; + margin: 0; + padding: 0; + font-size: 0.9375rem; + color: var(--ink-muted); + } + + ul.nogo li { + position: relative; + padding-left: 1.25rem; + } + + ul.nogo li::before { + content: '—'; + position: absolute; + left: 0; + color: var(--ink-faint); + } + .demo-note { margin: 1rem 0 0; font-family: var(--font); @@ -524,27 +763,38 @@ - -
-
+ + +
+

verdande

- Self-hosted tasks and projects, shared with the people you share the work with. - One binary, SQLite inside it. + Self-hosted tasks, notes and projects, shared with the people you share the work + with. One binary, SQLite inside it.

Built to be deployed as a Rune in Yggdrasil Panel — upload the manifest, fill in two fields, press start. It also runs anywhere Docker does.

-

- Verdande is one of the three Norns — the one who spins that which is - becoming. Not what has been, and not what is fated to be: the thing you are - in the middle of. Which is what a to-do list is. -

+ +
    +
  • One Go binary
  • +
  • SQLite inside it
  • +
  • amd64 and arm64
  • +
  • No account anywhere but yours
  • +
  • MIT
  • +
+

+ Verdande is one of the three Norns — the one who spins that which is + becoming. Not what has been, and not what is fated to be: the thing you are + in the middle of. Which is what a to-do list is. +

MIT licensed · github.com/kristianwind/verdande + · Documentation

Built to run as a Rune in