fabrica

hanna/fabrica

feat(web): card the sign-in and invite forms, centered

b21d224 · hanna committed on 2026-07-25

Wrap the login and invite-activation forms in a centered `.auth-card`
(flexbox, vertically and horizontally centered) and drop the unused
`.form-narrow` layout.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed by hannaSSH key fingerprint: SHA256:4g9cWhkAAw8gwqhJUcVbSnGEvdGwklW+1Aa/fMUu59k
2 files changed · +54 −28UnifiedSplit
assets/base.css +25 −3
@@ -274,9 +274,31 @@ select:focus {
274274 outline: none;
275275 }
276276
277-.form-narrow {
278- max-width: 24rem;
279- margin: 3rem auto;
277+/* Centered auth card (sign in, invite activation). */
278+.auth-wrap {
279+ display: flex;
280+ flex-direction: column;
281+ align-items: center;
282+ justify-content: center;
283+ min-height: calc(100vh - var(--fb-topbar-h) - 4rem);
284+ padding: 1rem 0;
285+}
286+.auth-card {
287+ width: 100%;
288+ max-width: 22rem;
289+ background: var(--fb-bg-raised);
290+ border: 1px solid var(--fb-border);
291+ border-radius: var(--fb-radius);
292+ padding: 1.75rem;
293+}
294+.auth-card h1 {
295+ text-align: center;
296+ margin-bottom: 1rem;
297+}
298+.auth-card form .btn {
299+ width: 100%;
300+ justify-content: center;
301+ margin-top: 1.25rem;
280302 }
281303
282304 /* ---- Flash / notices ---- */
crates/web/src/pages.rs +29 −25
@@ -165,19 +165,21 @@ pub async fn login_form(
165165 /// Render the login form with an optional error notice.
166166 fn login_body(csrf: &str, error: Option<&str>) -> Markup {
167167 html! {
168- div class="form-narrow" {
169- h1 { "Sign in" }
170- @if let Some(err) = error {
171- div class="notice notice-error" { (err) }
172- }
173- form method="post" action="/login" {
174- input type="hidden" name="_csrf" value=(csrf);
175- label for="username" { "Username" }
176- input type="text" id="username" name="username" autocomplete="username" required;
177- label for="password" { "Password" }
178- input type="password" id="password" name="password"
179- autocomplete="current-password" required;
180- p { button class="btn btn-primary" type="submit" { "Sign in" } }
168+ div class="auth-wrap" {
169+ div class="auth-card" {
170+ h1 { "Sign in" }
171+ @if let Some(err) = error {
172+ div class="notice notice-error" { (err) }
173+ }
174+ form method="post" action="/login" {
175+ input type="hidden" name="_csrf" value=(csrf);
176+ label for="username" { "Username" }
177+ input type="text" id="username" name="username" autocomplete="username" required;
178+ label for="password" { "Password" }
179+ input type="password" id="password" name="password"
180+ autocomplete="current-password" required;
181+ button class="btn btn-primary" type="submit" { "Sign in" }
182+ }
181183 }
182184 }
183185 }
@@ -307,18 +309,20 @@ pub async fn invite_form(
307309 let _ = invite;
308310 let (jar, chrome) = build_chrome(&state, jar, None, uri.path().to_string());
309311 let body = html! {
310- div class="form-narrow" {
311- h1 { "Set your password" }
312- p class="muted" { "Choose a password to activate your account." }
313- form method="post" action={ "/invite/" (token) } {
314- input type="hidden" name="_csrf" value=(chrome.csrf);
315- label for="password" { "Password" }
316- input type="password" id="password" name="password"
317- autocomplete="new-password" required minlength="8";
318- label for="confirm" { "Confirm password" }
319- input type="password" id="confirm" name="confirm"
320- autocomplete="new-password" required;
321- p { button class="btn btn-primary" type="submit" { "Activate account" } }
312+ div class="auth-wrap" {
313+ div class="auth-card" {
314+ h1 { "Set your password" }
315+ p class="muted" { "Choose a password to activate your account." }
316+ form method="post" action={ "/invite/" (token) } {
317+ input type="hidden" name="_csrf" value=(chrome.csrf);
318+ label for="password" { "Password" }
319+ input type="password" id="password" name="password"
320+ autocomplete="new-password" required minlength="8";
321+ label for="confirm" { "Confirm password" }
322+ input type="password" id="confirm" name="confirm"
323+ autocomplete="new-password" required;
324+ button class="btn btn-primary" type="submit" { "Activate account" }
325+ }
322326 }
323327 }
324328 };