Expand i18n locales and improve reviews UI

Add many new language packs with RTL support, refresh brand assets, and harden review rendering with tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
michilis
2026-08-21 05:06:58 +02:00
co-authored by Cursor
parent 1c5df18e81
commit 47e6537dde
51 changed files with 15291 additions and 246 deletions
+101 -16
View File
@@ -182,7 +182,13 @@
--line-soft: #211F19;
--text: #EDE8DC;
--muted: #9A927E;
--faint: #6B6555;
/*
* The quietest text on the site, but still text: #8A826E is the same warm hue as
* the old #6B6555 lifted just far enough to clear WCAG AA (4.5:1) on the page
* (5.1), a card (4.8) and a raised card (4.5). The old value sat at 3.0 to 3.3,
* which failed the contrast audit on every page that showed a date or an npub.
*/
--faint: #8A826E;
--amber: #F0A93B;
--amber-dim: #8A6320;
--violet: #A78BFA;
@@ -526,7 +532,9 @@ footer { border-top: 1px solid var(--line-soft); margin-top: 70px; }
.foot-inner .links { display: flex; gap: 18px; flex-wrap: wrap; }
.foot-inner a:hover { color: var(--muted); }
.foot-right { margin-left: auto; }
.foot-right a { color: var(--amber); }
/* Dotted underline like every other in-sentence link: colour alone is not enough
to mark a link inside running text (WCAG 1.4.1). */
.foot-right a { color: var(--amber); border-bottom: 1px dotted rgba(240, 169, 59, .4); }
/* ---------- prose (about, wallets) ---------- */
.prose { max-width: 720px; }
@@ -647,7 +655,7 @@ footer { border-top: 1px solid var(--line-soft); margin-top: 70px; }
.cid-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The reviewer's npub, under their name. Mono, quiet, and the copy control. */
.cid.rev-npub { font-family: var(--mono); font-size: 11.5px; color: var(--faint); }
.cid.rev-npub { font-family: var(--mono); font-size: 12px; color: var(--faint); }
.cid.rev-npub:hover { color: var(--amber); }
/* ---------- review rows (rendered by the Reviews island, so styled globally) ---------- */
@@ -669,11 +677,17 @@ footer { border-top: 1px solid var(--line-soft); margin-top: 70px; }
display: grid;
grid-template-columns: 38px minmax(0, 1fr) auto;
align-items: center;
gap: 16px;
padding: 20px;
column-gap: 16px;
/* 12px between the identity row and the body: the text is the content, and it
starts close enough to its author to read as one card at a squint. */
row-gap: 12px;
padding: 18px 20px;
border-bottom: 1px solid var(--line-soft);
transition: background var(--dur-quick);
}
.review:last-of-type { border-bottom: none; }
/* Subtle and stationary: the surface lifts a shade, nothing moves. */
.review:hover { background: var(--card2); }
.rev-avatar { grid-column: 1; grid-row: 1; align-self: start; position: relative; width: 38px; height: 38px; }
.avatar {
@@ -685,6 +699,9 @@ footer { border-top: 1px solid var(--line-soft); margin-top: 70px; }
/* The picture sits over the generated initial, so a dead URL falls back by removing
itself and revealing what was already underneath. No flash, no second request. */
.rev-avatar .avatar.pic { position: absolute; inset: 0; background: var(--card2); }
/* An unresolved key's tile: the first character after `npub1`, set in mono and kept
lowercase, so it reads as a key fragment rather than pretending to be an initial. */
.avatar.key { font-family: var(--mono); font-weight: 500; font-size: 14px; }
/*
* Two lines, and always both: the name (or "Anon") over the shortened npub. The block
@@ -707,36 +724,101 @@ footer { border-top: 1px solid var(--line-soft); margin-top: 70px; }
overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}
/*
* One right-aligned line: stars · date, top-aligned with the name row rather than
* stacked. `line-height: 20px` matches the identity row's height, so the two lines
* sit on the same visual rail.
*/
.rev-meta {
grid-column: 3; grid-row: 1; text-align: right; white-space: nowrap;
display: flex; flex-direction: column; align-items: flex-end; gap: 2px;
display: flex; align-items: center; gap: 6px;
align-self: start; margin-top: 1px; line-height: 20px;
}
.rev-stars { color: var(--amber); font-size: 13px; letter-spacing: 1.5px; }
.rev-stars.bad { color: var(--red); }
.rev-dot { color: var(--faint); font-size: 12px; }
.rev-date { font-size: 12px; color: var(--faint); }
.rev-body { grid-column: 2 / -1; font-size: 14.5px; color: #D8D2C4; line-height: 1.6; overflow-wrap: anywhere; white-space: pre-wrap; }
/*
* The body is the content, so it reads first: full text colour where everything
* around it is muted or faint, and a measure capped at ~65ch so a wide card does not
* stretch a paragraph into an unreadable line.
*/
.rev-text { grid-column: 2 / -1; min-width: 0; }
.rev-body { font-size: 15px; color: var(--text); line-height: 1.6; max-width: 65ch; overflow-wrap: anywhere; white-space: pre-wrap; }
/* Six lines, then "Read more". Applied by armReadMore only when there is a seventh. */
.rev-body.clamped {
display: -webkit-box;
-webkit-line-clamp: 6;
line-clamp: 6;
-webkit-box-orient: vertical;
overflow: hidden;
}
.rev-more {
margin-top: 6px; padding: 0; background: none; border: none; cursor: pointer;
font-family: var(--body); font-size: 13px; color: var(--amber);
border-bottom: 1px solid var(--amber-dim); transition: border-color .15s;
}
.rev-more:hover { border-bottom-color: var(--amber); }
/* Bare URLs in a body. Violet like every other identifier-flavoured link. */
.rev-link { color: var(--violet); border-bottom: 1px dotted rgba(167, 139, 250, .4); }
.rev-link:hover { border-bottom-style: solid; }
.rev-note { grid-column: 2 / -1; font-size: 12px; color: var(--faint); display: flex; align-items: center; gap: 6px; }
.rev-note svg { flex-shrink: 0; }
/* Which mint the review is about. Only the global feed renders it: on a mint page
the mint is the page. */
/* Which mint the review is about, as a small chip. Only the global feed renders it:
on a mint page the mint is the page. */
.rev-mint { grid-column: 2 / -1; min-width: 0; }
.rev-mint a {
font-family: var(--mono); font-size: 12.5px; color: var(--muted);
display: inline-block; max-width: 100%;
overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom;
.rev-mint-chip {
display: inline-flex; align-items: center; gap: 6px; max-width: 100%;
font-family: var(--mono); font-size: 12px; color: var(--muted);
border: 1px solid var(--line); background: var(--card2);
border-radius: 999px; padding: 3px 10px;
transition: color .15s, border-color .15s;
}
.rev-mint-chip svg { flex-shrink: 0; color: var(--faint); transition: color .15s; }
.rev-mint-chip span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.rev-mint-chip:hover { color: var(--amber); border-color: var(--amber-dim); }
.rev-mint-chip:hover svg { color: var(--amber); }
/*
* The quiet actions row: copy npub, open on njump, copy the permalink. Faint icons on
* the card's bottom line, lightening on hover. On a pointer device they arrive with
* the card's own hover (or any focus within it); where nothing can hover they are
* simply always there.
*/
.rev-actions {
grid-column: 2 / -1; display: flex; justify-content: flex-end; gap: 2px;
margin-top: -4px;
}
.rev-act {
display: inline-flex; align-items: center; justify-content: center;
padding: 6px; border: none; background: none; border-radius: 7px;
color: var(--faint); cursor: pointer; transition: color .15s, opacity .15s;
}
.rev-act:hover { color: var(--text); }
.rev-act svg { display: block; }
@media (hover: hover) {
.review .rev-act { opacity: 0; }
.review:hover .rev-act, .review:focus-within .rev-act { opacity: 1; }
}
.rev-mint a:hover { color: var(--amber); }
@media (max-width: 520px) {
/* The meta column drops under the identity rather than squeezing the name. */
.review { grid-template-columns: 38px minmax(0, 1fr); }
.rev-meta { grid-column: 2; grid-row: 2; align-items: flex-start; text-align: left; flex-direction: row; gap: 10px; }
.rev-body, .rev-note { grid-column: 2; }
.rev-meta { grid-column: 2; grid-row: 2; align-self: auto; margin-top: 0; text-align: left; gap: 8px; }
.rev-text, .rev-body, .rev-note, .rev-mint, .rev-actions { grid-column: 2; }
.review { row-gap: 10px; }
}
/*
* The one-line consequence of the "hide anon" toggle: how much of the list is
* showing. In the reviews panel it takes a full-width row under the filters; on the
* feed it is a bare line among the other count lines.
*/
.anon-note { font-size: 12.5px; color: var(--faint); }
.panel .anon-note { padding: 10px 20px; border-bottom: 1px solid var(--line-soft); margin: 0; }
/* ---------- rating only summary ---------- */
/*
* Dozens of "rated 5, no comment" cards drown the reviews that say something, so they
@@ -1312,6 +1394,9 @@ html.modal-open, html.modal-open body { overflow: hidden; }
.login-qr:empty { background: var(--card2); border: 1px dashed var(--line); }
.login-qr svg { display: block; }
/* The QR's link as a button, for the reader whose signer app is on this device. */
.login-open-app { align-self: center; justify-content: center; text-decoration: none; }
.login-uri {
display: flex; align-items: center; gap: 8px;
background: var(--card2); border: 1px solid var(--line-soft);