Public: anyone can read this; the crew can change it.
Design: an app's domain DNS records use the design system, and domain-records leaves §5d #4126
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/3836-design-an-app-s-domain-dns-records-use-t → main 19 commits fork 3 threads · 3 unresolved
4 files
changed +28
−8
docs/design/areas/settings.md added +3
| @@ -0,0 +1,3 @@ | ||
| 1 | # Settings page | |
| 2 | ||
| 3 | - `.record-value` — a DNS record's value on an app's domain row (`.setting-list` row meta): monospace, wraps instead of ellipsizing, and `user-select: all` so one click selects the whole value to copy. |
src/web/pages/settings.ts +15 −8
| @@ -937,7 +937,7 @@ function appDomainsView( | ||
| 937 | 937 | ${when( |
| 938 | 938 | rows.length > 0, |
| 939 | 939 | () => |
| 940 | html`<ul class="list"> | |
| 940 | html`<ul class="list setting-list"> | |
| 941 | 941 | ${rows} |
| 942 | 942 | </ul>`, |
| 943 | 943 | )} |
| @@ -982,13 +982,20 @@ function appDomainsView( | ||
| 982 | 982 | |
| 983 | 983 | /** The records as a person copies them into a zone: type, name, value. */ |
| 984 | 984 | function recordsToPublish(records: DnsRecord[]): Html { |
| 985 | const width = Math.max(...records.map((r) => r.name.length)) + 2; | |
| 986 | const lines = records | |
| 987 | .map((r) => `${r.type.padEnd(6)} ${r.name.padEnd(width)} ${r.value}`) | |
| 988 | .join("\n"); | |
| 989 | const notes = records.filter((r) => r.note !== undefined); | |
| 990 | return html`<pre class="domain-records">${lines}</pre> | |
| 991 | ${notes.map((r) => html`<span class="row-meta">${r.note}</span>`)}`; | |
| 985 | return html`${records.map( | |
| 986 | (r) => | |
| 987 | html`<span class="row-meta" | |
| 988 | >${badge(r.type, { variant: "outline" })} | |
| 989 | <span class="mono">${r.name}</span> | |
| 990 | <span aria-hidden="true">→</span> | |
| 991 | <span class="mono record-value" title="${r.value}" | |
| 992 | >${r.value}</span | |
| 993 | ></span | |
The title repeats the visible text. It is harmless, but useless on touch. Drop it if the value never truncates. |
||
| 994 | >`, | |
| 995 | )} | |
| 996 | ${records | |
| 997 | .filter((r) => r.note !== undefined) | |
| 998 | .map((r) => html`<span class="row-meta">${r.note}</span>`)}`; | |
| 992 | 999 | } |
| 993 | 1000 | |
| 994 | 1001 | const appValues = (repo: Repo, record: AppRecord | undefined): AppValues => ({ |
src/web/style/components.ts +1
| @@ -451,6 +451,7 @@ details[open] > summary.row > .row-lead { transform: rotate(90deg); } | ||
| 451 | 451 | .setting-list .row { flex-wrap: wrap; align-items: flex-start; } |
| 452 | 452 | .setting-list .row-main { flex: 1 1 18rem; } |
| 453 | 453 | .setting-list .row-title, .setting-list .row-meta { white-space: normal; overflow: visible; overflow-wrap: anywhere; } |
| 454 | .setting-list .record-value { user-select: all; } | |
| 454 | 455 | .setting-list .row-trail { flex: 0 1 auto; max-width: 100%; } |
| 455 | 456 | @media (max-width: 640px) { .setting-list .row-trail { flex-basis: 100%; } } |
| 456 | 457 | @media (pointer: coarse) { .row-actions { opacity: 1; } .row { min-height: 44px; } } |
test/app-domains.test.ts +9
| @@ -595,6 +595,15 @@ describe("an app gets its own domain", () => { | ||
| 595 | 595 | `forge-verify=${getDomain(app.db, "example.com")!.token}`, |
| 596 | 596 | ); |
| 597 | 597 | expect(page).toContain("ALIAS"); |
| 598 | expect(page).not.toContain("domain-records"); | |
| 599 | // The records wrap instead of truncating (.row-meta only wraps inside | |
| 600 | // .setting-list), carry the type badge, and the whole value is selectable. | |
| 601 | const token = getDomain(app.db, "example.com")!.token; | |
| 602 | expect(page).toContain('<ul class="list setting-list">'); | |
| 603 | expect(page).toMatch(/badge[^>]*>\s*TXT\s*</); | |
| 604 | expect(page).toMatch( | |
| 605 | new RegExp(`class="mono record-value" title="forge-verify=${token}"`), | |
| 606 | ); | |
| 598 | 607 | expect(page).toContain(`${settings}/domains/example.com/check`); |
| 599 | 608 | expect(page).toContain( |
| 600 | 609 | encodeURIComponent(`${settings}/domains/example.com/delete`), |
new above
offline
The bare → is read aloud as "right arrow". Wrap it in <span aria-hidden="true"> or use a gap instead.