# fdbck — minimalist UI redesign **Status:** design proposal, not yet approved. **Branch:** `mai/cronus/fdbck-minimalist-ui`. **Author:** cronus, 2026-05-06. m's brief: *"While our app is quite simple, the layout of it seems overly complex and crowded. I want clearer separation (by negative space) between the different elements and smarter control buttons for a smooth UX."* The app is already small (1 stylesheet, 2 reusable components, 5 routes). The problem isn't features — it's that the existing screens fight minimalism with *always-visible action rows*, *redundant copy*, *inline `style="..."` patches*, and *tight vertical rhythm*. This doc proposes per-screen edits that hold features constant but trade visible buttons for whitespace and progressive disclosure. --- ## 1. Audit ### 1.1 `/` (landing) ``` ┌────────────────────────────────────────┐ │ fdbck │ ← 1.75rem h1 │ Private feedback forms and live chat… │ │ │ │ This page is only reachable through… │ │ [ Admin sign-in ] │ ← ghost button └────────────────────────────────────────┘ ``` - Already close to minimalist — 20 lines of markup, two paragraphs, one ghost button. Mostly fine. - The wordmark `fdbck` is the brand moment but renders at the same size as every other page h1 (`1.75rem`). It should *feel* like a logo on the landing page, not a generic heading. - Two paragraphs ("Private feedback forms…" / "This page is only reachable…") partly say the same thing. The second one is a near-tautology — the user is already on the page, so "you got here through a private link" is implicit. - Vertical rhythm is tight: `1.5rem` between subtitle and section, then the button sits right under one short sentence. - Tiny: a marketing-blank landing for a tool whose CTA is "sign in" feels hollow. Either lean further into emptiness (just the wordmark + sign-in) or add one line of confidence-building text. We'll lean into emptiness. ### 1.2 `/login` ``` ┌────────────────────────────────────────┐ │ Sign in │ │ Admin access only. │ │ │ │ Email [______________] │ │ Password [______________] │ │ [ Sign in → ] │ └────────────────────────────────────────┘ ``` - "Sign in" + "Admin access only" is mild redundancy — the page is the form. - Error banner sits *between* the password field and the submit button, which pushes the button down on error and feels like a layout glitch. Better position: above the form (page-level error) or beneath the button as an inline string. - Two fields, one button, no dark patterns — already minimalist. Just needs more vertical breathing room and a quieter subtitle (or none). ### 1.3 `/admin/feedback` (list) ``` ┌──────────────────────────────────────────────────────────────────┐ │ Feedback forms [ + New form ] │ │ Collect feedback through forms or live chat. Share a private… │ │ │ │ Your forms (4) │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ Title (link) [Form+chat] [open] │ │ │ │ 12 responses · 7 messages · created 2026-05-01 │ │ │ │ /f/long-slug-shown-raw │ │ │ │ [Edit] [Copy link] [Open] [Close] [Delete] ← 5 buttons │ │ │ └──────────────────────────────────────────────────────────┘ │ │ … │ └──────────────────────────────────────────────────────────────────┘ ``` - **The biggest offender.** Five always-visible buttons per row (Edit, Copy link, Open, Close/Reopen, Delete) wrap onto multiple lines on narrow widths. Even on desktop they read as a noisy strip. - Each row has *border + shadow + bg + padding + status pill + mode pill + raw slug + meta line + button strip*. Nine visual objects per row. - Inline `style="…"` everywhere (`+page.svelte:108-156`). Should be classes in `feedback.css` so dark-mode and rhythm tokens stay consistent. - The raw `/f/` line is shown but has no purpose for the admin — they use Copy link or Open. It's just text noise. - Header repeats the page title in the body (`

Feedback forms

`) and then the section heading (`

Your forms (N)

`). The H2 mostly duplicates the H1, just with a count. ### 1.4 `/admin/feedback/new` ``` ┌──────────────────────────────────────────────────────────────────┐ │ [← All forms] │ │ Create a new form │ │ Set up a feedback form, a live chat session, or both. You'll… │ │ │ │ Title [______________________________] │ │ Description [______________________________] │ │ ☑ Enable live chat │ │ Questions (JSON, optional) [ + Insert sample ] │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ { … 14-row JSON textarea, monospace … } │ │ │ └──────────────────────────────────────────────────────────┘ │ │ Leave empty for chat-only feedback. You can edit questions… │ │ │ │ [ ✓ Create form ] │ └──────────────────────────────────────────────────────────────────┘ ``` - The JSON textarea is a *power-user escape hatch* dropped into the same surface as a beginner-friendly title/description form. It dominates the page (14 rows, monospace font), even though the visual builder on the detail page is the recommended path. - "Insert sample" sits *to the right* of the label, inside the field's header — visually clever but also noise. - The chat checkbox is a single inline item floating in its own block. It looks afterthought-y. (It also reuses `.fb-option-row` styling, which is meant for question options.) - Helper text under the JSON is good, but it's the *third* descriptive paragraph on the page (`

