Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0705b0100e |
@@ -1,133 +0,0 @@
|
||||
---
|
||||
name: ui-design-101
|
||||
description: Design the actions in an interface — buttons, confirmations, destructive operations, bulk edits, admin and moderation tools. Use when building or reviewing anything a user clicks repeatedly, anything that deletes or hides data, any list of rows with per-row actions, or when the user says a tool is tedious, asks for a confirmation dialog, or reports an interface that "does nothing".
|
||||
---
|
||||
|
||||
# UI Design 101: actions
|
||||
|
||||
Visual design is a different skill. This one is about what happens when someone
|
||||
clicks — and, above all, what happens when they click **the hundredth time**.
|
||||
|
||||
Every rule here is a checkable property of an interface. Apply all of them to
|
||||
whatever you are building; an interface that fails any one of them is not done.
|
||||
|
||||
## Price every action at its hundredth use
|
||||
|
||||
Design for the hundredth click, not the first. An extra dialog, an extra
|
||||
scroll, an extra page load is nothing once and unbearable a hundred times. Ask
|
||||
directly: how many times will one person do this in one sitting? If the answer
|
||||
is more than a handful, the per-use cost is the whole design problem.
|
||||
|
||||
The failure has a shape: a tool that is *pleasant to demo and brutal to use*.
|
||||
Nobody notices it while building, because builders click things twice.
|
||||
|
||||
## Ceremony follows reversibility
|
||||
|
||||
Undo beats confirm. A confirmation asks the user to be certain in advance; an
|
||||
undo lets them be wrong cheaply. Prefer the second.
|
||||
|
||||
- **Reversible action** (hide, archive, mark, tag): no confirmation. Ever. Do
|
||||
it, show it happened, offer the undo in the same spot.
|
||||
- **Irreversible action** (purge, permanent delete, send, publish): confirm
|
||||
once, and say plainly that it cannot be undone.
|
||||
|
||||
Ceremony on a reversible action is worse than useless: it trains the user to
|
||||
dismiss dialogs without reading, so the one dialog that matters gets dismissed
|
||||
too.
|
||||
|
||||
## Spend one confirmation per intent
|
||||
|
||||
The confirmation budget is per *intent*, not per *item*. A user removing a
|
||||
hundred things decided once; charge them once. A hundred dialogs for a hundred
|
||||
items is the same as no dialogs at all, minus an hour of their life.
|
||||
|
||||
When one confirmation covers many items, make it carry its weight: name the
|
||||
scope and the exact count ("Purge 143 pages in Skills — this cannot be
|
||||
undone"), and for the genuinely dangerous ones, require typing something
|
||||
deliberate rather than a single click.
|
||||
|
||||
## Let the reversible state be the selection
|
||||
|
||||
Where a flow is *choose many, then commit*, the reversible action is the
|
||||
selection mechanism. Mark → review → commit. No parallel checkbox concept
|
||||
required.
|
||||
|
||||
Marking beats checkboxes on properties, not just tokens: the marks survive a
|
||||
reload, they are visible to a colleague looking at the same screen, they can be
|
||||
undone one at a time, and the user sees the staged set accumulate as they go.
|
||||
|
||||
## Act in place
|
||||
|
||||
An action reports its result where the user is standing. Re-render the row, the
|
||||
card, the item. Keep scroll position, keep focus, keep their place in a long
|
||||
list.
|
||||
|
||||
A full page reload after each action is a scroll-position bug wearing a
|
||||
navigation costume. If the user must find their place again after every click,
|
||||
the hundredth click costs a hundred scrolls.
|
||||
|
||||
## Make silence impossible
|
||||
|
||||
Every action ends in an observable outcome: success shown, or failure shown
|
||||
with a reason. An action that can quietly do nothing is the worst bug class in
|
||||
interface design, because the user keeps working and only later discovers that
|
||||
none of it happened.
|
||||
|
||||
Own your dialogs for the same reason. Browsers let a user permanently suppress
|
||||
native `alert`/`confirm`/`prompt` dialogs ("prevent this page from creating
|
||||
additional dialogs"). Code that reads a suppressed `confirm` sees "cancelled"
|
||||
and correctly does nothing — forever, silently, until a reload. Use the
|
||||
platform's or framework's own modal component so the confirmation is yours to
|
||||
control.
|
||||
|
||||
## Give bulk work a bulk path
|
||||
|
||||
Wherever the data can hold hundreds of items, the interface offers an action
|
||||
over the whole set — a filtered set, a marked set, or a namespace. A per-item
|
||||
action is not a bulk feature no matter how fast the loop.
|
||||
|
||||
Bulk operations are partial by nature. Design for it up front:
|
||||
|
||||
- Process items independently; one failure does not abandon the rest.
|
||||
- Report per item: succeeded, failed with a reason, skipped with a reason.
|
||||
- Show progress while it runs, and let the user cancel between items.
|
||||
- Leave every item either fully done or fully untouched — never half-applied.
|
||||
- Chunk the work so a large set cannot time out the request.
|
||||
|
||||
## Finish the user's job
|
||||
|
||||
Match the actions to the *goal*, not to the data model. If the user's goal is
|
||||
"make this gone" and the tool only offers "make this quiet", the tool is
|
||||
incomplete even though every button works. Hidden is not deleted; unpublished
|
||||
is not removed; archived is not gone.
|
||||
|
||||
Where a two-step model exists for safety (hide, then remove), both steps belong
|
||||
on the screen where the work happens. A second step reachable only from a
|
||||
different page, one item at a time, is a step that will not be taken.
|
||||
|
||||
## Show the outstanding work
|
||||
|
||||
Surface the counts that tell the user how much is left: how many items are
|
||||
marked, how many remain, how many failed. A cleanup task needs a visible
|
||||
ending, otherwise it is just scrolling.
|
||||
|
||||
## Explain a missing action
|
||||
|
||||
When a control is absent because of a permission, a state, or a rule, say so in
|
||||
its place. An unexplained gap reads as a bug and sends the user hunting. State
|
||||
the reason and, where there is one, the path to earning the action.
|
||||
|
||||
## Design check
|
||||
|
||||
Before calling an interface done, walk it as a user with a hundred items:
|
||||
|
||||
1. Count the interactions for one item. Multiply by a hundred. Is that a
|
||||
working afternoon?
|
||||
2. Which actions prompt? Is every one of them irreversible?
|
||||
3. Which actions are irreversible? Does each state that plainly, once, with the
|
||||
count?
|
||||
4. After each action, did the user keep their place?
|
||||
5. Can any action fail silently? What does the user see when it does?
|
||||
6. Is there a path that handles the whole set in one decision?
|
||||
7. Does the tool reach the user's actual goal, or stop at the model's halfway
|
||||
state?
|
||||
@@ -0,0 +1,144 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>A — Ledger is the frame</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@600&family=Jost:wght@300;400;500&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root{
|
||||
--gold:#c2a35a; --gilt:#a8893f; --gold-lit:#e0c878;
|
||||
--plum-noir:#351e28; --blood:#8e3438; --wax:#a84a4e;
|
||||
--crypt:#0f0d12; --grave:#09080b;
|
||||
--panel:#18141d; --panel-deep:#110f15; --panel-raised:#201924;
|
||||
--bone:#e6e0d6; --ash:#b9b2a6; --dust:#9a9086;
|
||||
--ink:#d8c28a; --ink-muted:#a99d8f;
|
||||
--rule:1.78rem;
|
||||
}
|
||||
*{box-sizing:border-box}
|
||||
body{
|
||||
margin:0; background:var(--grave); color:var(--ash);
|
||||
font:400 1rem/1.6 Jost, system-ui, sans-serif;
|
||||
padding:2.5rem 1rem 4rem;
|
||||
}
|
||||
h1,h2,h3{font-family:Cinzel,Georgia,serif;font-weight:600;color:var(--bone);margin:0;
|
||||
text-shadow:0 1px 0 rgba(0,0,0,.9), 0 0 10px rgba(104,32,76,.16)}
|
||||
.label{font-family:Cinzel,Georgia,serif;font-weight:600;font-size:.78rem;
|
||||
letter-spacing:.16em;text-transform:uppercase;color:var(--gold)}
|
||||
|
||||
/* the book: one bound object, hard edges, margins */
|
||||
.book{
|
||||
max-width:1180px;margin:0 auto;background:var(--crypt);
|
||||
border:1px solid rgba(194,163,90,.22);
|
||||
box-shadow:0 30px 80px rgba(0,0,0,.65), inset 0 1px 0 rgba(255,255,255,.03);
|
||||
position:relative;
|
||||
}
|
||||
/* corner pieces — bookbinding */
|
||||
.book::before,.book::after{content:"";position:absolute;width:34px;height:34px;pointer-events:none}
|
||||
.book::before{top:8px;left:8px;border-top:1px solid var(--gilt);border-left:1px solid var(--gilt)}
|
||||
.book::after{bottom:8px;right:8px;border-bottom:1px solid var(--gilt);border-right:1px solid var(--gilt)}
|
||||
|
||||
/* topbar reads as a running head, not a nav bar */
|
||||
.runninghead{
|
||||
display:flex;justify-content:space-between;align-items:baseline;
|
||||
padding:1.1rem 3.2rem .9rem;border-bottom:1px solid rgba(194,163,90,.18);
|
||||
background:linear-gradient(180deg,#1a1017,var(--crypt));
|
||||
}
|
||||
.runninghead nav{display:flex;gap:1.8rem}
|
||||
.runninghead a{color:var(--ink-muted);text-decoration:none;font-family:Cinzel,serif;
|
||||
font-size:.8rem;letter-spacing:.14em;text-transform:uppercase}
|
||||
.runninghead a:hover{color:var(--gold-lit)}
|
||||
.folio{color:var(--dust);font-size:.74rem;letter-spacing:.1em}
|
||||
|
||||
/* frontispiece — a title page, not a hero */
|
||||
.frontispiece{padding:4.5rem 3.2rem 3rem;text-align:center;
|
||||
background:radial-gradient(120% 90% at 50% 0%, rgba(53,30,40,.55), transparent 70%)}
|
||||
.frontispiece h1{font-size:2.9rem;letter-spacing:.04em}
|
||||
.frontispiece .sub{color:var(--dust);font-size:.82rem;letter-spacing:.22em;
|
||||
text-transform:uppercase;font-family:Cinzel,serif;margin-top:.9rem}
|
||||
.fade-rule{height:1px;margin:1.8rem auto;max-width:420px;
|
||||
background:linear-gradient(90deg,transparent,var(--gilt),transparent)}
|
||||
.seal{display:inline-block;margin-top:.4rem;width:44px;height:44px;border-radius:50%;
|
||||
background:radial-gradient(circle at 35% 30%, var(--wax), #5e2124);
|
||||
box-shadow:inset 0 -2px 6px rgba(0,0,0,.6), 0 2px 10px rgba(0,0,0,.5)}
|
||||
.epigraph{max-width:560px;margin:1.6rem auto 0;color:var(--ink-muted);
|
||||
font-style:italic;font-size:.95rem}
|
||||
|
||||
/* the index: entries in ruled columns */
|
||||
.leaf{padding:1rem 3.2rem 3rem}
|
||||
.leaf-head{display:flex;justify-content:space-between;align-items:baseline;
|
||||
padding-bottom:.5rem;border-bottom:1px solid rgba(194,163,90,.14);margin-bottom:.2rem}
|
||||
.entry{
|
||||
display:grid;grid-template-columns:1fr 8rem 5rem;gap:1rem;align-items:baseline;
|
||||
min-height:var(--rule);padding:.42rem 0;
|
||||
border-bottom:1px solid rgba(194,163,90,.07);
|
||||
}
|
||||
.entry:hover{background:linear-gradient(90deg,rgba(53,30,40,.5),transparent)}
|
||||
.entry .t{color:var(--ink);font-family:Cinzel,serif;font-size:.98rem}
|
||||
.entry .m{color:var(--dust);font-size:.8rem;text-align:right}
|
||||
.entry .n{color:var(--gold);font-size:.8rem;text-align:right;font-variant-numeric:tabular-nums}
|
||||
|
||||
.two{display:grid;grid-template-columns:1fr 300px;gap:3rem;padding:0 3.2rem 3rem}
|
||||
.marginalia{border-left:1px solid rgba(194,163,90,.14);padding-left:1.4rem}
|
||||
.marginalia p{font-size:.88rem;color:var(--dust)}
|
||||
.plaque{display:inline-block;padding:.28rem .7rem;border:1px solid rgba(194,163,90,.3);
|
||||
background:var(--panel-deep);color:var(--ink);font-family:Cinzel,serif;
|
||||
font-size:.7rem;letter-spacing:.14em;text-transform:uppercase}
|
||||
.colophon{padding:1.4rem 3.2rem;border-top:1px solid rgba(194,163,90,.18);
|
||||
display:flex;justify-content:space-between;color:var(--dust);font-size:.74rem;
|
||||
letter-spacing:.08em;background:var(--panel-deep)}
|
||||
.tag{color:#7a7168;font-size:.72rem;letter-spacing:.2em;text-transform:uppercase;
|
||||
text-align:center;margin:0 auto 1.2rem;max-width:1180px}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="tag">Option A — the ledger is the frame · zero images</div>
|
||||
|
||||
<div class="book">
|
||||
<div class="runninghead">
|
||||
<span class="label">Shadows Over Westgate</span>
|
||||
<nav><a href="#">The Court</a><a href="#">Entries</a><a href="#">Archive</a><a href="#">Petitions</a><a href="#">Enter</a></nav>
|
||||
<span class="folio">fol. i</span>
|
||||
</div>
|
||||
|
||||
<div class="frontispiece">
|
||||
<div class="label">Being an account of the city and its debts</div>
|
||||
<div class="fade-rule"></div>
|
||||
<h1>Shadows Over Westgate</h1>
|
||||
<div class="sub">A record kept against the dark</div>
|
||||
<div class="fade-rule"></div>
|
||||
<div class="seal"></div>
|
||||
<p class="epigraph">Everyone in this city is compromised. The only question the ledger asks is by how much, and to whom.</p>
|
||||
<div style="margin-top:1.6rem"><span class="plaque">Open the record</span></div>
|
||||
</div>
|
||||
|
||||
<div class="leaf">
|
||||
<div class="leaf-head"><h2 style="font-size:1.05rem;letter-spacing:.06em">Latest entries</h2><span class="folio">fol. ii — vii</span></div>
|
||||
<div class="entry"><span class="t">The Ashgate Concession</span><span class="m">Ledgerkeeper</span><span class="n">41</span></div>
|
||||
<div class="entry"><span class="t">On the matter of the second seal</span><span class="m">V. Marrow</span><span class="n">17</span></div>
|
||||
<div class="entry"><span class="t">Debts owed to the Clockmakers' Guild</span><span class="m">Anonymous</span><span class="n">8</span></div>
|
||||
<div class="entry"><span class="t">Petition: the widow at Candlewharf</span><span class="m">House Verrine</span><span class="n">63</span></div>
|
||||
<div class="entry"><span class="t">Errata to the census of 1114</span><span class="m">Ledgerkeeper</span><span class="n">2</span></div>
|
||||
</div>
|
||||
|
||||
<div class="two">
|
||||
<div>
|
||||
<h3 style="font-size:1rem;letter-spacing:.06em">From the archive</h3>
|
||||
<div class="fade-rule" style="margin:.9rem 0 1.2rem;max-width:none"></div>
|
||||
<p>The city keeps two books. One is public, ruled and countersigned, and it is a lie of omission. The other is this one.</p>
|
||||
<p>Entries are made nightly. Corrections are made rarely, and never by the same hand twice.</p>
|
||||
</div>
|
||||
<aside class="marginalia">
|
||||
<div class="label" style="font-size:.7rem">Standing</div>
|
||||
<p style="margin-top:.8rem">The record is open. Fourteen hands are writing tonight.</p>
|
||||
<div class="fade-rule" style="margin:1.2rem 0;max-width:none"></div>
|
||||
<div class="label" style="font-size:.7rem">Seals in force</div>
|
||||
<p style="margin-top:.8rem">Verrine · Marrow · the Guild</p>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<div class="colophon"><span>Kept at Westgate</span><span>fol. vii of ccxi</span></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,91 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Westgate — the archive</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@600&family=Jost:wght@300;400;500&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="shell.css">
|
||||
<style>
|
||||
/* indoors: same shell, ornament turned down, ledger takes over */
|
||||
/* One Rail: shell and content share the same left/right edge on every page.
|
||||
Nothing is inset inside anything. Density is spacing and ornament only. */
|
||||
:root{--fit:.35}
|
||||
.crumb{display:flex;gap:.6rem;align-items:baseline;padding:1.2rem var(--rail) .4rem;
|
||||
font-size:.8rem;color:var(--dust)}
|
||||
.crumb a{text-decoration:none;color:var(--ink-muted)}
|
||||
.crumb span{color:var(--pewter-deep)}
|
||||
.room{display:grid;grid-template-columns:1fr 300px;gap:2rem;padding:.6rem var(--rail) 3rem;
|
||||
background:var(--crypt)}
|
||||
.bar{display:flex;justify-content:space-between;align-items:center;
|
||||
padding:.5rem .2rem 1rem}
|
||||
.bar h1{font-size:1.35rem;letter-spacing:.05em}
|
||||
aside .box{padding-left:1.2rem;margin-bottom:1.6rem;position:relative}
|
||||
aside .box::before{content:"";position:absolute;left:0;top:.2rem;bottom:.2rem;width:1px;
|
||||
background:linear-gradient(180deg,rgba(140,143,151,.3),transparent)}
|
||||
/* the page title sits on a fading rule, not a hard bar */
|
||||
.bar{border-bottom:0}
|
||||
aside p{font-size:.88rem;color:var(--dust);margin:.6rem 0 0}
|
||||
.pager{display:flex;gap:.4rem;justify-content:flex-end;margin-top:1rem;
|
||||
font-size:.8rem;color:var(--dust)}
|
||||
.pager b{color:var(--gold);font-weight:400}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="tag">Indoors — app density (--fit: .35)</div>
|
||||
<div class="page">
|
||||
|
||||
<header class="lintel">
|
||||
<a class="mark" href="index.html">Shadows Over Westgate</a>
|
||||
<nav>
|
||||
<a href="index.html">The City</a>
|
||||
<a href="forum.html" aria-current="page">Forum</a>
|
||||
<a href="forum.html">Archive</a>
|
||||
<a href="forum.html">Enter</a>
|
||||
</nav>
|
||||
<div class="rivets"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div>
|
||||
</header>
|
||||
|
||||
<div class="status"><span class="lamp"></span> The gates are open. Fourteen souls walk the city tonight.</div>
|
||||
|
||||
<div class="crumb"><a href="index.html">The City</a><span>·</span><a href="forum.html">The Archive</a><span>·</span><span>Entries of the Court</span></div>
|
||||
|
||||
<div class="room">
|
||||
<main>
|
||||
<div class="bar">
|
||||
<h1>Entries of the Court</h1>
|
||||
<a class="btn" href="#">New entry</a>
|
||||
</div>
|
||||
|
||||
<div class="ledger framed">
|
||||
<div class="row head"><span>Entry</span><span>Hand</span><span>Replies</span><span>Last</span></div>
|
||||
<div class="row"><a class="t" href="#">The Ashgate Concession</a><span class="m">Ledgerkeeper</span><span class="n">41</span><span class="d">2h</span></div>
|
||||
<div class="row"><a class="t" href="#">On the matter of the second seal</a><span class="m">V. Marrow</span><span class="n">17</span><span class="d">5h</span></div>
|
||||
<div class="row"><a class="t" href="#">Debts owed to the Clockmakers' Guild</a><span class="m">Anonymous</span><span class="n">8</span><span class="d">9h</span></div>
|
||||
<div class="row"><a class="t" href="#">Petition: the widow at Candlewharf</a><span class="m">House Verrine</span><span class="n">63</span><span class="d">1d</span></div>
|
||||
<div class="row"><a class="t" href="#">Errata to the census of 1114</a><span class="m">Ledgerkeeper</span><span class="n">2</span><span class="d">1d</span></div>
|
||||
<div class="row"><a class="t" href="#">Concerning the Foundry night shift</a><span class="m">Grell</span><span class="n">29</span><span class="d">2d</span></div>
|
||||
<div class="row"><a class="t" href="#">A list of names, unsigned</a><span class="m">Anonymous</span><span class="n">104</span><span class="d">2d</span></div>
|
||||
<div class="row"><a class="t" href="#">On lamp oil and its accounting</a><span class="m">Sable</span><span class="n">6</span><span class="d">3d</span></div>
|
||||
<div class="row"><a class="t" href="#">The harbour tithe, revised</a><span class="m">House Verrine</span><span class="n">12</span><span class="d">4d</span></div>
|
||||
<div class="row" style="border:0"><a class="t" href="#">Who kept the third key</a><span class="m">V. Marrow</span><span class="n">38</span><span class="d">6d</span></div>
|
||||
</div>
|
||||
|
||||
<div class="pager"><b>1</b> · 2 · 3 · 4 · 211</div>
|
||||
</main>
|
||||
|
||||
<aside>
|
||||
<div class="box"><div class="label dull" style="font-size:.7rem">Tonight</div>
|
||||
<p>Rain off the harbour. Two wards dark. The Foundry is still running.</p></div>
|
||||
<div class="box"><div class="label dull" style="font-size:.7rem">Seals in force</div>
|
||||
<p>Verrine · Marrow · the Guild</p></div>
|
||||
<div class="box"><div class="label dull" style="font-size:.7rem">Newly arrived</div>
|
||||
<p>Three petitioners at the Verrine gate.</p></div>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<footer class="foundation"><span>Westgate</span><span>Est. 1114 · the record is kept</span></footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,105 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Westgate — outdoors</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@600&family=Jost:wght@300;400;500&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="shell.css">
|
||||
<style>
|
||||
/* showcase-only furniture: lives on the home page, nowhere else */
|
||||
.horizon{position:relative;min-height:440px;display:flex;flex-direction:column;
|
||||
justify-content:flex-end;padding:5rem var(--rail) 3rem;overflow:hidden;
|
||||
background:
|
||||
radial-gradient(70% 55% at 50% 100%, rgba(200,176,138,.16), transparent 70%),
|
||||
radial-gradient(90% 70% at 50% 100%, rgba(53,30,40,.85), transparent 72%),
|
||||
linear-gradient(180deg,#07060a 0%, #0d0b11 55%, #16121a 100%)}
|
||||
.skyline{position:absolute;left:0;right:0;bottom:0;height:190px;display:flex;
|
||||
align-items:flex-end;gap:6px;padding:0 1rem;opacity:.92}
|
||||
.skyline b{display:block;background:#06050880;
|
||||
border-top:1px solid rgba(140,143,151,.10);box-shadow:0 -1px 0 rgba(0,0,0,.9)}
|
||||
/* One Rail: the hero sits on the same 3-column grid as the plaques below,
|
||||
so its right edge lands on a shared grid line — a column, not an inset. */
|
||||
.horizon .inner{position:relative;display:grid;
|
||||
grid-template-columns:repeat(3,1fr);column-gap:1px}
|
||||
.horizon .inner > *{grid-column:1 / span 2}
|
||||
.horizon h1{font-size:3.2rem;letter-spacing:.03em;line-height:1.08}
|
||||
.horizon p{color:var(--ink-muted);margin-top:1rem}
|
||||
.cta{display:flex;gap:.9rem;margin-top:1.8rem}
|
||||
/* plaques divided by rules that fade top and bottom, not a hard grid */
|
||||
.band{display:grid;grid-template-columns:repeat(3,1fr)}
|
||||
.plaque{background:linear-gradient(180deg,#191419,#120f14);
|
||||
padding:1.6rem var(--rail);position:relative}
|
||||
.plaque + .plaque::before{content:"";position:absolute;left:0;top:12%;bottom:12%;width:1px;
|
||||
background:linear-gradient(180deg,transparent,rgba(194,163,90,.28),transparent)}
|
||||
.plaque h3{font-size:1.05rem;letter-spacing:.05em;margin:.7rem 0 .5rem}
|
||||
.plaque p{font-size:.9rem;color:var(--dust);margin:0}
|
||||
.dispatches{padding:2.5rem var(--rail);background:var(--crypt);
|
||||
border-top:0}
|
||||
.dispatches .lead{display:flex;justify-content:space-between;align-items:baseline;
|
||||
margin-bottom:.9rem}
|
||||
.dispatches a.more{font-family:Cinzel,serif;font-size:.74rem;letter-spacing:.14em;
|
||||
text-transform:uppercase;text-decoration:none}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="tag">Outdoors — showcase density (--fit: 1)</div>
|
||||
<div class="page">
|
||||
|
||||
<header class="lintel">
|
||||
<a class="mark" href="index.html">Shadows Over Westgate</a>
|
||||
<nav>
|
||||
<a href="index.html" aria-current="page">The City</a>
|
||||
<a href="forum.html">Forum</a>
|
||||
<a href="forum.html">Archive</a>
|
||||
<a href="forum.html">Enter</a>
|
||||
</nav>
|
||||
<div class="rivets"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div>
|
||||
</header>
|
||||
|
||||
<div class="status"><span class="lamp"></span> The gates are open. Fourteen souls walk the city tonight.</div>
|
||||
|
||||
<section class="horizon">
|
||||
<div class="skyline">
|
||||
<b style="width:5%;height:52%"></b><b style="width:3%;height:74%"></b><b style="width:7%;height:38%"></b>
|
||||
<b style="width:4%;height:88%"></b><b style="width:6%;height:46%"></b><b style="width:3%;height:96%"></b>
|
||||
<b style="width:8%;height:34%"></b><b style="width:5%;height:66%"></b><b style="width:4%;height:44%"></b>
|
||||
<b style="width:9%;height:28%"></b><b style="width:3%;height:80%"></b><b style="width:6%;height:40%"></b>
|
||||
<b style="width:4%;height:58%"></b><b style="width:7%;height:32%"></b><b style="width:5%;height:70%"></b>
|
||||
<b style="width:3%;height:50%"></b><b style="width:8%;height:36%"></b><b style="width:4%;height:62%"></b>
|
||||
</div>
|
||||
<div class="inner">
|
||||
<div class="label">A gothic-fantasy roleplay city</div>
|
||||
<h1>The lamps are lit.<br>No one is innocent.</h1>
|
||||
<p>Renaissance stone, clockwork and lightning, and a court that has ruled from behind the shutters for four hundred years. You are already compromised. Decide what it bought you.</p>
|
||||
<div class="cta">
|
||||
<a class="btn" href="forum.html">Enter the city</a>
|
||||
<a class="btn dull" href="forum.html">Read the archive</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="band">
|
||||
<div class="plaque"><div class="label dull">Decadence</div><h3>The Court</h3><p>Houses, favours, and debts that outlive the people who signed them.</p></div>
|
||||
<div class="plaque"><div class="label dull">Decay</div><h3>The Wards</h3><p>Candlewharf, Ashgate, the Foundry. Gaslight where the money is, dark where it is not.</p></div>
|
||||
<div class="plaque"><div class="label dull">Machinery</div><h3>The Guild</h3><p>Clockwork, steam, and the new lightning — sold to anyone who can pay the interest.</p></div>
|
||||
</section>
|
||||
|
||||
<section class="dispatches">
|
||||
<div class="lead">
|
||||
<div class="label">Latest dispatches</div>
|
||||
<a class="more" href="forum.html">All entries →</a>
|
||||
</div>
|
||||
<div class="fade-rule" style="margin-bottom:1rem"></div>
|
||||
<div class="ledger framed">
|
||||
<div class="row"><a class="t" href="forum.html">The Ashgate Concession</a><span class="m">Ledgerkeeper</span><span class="n">41</span><span class="d">2h</span></div>
|
||||
<div class="row"><a class="t" href="forum.html">On the matter of the second seal</a><span class="m">V. Marrow</span><span class="n">17</span><span class="d">5h</span></div>
|
||||
<div class="row" style="border:0"><a class="t" href="forum.html">Petition: the widow at Candlewharf</a><span class="m">House Verrine</span><span class="n">63</span><span class="d">1d</span></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer class="foundation"><span>Westgate</span><span>Est. 1114 · the record is kept</span></footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,108 @@
|
||||
/* Throwaway prototype — Frame B shell, two metals, one shell / two densities.
|
||||
Not for merge. Values here are candidates, not tokens. */
|
||||
:root{
|
||||
--gold:#c2a35a; --gilt:#a8893f; --gold-lit:#e0c878;
|
||||
--plum-noir:#351e28; --blood:#8e3438; --wax:#a84a4e;
|
||||
--crypt:#0f0d12; --grave:#09080b;
|
||||
--panel:#18141d; --panel-deep:#110f15; --panel-raised:#201924;
|
||||
--bone:#e6e0d6; --ash:#b9b2a6; --dust:#9a9086;
|
||||
--ink:#d8c28a; --ink-muted:#a99d8f;
|
||||
/* second metal — structural only */
|
||||
--pewter:#8c8f97; --pewter-deep:#5a5d64;
|
||||
--filament:#c8b08a;
|
||||
/* the Threshold: one knob per density */
|
||||
--fit:1; /* ornament weight: 1 outdoors, 0.35 indoors */
|
||||
--page:1420px;
|
||||
--rail:2rem; /* One Rail: the only horizontal edge on any page */
|
||||
}
|
||||
*{box-sizing:border-box}
|
||||
body{margin:0;background:var(--grave);color:var(--ash);
|
||||
font:400 1rem/1.6 Jost,system-ui,sans-serif}
|
||||
a{color:var(--gold)}
|
||||
h1,h2,h3{font-family:Cinzel,Georgia,serif;font-weight:600;color:var(--bone);margin:0;
|
||||
text-shadow:0 1px 0 rgba(0,0,0,.9), 0 0 10px rgba(104,32,76,.16)}
|
||||
.label{font-family:Cinzel,Georgia,serif;font-weight:600;font-size:.78rem;
|
||||
letter-spacing:.16em;text-transform:uppercase;color:var(--gold)}
|
||||
.label.dull{color:var(--pewter)}
|
||||
.page{max-width:var(--page);margin:0 auto}
|
||||
.fade-rule{height:1px;background:linear-gradient(90deg,transparent,var(--gilt),transparent)}
|
||||
|
||||
/* ---- lintel: identical on every page ---- */
|
||||
.lintel{
|
||||
display:flex;align-items:center;gap:2rem;padding:.85rem var(--rail);position:relative;
|
||||
background:linear-gradient(180deg,#1b1a1f,#111015);
|
||||
border-top:1px solid rgba(140,143,151,calc(.28 * var(--fit) + .06));
|
||||
box-shadow:0 14px 30px rgba(0,0,0,.5);
|
||||
}
|
||||
/* the lintel's underside is a gilt rule that fades out, not a hard line */
|
||||
.lintel::after{content:"";position:absolute;left:0;right:0;bottom:0;height:1px;
|
||||
background:linear-gradient(90deg,transparent,rgba(194,163,90,.32) 12%,
|
||||
rgba(194,163,90,.32) 88%,transparent)}
|
||||
.rivets{position:absolute;inset:auto 0 6px 0;display:flex;justify-content:space-between;
|
||||
padding:0 1rem;opacity:var(--fit)}
|
||||
.rivets i{width:4px;height:4px;border-radius:50%;background:var(--pewter-deep);
|
||||
box-shadow:inset 0 1px 0 rgba(255,255,255,.25)}
|
||||
.mark{font-family:Cinzel,serif;color:var(--gold);letter-spacing:.18em;
|
||||
text-transform:uppercase;font-size:.86rem;text-decoration:none}
|
||||
.lintel nav{display:flex;gap:1.6rem;margin-left:auto}
|
||||
.lintel nav a{color:var(--ink-muted);text-decoration:none;font-family:Cinzel,serif;
|
||||
font-size:.78rem;letter-spacing:.14em;text-transform:uppercase}
|
||||
.lintel nav a:hover{color:var(--gold-lit)}
|
||||
.lintel nav a[aria-current]{color:var(--gold);border-bottom:1px solid var(--gilt)}
|
||||
|
||||
/* ---- status: a sentence, on every page ---- */
|
||||
.status{display:flex;align-items:center;gap:.8rem;padding:.9rem var(--rail);
|
||||
background:var(--panel-deep);font-size:.88rem;color:var(--ink-muted);
|
||||
position:relative}
|
||||
.status::after{content:"";position:absolute;left:0;right:0;bottom:0;height:1px;
|
||||
background:linear-gradient(90deg,transparent,rgba(140,143,151,.24) 12%,
|
||||
rgba(140,143,151,.24) 88%,transparent)}
|
||||
.lamp{width:8px;height:8px;border-radius:50%;background:var(--filament);
|
||||
box-shadow:0 0 10px 2px rgba(200,176,138,.5)}
|
||||
|
||||
/* ---- foundation: identical on every page ---- */
|
||||
.foundation{padding:1.6rem var(--rail);background:#0a090c;position:relative;
|
||||
display:flex;justify-content:space-between;color:var(--dust);
|
||||
font-size:.74rem;letter-spacing:.1em}
|
||||
.foundation::before{content:"";position:absolute;left:0;right:0;top:0;height:1px;
|
||||
background:linear-gradient(90deg,transparent,rgba(194,163,90,.26) 12%,
|
||||
rgba(194,163,90,.26) 88%,transparent)}
|
||||
|
||||
/* ---- buttons ---- */
|
||||
.btn{display:inline-block;padding:.6rem 1.3rem;font-family:Cinzel,serif;font-size:.76rem;
|
||||
letter-spacing:.16em;text-transform:uppercase;border:1px solid var(--gilt);
|
||||
color:var(--ink);background:linear-gradient(180deg,#2a2214,#1a1510);text-decoration:none}
|
||||
.btn.dull{border-color:rgba(140,143,151,.4);color:var(--pewter);
|
||||
background:linear-gradient(180deg,#232529,#16171b)}
|
||||
|
||||
/* ---- gilded frame: a notched plate with corner marks, not a box ----
|
||||
Edges fade rather than stop; corners are cut, then re-marked in gilt. */
|
||||
/* No corner marks: an ornament inset from an edge agrees with nothing and
|
||||
reads as misalignment. The plate is carried by material and fading rules. */
|
||||
.framed{position:relative}
|
||||
/* a rule that fades at both ends — the default separator */
|
||||
.hair{height:1px;border:0;margin:0;
|
||||
background:linear-gradient(90deg,transparent,rgba(194,163,90,.34) 18%,
|
||||
rgba(194,163,90,.34) 82%,transparent)}
|
||||
.hair.dull{background:linear-gradient(90deg,transparent,rgba(140,143,151,.3) 18%,
|
||||
rgba(140,143,151,.3) 82%,transparent)}
|
||||
|
||||
/* ---- ledger interior: the archive material ---- */
|
||||
.ledger{background:var(--panel);
|
||||
box-shadow:inset 0 0 0 1px rgba(194,163,90,.10),
|
||||
inset 0 1px 0 rgba(255,255,255,.022),
|
||||
0 8px 22px rgba(0,0,0,.28)}
|
||||
.ledger .row{display:grid;grid-template-columns:1fr 8rem 4rem 5rem;gap:1rem;
|
||||
align-items:baseline;min-height:1.78rem;padding:.5rem 1.2rem;
|
||||
border-bottom:1px solid rgba(194,163,90,.07)}
|
||||
.ledger .row:hover{background:linear-gradient(90deg,rgba(53,30,40,.55),transparent)}
|
||||
.ledger .t{color:var(--ink);font-family:Cinzel,serif;font-size:.95rem;text-decoration:none}
|
||||
.ledger .m,.ledger .n,.ledger .d{font-size:.8rem;text-align:right;color:var(--dust)}
|
||||
.ledger .n{color:var(--gold);font-variant-numeric:tabular-nums}
|
||||
/* header row uses pewter — it structures, it is not touchable */
|
||||
.ledger .head{background:var(--panel-deep);border-bottom:1px solid rgba(140,143,151,.2)}
|
||||
.ledger .head span{font-family:Cinzel,serif;font-size:.7rem;letter-spacing:.14em;
|
||||
text-transform:uppercase;color:var(--pewter)}
|
||||
|
||||
.tag{color:#7a7168;font-size:.72rem;letter-spacing:.2em;text-transform:uppercase;
|
||||
text-align:center;padding:1rem 0 .4rem}
|
||||
Reference in New Issue
Block a user