Subgrid Domain settings rows, match Address mode and trusted-IP controls, and pin Export/Danger buttons to a shared baseline in split cards. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+3
-2
@@ -11,8 +11,9 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version
|
||||
(`N messages / Ws — Settings`) and in message-limit labels; Settings uses a
|
||||
code-row for the L1 value. Domain and application rate-limit status uses the
|
||||
shared `st` badge. Trusted-IP help sits under the IP field; domain rate-limit
|
||||
help sits above Save; Address mode drops the duplicate field label; field-pair
|
||||
top spacing aligns with paired DMARC labels.
|
||||
help sits above Save; Address mode / trusted-IP columns share muted leads and
|
||||
matched control height; Domain settings uses subgrid so DMARC and rate-limit
|
||||
rows line up; paired `.split` cards pin primary actions to a shared baseline.
|
||||
|
||||
## [1.2.4] - 2026-08-12
|
||||
|
||||
|
||||
@@ -360,6 +360,19 @@ button.danger:hover, a.danger:hover { background: var(--danger-fill-hover); }
|
||||
.app .actions > .panel .panel-buttons form { margin-top: 0; }
|
||||
.app .actions > .panel .check-cols { margin-top: 0.6rem; }
|
||||
.app .actions > .panel .check-col > form { margin-top: 0; }
|
||||
.app .actions > .panel .check-col > .check-col-title { margin-top: 0; }
|
||||
.app .actions > .panel .check-col > .muted { margin: 0; }
|
||||
/* Address mode select ‖ trusted-IP field: same top offset and control height. */
|
||||
.app .actions > .panel .check-col > .muted + form > select:first-of-type,
|
||||
.app .actions > .panel .check-col > .muted + form > textarea[name="allowed_ips"] {
|
||||
margin-top: 0.45rem;
|
||||
min-height: 2.55rem;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.app .actions > .panel .check-col > .muted + form > textarea[name="allowed_ips"] {
|
||||
field-sizing: fixed;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.credential { border-color: var(--credential-border); background: var(--credential-bg); }
|
||||
/* Panel navigation: rendered once from the layout, so it is present on every
|
||||
authenticated page without each content template having to include it. */
|
||||
@@ -538,6 +551,22 @@ meter { width: 5rem; height: 0.7rem; vertical-align: middle; margin-right: 0.4re
|
||||
the cell to shrink below the card's intrinsic minimum when the column is
|
||||
tight. */
|
||||
.split > .card { margin-left: 0; margin-right: 0; min-width: 0; }
|
||||
/* Paired cards often differ in body length (export's encrypt checkbox vs
|
||||
danger's short blurb). Stretch both in the grid row and pin the primary
|
||||
action to the bottom so the buttons share a baseline. */
|
||||
.split > .card {
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.split > .card > form {
|
||||
display: flex; flex-direction: column; flex: 1 1 auto;
|
||||
}
|
||||
.split > .card > form > button[type="submit"]:last-child {
|
||||
margin-top: auto;
|
||||
}
|
||||
.split > .card > a.danger:last-child,
|
||||
.split > .card > a.btn:last-child {
|
||||
margin-top: auto; align-self: flex-start;
|
||||
}
|
||||
/* The gap already spaces the cards inside the grid; the general .card + .card
|
||||
rule would add a second gap's worth on top of it, and only to the second
|
||||
card, which in one column reads as an uneven stack. The blocks around the
|
||||
@@ -561,16 +590,37 @@ meter { width: 5rem; height: 0.7rem; vertical-align: middle; margin-right: 0.4re
|
||||
margin: 1rem 0 0.35rem; font-size: 1.05rem; font-weight: 600;
|
||||
}
|
||||
.card > h2 + .check-col-title { margin-top: 0.55rem; }
|
||||
/* Domain settings pairs DMARC ‖ rate limit: share four row tracks so titles,
|
||||
leads, fields and action buttons line up across columns. */
|
||||
@supports (grid-template-rows: subgrid) {
|
||||
.check-cols-rows {
|
||||
grid-template-rows: auto auto 1fr auto;
|
||||
}
|
||||
.check-cols-rows > .check-col {
|
||||
display: grid;
|
||||
grid-template-rows: subgrid;
|
||||
grid-row: span 4;
|
||||
}
|
||||
}
|
||||
.check-cols-rows > .check-col > .check-col-title { margin-top: 0; }
|
||||
.check-cols-rows > .check-col > .muted { margin: 0; }
|
||||
.check-col-fields { min-width: 0; }
|
||||
.check-col-fields > form { margin: 0; }
|
||||
.check-col-fields .field-pair { margin-top: 0; }
|
||||
.check-col-fields .field-pair > div > label { margin-top: 0.9rem; }
|
||||
.check-col-actions {
|
||||
display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem 0.75rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
.check-col-actions > button { margin-top: 1.2rem; }
|
||||
.check-col-actions > form.inline { margin: 0; }
|
||||
.check-col-actions > form.inline > button { margin-top: 1.2rem; }
|
||||
/* Two short fields on one row (rate-limit message count ‖ window). Falls to
|
||||
one column when the parent is too narrow for both. */
|
||||
.field-pair {
|
||||
display: grid; grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
|
||||
gap: 0 1rem;
|
||||
gap: 0 1rem; margin-top: 0.45rem;
|
||||
}
|
||||
/* First control row in a check-col form: match a bare label's top margin so
|
||||
paired columns (DMARC ‖ rate limit) line up their first field labels. */
|
||||
form > .field-pair { margin-top: 0; }
|
||||
form > .field-pair > div > label { margin-top: 0.9rem; }
|
||||
.field-pair > div { min-width: 0; }
|
||||
.field-pair label { margin-top: 0.45rem; }
|
||||
/* Hint under a field-pair sits tight above the form's submit button (the
|
||||
|
||||
@@ -329,6 +329,7 @@
|
||||
<div class="check-cols">
|
||||
<div class="check-col">
|
||||
<p class="check-col-title">Address mode</p>
|
||||
<p class="muted">Which From addresses this application may use.</p>
|
||||
<form method="post" action="/applications/{{.ID}}/mode">
|
||||
<select name="mode" data-list-mode="{{$.List}}" aria-label="Address mode">
|
||||
<option value="{{$.Wildcard}}" {{if eq .AddressMode $.Wildcard}}selected{{end}}>Any address of the domain</option>
|
||||
@@ -345,12 +346,14 @@
|
||||
<div class="check-col">
|
||||
<p class="check-col-title">Optional trusted-IP override
|
||||
{{if .HasLimit}}<span class="st st-ok">active</span>{{else}}<span class="st st-unknown">inactive</span>{{end}}</p>
|
||||
<p class="muted">One per line or comma-separated.</p>
|
||||
<form id="rl-{{.ID}}" method="post" action="/applications/{{.ID}}/ratelimit">
|
||||
<label>Trusted client IPs (required; one per line or comma-separated)</label>
|
||||
<textarea name="allowed_ips" rows="2" placeholder="203.0.113.10">{{.IPsText}}</textarea>
|
||||
<textarea name="allowed_ips" rows="1" placeholder="203.0.113.10"
|
||||
aria-label="Trusted client IPs">{{.IPsText}}</textarea>
|
||||
<p class="muted">These IPs get a higher ceiling than the domain
|
||||
(≤ level 1) and skip the domain check; everyone else uses
|
||||
{{if $.DomainHasRL}}the domain ceiling ({{$.DomainRLMaxNum}}){{else}}level 1 only{{end}}.</p>
|
||||
the domain level-2 limit{{if $.DomainHasRL}} ({{$.DomainRLMaxNum}}){{end}}
|
||||
if set, otherwise level 1.</p>
|
||||
<div class="field-pair">
|
||||
<div>
|
||||
<label>Message limit (max {{$.L1Messages}})</label>
|
||||
@@ -390,12 +393,13 @@
|
||||
<div class="card" id="domain-settings">
|
||||
<h2>Domain settings</h2>
|
||||
|
||||
<div class="check-cols">
|
||||
<div class="check-cols check-cols-rows">
|
||||
<div class="check-col">
|
||||
<p class="check-col-title">DMARC reports</p>
|
||||
<p class="muted">Default comes from <a href="/settings">Settings</a>;
|
||||
override per domain here.</p>
|
||||
<form method="post" action="/domains/{{.Domain.ID}}/dmarc">
|
||||
<div class="check-col-fields">
|
||||
<form id="dmarc-domain" method="post" action="/domains/{{.Domain.ID}}/dmarc">
|
||||
<label for="dmarc_rua_mode">Aggregate reports (rua=)</label>
|
||||
<select id="dmarc_rua_mode" name="dmarc_rua_mode" data-custom-mode="custom">
|
||||
<option value="inherit"{{if eq .DMARCRuaMode "inherit"}} selected{{end}}>Same as Settings{{if .ProfileDMARCEmail}} ({{.ProfileDMARCEmail}}){{end}}</option>
|
||||
@@ -409,17 +413,19 @@
|
||||
autocapitalize="none" spellcheck="false" value="{{.DMARCRuaCustom}}"
|
||||
placeholder="reports@your-mail-domain.com">
|
||||
</div>
|
||||
|
||||
<button type="submit">Save DMARC report settings</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="check-col-actions">
|
||||
<button type="submit" form="dmarc-domain">Save DMARC report settings</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="check-col">
|
||||
<p class="check-col-title">Level-2 rate limit
|
||||
{{if .DomainHasRL}}<span class="st st-ok">active</span>{{else}}<span class="st st-unknown">inactive</span>{{end}}</p>
|
||||
<p class="muted">Level 1 backstop: {{.L1Messages}} messages / {{.L1Window}}s — <a href="/settings#rate-limits">Settings</a>.</p>
|
||||
|
||||
<form method="post" action="/domains/{{.Domain.ID}}/ratelimit">
|
||||
<div class="check-col-fields">
|
||||
<form id="rl-domain" method="post" action="/domains/{{.Domain.ID}}/ratelimit">
|
||||
<div class="field-pair">
|
||||
<div>
|
||||
<label for="d_max">Message limit (max {{.L1Messages}})</label>
|
||||
@@ -433,8 +439,10 @@
|
||||
</div>
|
||||
<p class="muted">Applies to every client IP on this domain. Leave the
|
||||
message limit empty to use level 1 only.</p>
|
||||
<button type="submit">Save limit</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="check-col-actions">
|
||||
<button type="submit" form="rl-domain">Save limit</button>
|
||||
{{if .DomainHasRL}}
|
||||
<form class="inline" method="post" action="/domains/{{.Domain.ID}}/ratelimit"
|
||||
data-confirm="Remove the domain rate limit? Only the global level-1 limit will apply.">
|
||||
@@ -444,6 +452,7 @@
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="split">
|
||||
|
||||
Reference in New Issue
Block a user