Set up a feedback form…

`, `placeholder="…"`, `
`). ### 1.5 `/admin/feedback/[id]` (detail) ``` ┌────────────────────────────────────────────────────────────────────────┐ │ [← All forms] │ │ My session feedback │ │ Some optional description text shown if present. │ │ [open] /f/long-slug [Copy link] [Preview] [Close] [CSV] [JSON] [Delete] ← 8 chips/buttons in one row │ │ │ ┌── Share ─────────────────────────────────────────────────────────┐ │ │ │ Memorable short link — resolves to /f/long-slug. │ │ │ │ https://msbls.de/vote [ Copy ] [ Open ↗ ] │ │ │ │ ▸ Replace with a different short link │ │ │ └──────────────────────────────────────────────────────────────────┘ │ │ │ │ [ Chat (3) ] [ Results (12) ] [ Responses (12) ] [ Edit ] │ │ …active tab body… │ └────────────────────────────────────────────────────────────────────────┘ ``` - **Header is the densest surface in the app.** Eight clickable controls in a flex-wrap row: status pill, raw slug, Copy link, Preview, Close/Reopen, CSV, JSON, Delete. On a 1024px viewport this wraps onto two lines. - "Copy link / Preview" in the header *and* a separate Share section *and* the slug shown raw — three different ways to access the same URL. - CSV / JSON download buttons sit between Close and Delete — they're not in the same gravitational tier (export is benign, Delete is nuclear), but they look identical (both `fb-btn--ghost fb-btn--sm`). - The Share section (recently added) was bolted onto the layout rather than designed into it. It sits between header and tabs, breaking the "header → tab → tab body" mental model. - The Edit tab's form has its own internal mini-toolbar (Visual / JSON toggle), which is correct but visually clashes with the parent tab strip one screen up. - Polling `setInterval(refresh, 5000)` is right; no UX change needed there. ### 1.6 `/f/[slug]` (participant) ``` ┌──────────────────────────────────────────┐ │ Title │ │ Optional description │ │ [⚠ closed banner if closed] │ │ │ │ Dein Name (optional): [Anonym______] ← inline label + full-width input collide │ │ │ ┌── Live-Feedback ──────────────────┐ │ │ │ scroll list of posts │ │ │ │ [textarea] │ │ │ │ [ Senden ] │ │ │ └───────────────────────────────────┘ │ │ │ │ ┌── Fragebogen ─────────────────────┐ │ │ │ q1 [...] │ │ │ │ q2 [...] │ │ │ │ [ Absenden ] │ │ │ └───────────────────────────────────┘ │ │ │ │ fdbck.msbls.de │ └──────────────────────────────────────────┘ ``` - Already simple. Needs *more* breathing room between Chat and Form sections — they read as two equally-weighted siblings, but in practice one of them is what the user came for. - The `Dein Name (optional):` label sits inline left of a full-width input (`fb-name-row`), while every other label in the participant form is on its own line above the input. The inline-label pattern is the only one of its kind on this page — kill it. - Footer `fdbck.msbls.de` is fine as a quiet trust signal. - Closed banner uses `--color-warning` palette which reads as alert; for a closed-form *neutral* state, a quieter visual is better. - Locale mix: admin pages use English (`Sign in`, `Edit`, `Delete`), but the participant page is German (`Senden`, `Absenden`, `Fragebogen`). Probably intentional (admins are us, participants are German-speaking) — flagging it so we don't accidentally homogenise during the redesign. **Not in scope to change.** --- ## 2. Principles The following six rules apply to every screen. They are the lens for §3. 1. **Whitespace over dividers.** Replace borders/shadows/dividers with generous vertical space. The rule of thumb: if a card is bordered on a page that's already on a coloured `--gradient-bg`, the card competes with the page background — drop the border, keep the radius+padding, lean on negative space. Spacing scale: `0.5rem` (within a row), `1.25rem` (between form fields), `2.5rem` (between sections), `4rem` (above page footer). 2. **One primary action per page.** Per screen, exactly one button uses the solid `.fb-btn` (filled green). Everything else is `--secondary` or `--ghost` or icon-only. Today most screens have 2–8 solid-or-near-solid actions competing for attention. 3. **Smarter controls — collapse rows.** Anywhere we currently render 3+ buttons in a strip, the redesign keeps **one** primary inline (or zero — "the row itself is the button") and tucks the rest behind a `⋯` menu (`
` summary or a small popover). Status flips become pill toggles instead of buttons. Destructive actions live *only* inside the `⋯` menu, never as a top-level visible button. 4. **Visual hierarchy via type weight, not borders.** Inter is loaded — use weight `700` for page-h1, `600` for section-h2, `500` for form labels, `400` for body. Tracking `-0.02em` on headings (already in the CSS for h1) extended to h2. This lets us drop a lot of decorative chrome. 5. **Optimistic UX.** Delete a row → row disappears immediately, with an undo toast for ~6s. Toggle status → pill switches instantly, network call in background. Errors revert the optimistic change and show a small inline message. We already have Svelte 5 `$state` everywhere, so this is plumbing, not architecture. 6. **No inline `style="..."` for layout.** Every inline style currently in the .svelte files moves to a class in `feedback.css`. Two reasons: dark mode lives in `@media (prefers-color-scheme: dark)` rules and inline styles bypass it; and inline styles defeat the spacing-scale rule (1). Optional principle 7 (suggest, but waiting for m): **list, not cards.** A naked padded list with hover-revealed actions beats card-grids for a small N (≈ <50 forms). Keeps the page feeling like *content*, not a dashboard. --- ## 3. Per-screen redesigns ### 3.1 `/` (landing) ``` ┌────────────────────────────────────────┐ │ │ │ │ │ fdbck │ ← 2.5rem, weight 700, tracked -0.03em │ feedback by link │ ← muted, 1rem, weight 400 │ │ │ │ │ [ Admin sign-in → ] │ ← single ghost button, sized fb-btn--lg │ │ └────────────────────────────────────────┘ ↑ massive vertical centering ``` - Centre the page vertically (max-width 480px, `min-height: 100vh`, `display: grid; place-items: center;`). - Drop the "This page is only reachable through a private link…" sentence — if a participant lands here they're not lost; the wordmark + sign-in is enough. - Wordmark grows to `2.5rem`, weight `700`, tracking `-0.03em`. Subtitle shrinks to `1rem` muted. - Single CTA, `fb-btn--lg fb-btn--ghost` with the existing `arrow-right` icon (already used on login submit). ### 3.2 `/login` ``` ┌────────────────────────────────────────┐ │ │ │ Sign in │ ← drop "Admin access only" │ │ │ Email │ │ [______________________] │ │ │ │ Password │ │ [______________________] │ │ │ │ ⓘ inline error appears here, muted │ │ │ │ [ Sign in → ] │ └────────────────────────────────────────┘ ``` - Drop the "Admin access only." subtitle (the URL says it). - Vertical-centre the form (same grid trick as landing). - Inline error: render as a small muted line *below* the submit button rather than an `fb-banner--error` block above it. Less alarming, no layout shift. - Keep the existing primary `fb-btn` with arrow-right icon. ### 3.3 `/admin/feedback` (list) — **the biggest change** ``` ┌────────────────────────────────────────────────────────────────────┐ │ Forms [ + New ] │ ← terse h1, primary CTA │ ───────── │ │ │ │ My session feedback ●open ⋯ │ ← row hover-reveals ⋯ │ Form + chat · 12 responses · 7 messages │ │ │ │ ┌── 2.5rem of pure whitespace ──┐ │ │ │ │ Sprint 4 retro ○closed ⋯ │ │ Form · 0 responses │ │ │ │ ┌── 2.5rem of pure whitespace ──┐ │ │ │ │ Quick chat ●open ⋯ │ │ Chat · 21 messages │ └────────────────────────────────────────────────────────────────────┘ ``` **Row anatomy:** - The whole row title is a link → goes to detail (today's "Edit" button becomes implicit). - Subtitle: mode + counts only. **Drop** the raw `/f/` line, the `created DATE` ("created" word), and the mode pill — fold mode into the subtitle text ("Form + chat · 12 responses"). - Right side: a status dot+label (●open / ○closed) that *is the toggle* — click flips it (optimistic). One control replaces today's pill-+-Close-button pair. - Trailing `⋯` menu (`
` summary or popover) opens: - Copy link - Open ↗ - Edit (explicit jump, even though row is also a link) - ────── - Delete (red) - Drop `border + shadow + bg` on the row container. Rely on row-padding + `2.5rem` gap between rows. Hover state: faint `--color-bg-tertiary` background, no border. - Mobile (`< 640px`): keep the `⋯` always visible; on desktop reveal on hover for a calmer default state. **Header:** - Drop the descriptive paragraph ("Collect feedback through forms…"). The user already knows what fdbck is. - H1 reads "Forms" (terser than "Feedback forms", since we're already at `/admin/feedback`). - Drop the H2 "Your forms (N)" — the count moves into the H1 area as a small muted number, or drops entirely (the rows themselves are the count). - Keep one solid `+ New` button on the right. **Empty state:** generous whitespace + "No forms yet — create your first one." + the same `+ New` button as primary CTA. **Optimistic delete:** after confirming, row disappears immediately; toast at the bottom: "Form deleted. [Undo]" for 6s. ### 3.4 `/admin/feedback/new` ``` ┌────────────────────────────────────────────────────────────────────┐ │ ← Forms │ ← terse back-link, no chip │ │ │ New form │ │ │ │ Title │ │ [______________________________________________] │ │ │ │ Description (optional) │ │ [______________________________________________] │ │ │ │ ☐ Enable live chat │ │ │ │ ▸ Add questions now (advanced) │ ← collapsed by default │ │ │ [ ✓ Create form ] │ └────────────────────────────────────────────────────────────────────┘ ``` - Title + Description + chat checkbox are the *common path*. They occupy the full page width without any JSON noise. - The JSON textarea moves behind a `
` disclosure labelled "Add questions now (advanced)". Inside the disclosure we keep: - the `Insert sample` button (now sits *inside* the disclosure, where it belongs) - the textarea - the helper paragraph - Rationale: the visual builder on the detail page is the canonical path for adding questions; offering JSON paste at *creation* is power-user speed-up, not the default. By collapsing it, the new-form page reads like 4 inputs and a button. - Replace the back-link button with a plain `← Forms` text-link in muted weight 500, no chip styling. - Drop the page subtitle ("Set up a feedback form…"). The label "New form" + the form below carry the meaning. - Move the chat toggle from a faux-`fb-option-row` checkbox to a proper `.fb-toggle` with a clean label-on-the-left layout (new class — small CSS addition). ### 3.5 `/admin/feedback/[id]` (detail) — **the second-biggest change** ``` ┌────────────────────────────────────────────────────────────────────────┐ │ ← Forms │ │ │ │ My session feedback ●open ⋯ │ ← title + clickable status pill + ⋯ │ Some optional description shown if present. │ │ │ │ msbls.de/vote [ Copy ] [ Open ↗ ] [ Replace ] │ ← share-link strip, INLINE in header │ │ │ ───────────────────────────────────────────────────────────────── │ ← whitespace, no border │ │ │ [ Chat (3) ] [ Results (12) ] [ Responses (12) ] [ Edit ] │ │ │ │ …active tab body, with 2.5rem top-padding… │ └────────────────────────────────────────────────────────────────────────┘ ``` **Header collapse — 8 controls → 3 visible:** | today's control | redesign | | ------------------------------------ | ---------------------- | | status pill (display only) | status pill, clickable to toggle (optimistic) | | `/f/slug` raw text | dropped (the share strip below has the link) | | Copy link button | inside share strip | | Preview button | inside share strip ("Open ↗") | | Close / Reopen button | merged into status pill | | CSV button | inside `⋯` menu | | JSON button | inside `⋯` menu | | Delete button | inside `⋯` menu (red, last) | `⋯` menu contents (in order, with separators): 1. Copy link (when the share strip is hidden on mobile) 2. Export CSV 3. Export JSON 4. ────── 5. Delete (red) **Share section disappears as a separate block.** The short URL is shown inline in the header as one strip: - If `inst.short_url` exists: show it + Copy + Open ↗ + Replace (Replace opens a small inline form *under* the strip — same `
` pattern as today, but compact). - If it doesn't: show "No short link · [ Create one ]" → click reveals the slug input + create button. This kills today's awkward "section between header and tabs" structure and folds share into the place where the user expects "where do I share this". **Tabs unchanged in count and labels.** They stay: Chat / Results / Responses / Edit. Selection style stays the segmented-pill (`fb-tabs` / `fb-tab--active`). Whitespace between tab strip and tab body grows from `1.25rem` to `2.5rem` so the tab body breathes. **Edit tab inline polish:** - Drop the duplicate `

