From 43f8c0ea850ae022f9384be3143dbcd4c3b3d045 Mon Sep 17 00:00:00 2001 From: Kristian Wind Date: Thu, 20 Aug 2026 14:57:15 +0200 Subject: [PATCH] feat(site): landingssiden siger hvad der findes nu, og siger det med kanter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Siden stod fra før kalenderen, noternes billeder og arkiv, sikkerhedsarbejdet og beaconet. Nu står de der: søgning der læser dansk, kalenderen, Venter på andre, gentagelser, AI slået fra som udgangspunkt, andenfaktor, at ingen skriver sig selv op, og beaconet — sagt som det er, tændt fra start og med ét klik til at slukke. Google Kalender står med sit forbehold på selve kortet. Den er skrevet efter dokumentationen og har aldrig kørt mod Google, og en landingsside, der siger noget andet, er den ene sætning, resten af siden ikke kan bære. Ikke-målene løj om posten: "No mail integration beyond Gmail and the forwarding address" har været forkert, siden IMAP kom til. iCloud og Fastmail virker. Det, der faktisk ikke virker, er Outlook, og det er nu det, der står. Designet: en bjælke der bliver stående, fordi siden er 5.600 px og vejen ud kun lå i toppen. Fire fakta under knapperne, hvor en landingsside plejer at sætte logoer, den ikke har gjort sig fortjent til. Kort med egne kanter frem for celler i ét gitter — ti celler i tre søjler er et hul i tegningen, atten kort i tre grupper er ikke. To paneler under "Running it", fordi de er alternativer og ikke trin. Ikke-målene som en liste, der kan tælles. Og tre skærmbilleder, der lå i site/screenshots uden at være brugt. Co-Authored-By: Claude Opus 5 --- site/index.html | 546 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 465 insertions(+), 81 deletions(-) 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