Edit · v3

` heading inside the tab — the active tab pill already says "Edit". Save the version pill for a quiet spot next to the Save button. - The `Visual` / `JSON` toggle becomes a small segmented control in the same shape as the tab pills (consistency). - Save button is the page's primary action *while* this tab is active. No other solid button on the page in that state. **Banner about responses-already-received** (`{submissions.length} responses already received…`) becomes a single muted line above the form, not a banner box. ### 3.6 `/f/[slug]` (participant) ``` ┌──────────────────────────────────────────┐ │ │ │ Title │ ← h1, room above │ Optional description │ │ │ │ ⓘ closed (only if closed, neutral) │ ← muted not yellow when closed │ │ │ ───────────────────────────────────── │ ← 3rem gap │ │ │ Live-Feedback │ │ ┌── chat list ──────────────────────┐ │ │ │ … │ │ │ └───────────────────────────────────┘ │ │ [ textarea (full width) ] │ │ [Senden]│ │ │ │ ───────────────────────────────────── │ ← 3rem gap (same as above) │ │ │ Fragebogen │ │ q1 │ │ q2 │ │ │ │ [ Absenden ] │ │ │ │ │ │ fdbck.msbls.de │ └──────────────────────────────────────────┘ ``` - **Drop the inline-label name row.** Replace with a normal labelled field matching the rest of the form: ``` Dein Name (optional) [Anonym__________________________] ``` Or move it *below* the chat/form sections as a footer-style "Du schreibst als: Anonym ✎" with click-to-edit. (Suggested — see open question #5.) For v1 keep it at top but normalise its layout. - Increase section gap from `1.75rem` to `3rem` between the chat block and the form block, so they read as two distinct activities. - Closed banner: switch from amber `--color-warning` palette to a quiet neutral muted style (1px top border + small italic line) — closed is a state, not an alert. - Drop the `border-color: var(--color-primary)` on `.fb-chat__post--mine` in favour of a subtle left-border-only accent (3px, `--color-primary`). The current full pill-with-coloured-border treatment is loud. - Footer `fdbck.msbls.de` → wrap as a permalink to `/`, very muted. --- ## 4. Implementation plan The redesign is *only* CSS + small `.svelte` markup edits. No new components, no new dependencies, no schema changes. Suggested commit order (each commit is independently mergeable and testable): ### Commit 1 — CSS foundation: spacing scale + typography rhythm - `src/lib/styles/feedback.css`: - Add a spacing scale as CSS custom properties (`--space-1` through `--space-7`, or named `--gap-row / --gap-field / --gap-section / --gap-foot`). - Bump `.fb-section` margin-bottom from `1.75rem` → `--gap-section` (≈ `2.5rem`). - `.fb-header h1` weight stays 700; `.fb-section h2` weight bumps from `600` → `600` (no change) but tracking `-0.01em` already there. - Add `.fb-page-narrow` (max-width `480px`) and `.fb-page-center` (vertical grid centre) modifiers for landing/login. - Add `.fb-toggle` for label-left+switch-right boolean fields. - Add `.fb-row` for spacious list rows (no border, no shadow, hover bg) plus `.fb-row__title`, `.fb-row__meta`, `.fb-row__actions`. - Add `.fb-status-pill` (clickable open/closed pill). - Add `.fb-menu` (the `⋯` dropdown — implemented as `
` + `` + a positioned panel). - Add `.fb-share-strip` (the inline header share row in 3.5). - No structural Svelte changes in this commit — verify in the browser that the existing pages still render fine since added classes are unused additions. ### Commit 2 — landing + login (cheap wins) - `src/routes/+page.svelte`: drop the second paragraph; wrap shell in `fb-page-center fb-page-narrow`; bump wordmark size. - `src/routes/login/+page.svelte`: drop the "Admin access only." subtitle; wrap in `fb-page-center fb-page-narrow`; move error from `fb-banner--error` block to a small inline muted line. - Verify both routes in the browser at 375px / 1024px / 1440px. ### Commit 3 — `/admin/feedback/new` - `src/routes/admin/feedback/new/+page.svelte`: - Drop the page subtitle. - Replace the chat-checkbox block with `.fb-toggle`. - Wrap "Questions (JSON, optional)" + sample button + textarea + helper inside a `
` with summary "Add questions now (advanced)". - Remove inline `style="..."` on the back-link block; replace with a `.fb-back-link` class (text-only, muted). ### Commit 4 — `/admin/feedback` (list) — biggest change, isolated commit - `src/routes/admin/feedback/+page.svelte`: - Drop the H1 paragraph. - Drop the H2 "Your forms (N)". - Replace each row (today's bordered card with 5 buttons) with the `.fb-row` pattern from commit 1: title is a link to detail, subtitle merges mode + counts, right side is `.fb-status-pill` (toggle) + `.fb-menu` trigger. - Move all inline `style="..."` to classes (no new feature). - Add the `⋯` menu component inline using `
` — no JS framework needed; click-outside-to-close via a small `addEventListener` in `onMount`. - Add optimistic delete with undo toast (small `