From fa49f80d657ec0bb0fb4526bc4e3d7628920e45c Mon Sep 17 00:00:00 2001 From: tleininger Date: Wed, 23 Sep 2026 20:28:15 +0200 Subject: [PATCH] Give the site a warm design system with card-based content lists --- .../Components/Layout/MainLayout.razor.css | 70 ++--- .../Features/Embeds/EventsEmbed.razor | 2 +- .../Features/Embeds/EventsEmbed.razor.css | 34 +- .../Features/Embeds/FaqsEmbed.razor | 2 +- .../Features/Embeds/FaqsEmbed.razor.css | 19 +- .../Features/Embeds/PostsEmbed.razor | 2 +- .../Features/Embeds/PostsEmbed.razor.css | 25 +- .../Features/Events/EventList.razor.css | 44 +-- .../Features/Faqs/FaqList.razor.css | 22 +- .../Features/Posts/PostList.razor.css | 22 +- Elternbeirat.Web/wwwroot/app.css | 296 +++++++++++++++++- 11 files changed, 338 insertions(+), 200 deletions(-) diff --git a/Elternbeirat.Web/Components/Layout/MainLayout.razor.css b/Elternbeirat.Web/Components/Layout/MainLayout.razor.css index be871ed..4096c06 100644 --- a/Elternbeirat.Web/Components/Layout/MainLayout.razor.css +++ b/Elternbeirat.Web/Components/Layout/MainLayout.razor.css @@ -25,27 +25,30 @@ z-index keeps it above the content; the opaque background stops content showing through as it scrolls underneath. */ .site-header { - border-bottom: 1px solid #e0e0e0; - background: #fff; + /* Brand-blue bar with a warm accent underline -- the strongest, most visible + cue that carries the friendly tone across every page. */ + background: var(--color-brand); + border-bottom: 3px solid var(--color-accent); + box-shadow: var(--shadow); position: sticky; top: 0; z-index: 20; } .header-inner { - max-width: 60rem; + max-width: var(--content-width); margin: 0 auto; - padding: 1rem 1.25rem; + padding: var(--space-3) 1.25rem; display: flex; flex-wrap: wrap; align-items: baseline; - gap: 0.5rem 1.5rem; + gap: var(--space-2) var(--space-4); } .brand { font-size: 1.35rem; font-weight: 700; - color: #1f3a5f; + color: #fff; text-decoration: none; white-space: nowrap; } @@ -65,22 +68,9 @@ display: none; } -.main-nav a { - color: #333; - text-decoration: none; - padding: 0.2rem 0; - border-bottom: 2px solid transparent; -} - -.main-nav a:hover { - color: #1f3a5f; -} - -/* NavLink adds .active to the link of the current route. */ -.main-nav a.active { - color: #1f3a5f; - border-bottom-color: #1f3a5f; -} +/* The link colours/states for .main-nav a live in the GLOBAL app.css, not here. + Reason: NavLink renders plain elements that carry no scoped-CSS attribute + (b-xxxxx), so a scoped rule ".main-nav a" would not match them. See app.css. */ /* Content: at least tall enough that the footer sits at the bottom of the viewport on short pages. 100dvh - 4rem leaves room for the header (which sits above in @@ -90,41 +80,41 @@ min-height: calc(100dvh - 4rem); box-sizing: border-box; width: 100%; - max-width: 60rem; + max-width: var(--content-width); margin: 0 auto; - padding: 1.5rem 1.25rem 3rem; + padding: var(--space-4) 1.25rem var(--space-6); } /* Footer */ .site-footer { - border-top: 1px solid #e0e0e0; - background: #fafafa; - color: #555; + border-top: 1px solid var(--color-border); + background: var(--color-surface); + color: var(--color-text-soft); font-size: 0.9rem; } .footer-inner { - max-width: 60rem; + max-width: var(--content-width); margin: 0 auto; - padding: 1rem 1.25rem; + padding: var(--space-3) 1.25rem; display: flex; flex-wrap: wrap; justify-content: space-between; - gap: 0.5rem 1.5rem; + gap: var(--space-2) var(--space-4); } .footer-nav { display: flex; - gap: 1.25rem; + gap: var(--space-4); } .footer-nav a { - color: #555; + color: var(--color-text-soft); text-decoration: none; } .footer-nav a:hover { - color: #1f3a5f; + color: var(--color-brand); text-decoration: underline; } @@ -155,7 +145,7 @@ display: block; width: 1.5rem; height: 2px; - background: #1f3a5f; + background: #fff; /* on the blue header bar */ content: ""; } @@ -191,14 +181,6 @@ display: block; } - .main-nav a { - padding: 0.6rem 0; - border-bottom: 1px solid #eee; - } - - /* The active link keeps its colour but not the desktop underline, which would - clash with the row separators. */ - .main-nav a.active { - border-bottom-color: #eee; - } + /* Mobile per-link spacing/separators for .main-nav a are in the global app.css + too, for the same scoped-attribute reason as above. */ } diff --git a/Elternbeirat.Web/Features/Embeds/EventsEmbed.razor b/Elternbeirat.Web/Features/Embeds/EventsEmbed.razor index cae3aa3..080ed5e 100644 --- a/Elternbeirat.Web/Features/Embeds/EventsEmbed.razor +++ b/Elternbeirat.Web/Features/Embeds/EventsEmbed.razor @@ -20,6 +20,6 @@ } -

Alle Termine →

+

Alle Termine

} diff --git a/Elternbeirat.Web/Features/Embeds/EventsEmbed.razor.css b/Elternbeirat.Web/Features/Embeds/EventsEmbed.razor.css index 9c51138..afcd19b 100644 --- a/Elternbeirat.Web/Features/Embeds/EventsEmbed.razor.css +++ b/Elternbeirat.Web/Features/Embeds/EventsEmbed.razor.css @@ -1,33 +1,5 @@ +/* Card styles for .event-list are shared with the full events page and live in the + global app.css. Only the embed's spacing on the home page is specific here. */ .embed-events { - margin-top: 2rem; -} - -.event-list { - list-style: none; - padding: 0; -} - -.event-list li { - padding: 0.75rem 0; - border-bottom: 1px solid #eee; -} - -.event-list time { - display: block; - color: #777; - font-size: 0.85rem; -} - -.event-title { - font-size: 1.1rem; - font-weight: 600; -} - -.event-location::before { - content: " · "; - color: #777; -} - -.event-location { - color: #555; + margin-top: var(--space-5); } diff --git a/Elternbeirat.Web/Features/Embeds/FaqsEmbed.razor b/Elternbeirat.Web/Features/Embeds/FaqsEmbed.razor index 9460bdb..278e395 100644 --- a/Elternbeirat.Web/Features/Embeds/FaqsEmbed.razor +++ b/Elternbeirat.Web/Features/Embeds/FaqsEmbed.razor @@ -16,6 +16,6 @@ } -

Alle Fragen →

+

Alle Fragen

} diff --git a/Elternbeirat.Web/Features/Embeds/FaqsEmbed.razor.css b/Elternbeirat.Web/Features/Embeds/FaqsEmbed.razor.css index 1022fd7..4a084e8 100644 --- a/Elternbeirat.Web/Features/Embeds/FaqsEmbed.razor.css +++ b/Elternbeirat.Web/Features/Embeds/FaqsEmbed.razor.css @@ -1,18 +1,5 @@ +/* Card styles for .faq-item are shared with the full FAQ page and live in the + global app.css. Only the embed's spacing on the home page is specific here. */ .embed-faqs { - margin-top: 2rem; -} - -.faq-item { - border-bottom: 1px solid #eee; - padding: 0.5rem 0; -} - -.faq-item summary { - cursor: pointer; - font-weight: 600; -} - -.faq-answer { - padding: 0.5rem 0 0.25rem; - color: #333; + margin-top: var(--space-5); } diff --git a/Elternbeirat.Web/Features/Embeds/PostsEmbed.razor b/Elternbeirat.Web/Features/Embeds/PostsEmbed.razor index c4de3dc..a70df14 100644 --- a/Elternbeirat.Web/Features/Embeds/PostsEmbed.razor +++ b/Elternbeirat.Web/Features/Embeds/PostsEmbed.razor @@ -18,6 +18,6 @@ } -

Alle Beiträge →

+

Alle Beiträge

} diff --git a/Elternbeirat.Web/Features/Embeds/PostsEmbed.razor.css b/Elternbeirat.Web/Features/Embeds/PostsEmbed.razor.css index 9f4fa3b..357ec35 100644 --- a/Elternbeirat.Web/Features/Embeds/PostsEmbed.razor.css +++ b/Elternbeirat.Web/Features/Embeds/PostsEmbed.razor.css @@ -1,24 +1,5 @@ +/* Card styles for .post-list are shared with the full posts page and live in the + global app.css. Only the embed's spacing on the home page is specific here. */ .embed-posts { - margin-top: 2rem; -} - -.post-list { - list-style: none; - padding: 0; -} - -.post-list li { - padding: 0.75rem 0; - border-bottom: 1px solid #eee; -} - -.post-list a { - font-size: 1.1rem; - text-decoration: none; -} - -.post-list time { - display: block; - color: #777; - font-size: 0.85rem; + margin-top: var(--space-5); } diff --git a/Elternbeirat.Web/Features/Events/EventList.razor.css b/Elternbeirat.Web/Features/Events/EventList.razor.css index 1b33ecc..f95fa43 100644 --- a/Elternbeirat.Web/Features/Events/EventList.razor.css +++ b/Elternbeirat.Web/Features/Events/EventList.razor.css @@ -1,42 +1,2 @@ -.event-list { - list-style: none; - padding: 0; -} - -.event-list li { - padding: 0.75rem 0; - border-bottom: 1px solid #eee; -} - -.event-list time { - display: block; - color: #777; - font-size: 0.85rem; -} - -.event-title { - font-size: 1.1rem; - font-weight: 600; -} - -.event-location::before { - content: " · "; - color: #777; -} - -.event-location { - color: #555; -} - -.event-note { - margin: 0.25rem 0 0; - color: #444; -} - -.event-list-past { - color: #777; -} - -.event-list-past .event-title { - font-weight: 400; -} +/* The .event-list card styles (including past-event styling) are shared with the + home-page embed and live in the global app.css. Nothing page-specific here. */ diff --git a/Elternbeirat.Web/Features/Faqs/FaqList.razor.css b/Elternbeirat.Web/Features/Faqs/FaqList.razor.css index d8d3670..6bb2570 100644 --- a/Elternbeirat.Web/Features/Faqs/FaqList.razor.css +++ b/Elternbeirat.Web/Features/Faqs/FaqList.razor.css @@ -1,22 +1,10 @@ +/* The .faq-item card styles are shared with the home-page embed and live in the + global app.css. Only the topic grouping is specific to this full page (the + embed shows a flat list without topic headings). */ .faq-topic { - margin-bottom: 2rem; + margin-bottom: var(--space-5); } .faq-topic h2 { - margin-bottom: 0.5rem; -} - -.faq-item { - border-bottom: 1px solid #eee; - padding: 0.5rem 0; -} - -.faq-item summary { - cursor: pointer; - font-weight: 600; -} - -.faq-answer { - padding: 0.5rem 0 0.25rem; - color: #333; + margin-bottom: var(--space-3); } diff --git a/Elternbeirat.Web/Features/Posts/PostList.razor.css b/Elternbeirat.Web/Features/Posts/PostList.razor.css index ac756fa..88fbef8 100644 --- a/Elternbeirat.Web/Features/Posts/PostList.razor.css +++ b/Elternbeirat.Web/Features/Posts/PostList.razor.css @@ -1,20 +1,2 @@ -.post-list { - list-style: none; - padding: 0; -} - -.post-list li { - padding: 0.75rem 0; - border-bottom: 1px solid #eee; -} - -.post-list a { - font-size: 1.1rem; - text-decoration: none; -} - -.post-list time { - display: block; - color: #777; - font-size: 0.85rem; -} +/* The .post-list card styles are shared with the home-page embed and live in the + global app.css. This page needs no extra styling of its own. */ diff --git a/Elternbeirat.Web/wwwroot/app.css b/Elternbeirat.Web/wwwroot/app.css index e49babf..ac95a71 100644 --- a/Elternbeirat.Web/wwwroot/app.css +++ b/Elternbeirat.Web/wwwroot/app.css @@ -1,27 +1,313 @@ +/* Design tokens: one place for colours, spacing, radius, shadow and type. Every + component pulls from these variables (var(--...)), so the whole site shares one + look and a change here ripples everywhere. Warm-friendly palette: the brand blue + plus a warm accent. No external anything -- just CSS custom properties. */ +:root { + /* Brand blue and two derived shades (darker for hover, lighter for tints). */ + --color-brand: #1f3a5f; + --color-brand-dark: #16293f; + --color-brand-tint: #eef2f7; + + /* Warm accent for highlights (active nav, event dates, call-outs). */ + --color-accent: #e8a13a; + --color-accent-dark: #c9821c; + + /* Text and surfaces. */ + --color-text: #222; + --color-text-soft: #555; + --color-bg: #fff; /* cards / content surface */ + --color-page: #f4f1ea; /* soft warm page background behind the content */ + --color-surface: #fafafa; + --color-border: #e0e0e0; + + /* Spacing scale -- use these instead of ad-hoc rem values so gaps stay even. */ + --space-1: 0.25rem; + --space-2: 0.5rem; + --space-3: 1rem; + --space-4: 1.5rem; + --space-5: 2rem; + --space-6: 3rem; + + /* Rounded corners and a soft shadow for cards, matching the friendly tone. */ + --radius: 12px; + --radius-sm: 6px; + --shadow: 0 1px 3px rgba(31, 58, 95, 0.08), 0 4px 12px rgba(31, 58, 95, 0.06); + --shadow-hover: 0 2px 6px rgba(31, 58, 95, 0.12), 0 8px 20px rgba(31, 58, 95, 0.1); + + /* One transition speed for hovers/focus across the site. */ + --transition: 0.15s ease; + + /* Reading width, so long text lines never get uncomfortably wide. */ + --content-width: 60rem; +} + /* Base styles. Self-hosted system font stack — no external fonts, for privacy. */ html { font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; - line-height: 1.6; - color: #222; + line-height: 1.65; + color: var(--color-text); + /* Smooth-scroll for in-page anchors, with room so headings clear the sticky + header instead of hiding under it. */ + scroll-padding-top: 5rem; } body { margin: 0; + /* A soft tinted page background so white cards/content lift off it. */ + background: var(--color-page); + color: var(--color-text); } a { - color: #1f3a5f; + color: var(--color-brand); + text-underline-offset: 2px; + transition: color var(--transition); } -h1, h2, h3 { +a:hover { + color: var(--color-brand-dark); +} + +/* Headings: clear hierarchy and a touch more air above them than below, so + sections read as groups. */ +h1, h2, h3, h4 { line-height: 1.25; - color: #1f3a5f; + color: var(--color-brand); + margin: var(--space-5) 0 var(--space-3); +} + +h1 { + font-size: 2rem; + margin-top: 0; /* the first heading on a page needs no gap above */ +} + +h2 { + font-size: 1.5rem; +} + +h3 { + font-size: 1.2rem; +} + +/* Comfortable paragraph rhythm and a capped line length for readability. */ +p { + margin: 0 0 var(--space-3); + max-width: 42rem; } h1:focus { outline: none; } +/* Header navigation links. These live here (global), not in MainLayout.razor.css: + NavLink renders plain elements without a scoped-CSS attribute, so a scoped + ".main-nav a" rule would not match them -- the links would fall back to the + global link colour (dark blue) and become unreadable on the blue header bar. */ +.main-nav a { + color: rgba(255, 255, 255, 0.85); + text-decoration: none; + padding: var(--space-1) 0; + border-bottom: 2px solid transparent; + transition: color var(--transition), border-color var(--transition); +} + +.main-nav a:hover { + color: #fff; +} + +/* NavLink adds .active to the current route's link; a warm accent underline marks + where the visitor is on the blue bar. */ +.main-nav a.active { + color: #fff; + border-bottom-color: var(--color-accent); +} + +/* Mobile: stacked links inside the collapsed menu get row separators. Same global + placement, same NavLink reason as above. Scoped to the narrow layout via the + media query so it does not affect the desktop row. */ +@media (max-width: 40rem) { + .main-nav a { + padding: 0.6rem 0; + border-bottom: 1px solid rgba(255, 255, 255, 0.15); + } + + .main-nav a.active { + border-bottom-color: rgba(255, 255, 255, 0.15); + } +} + +/* --- Shared card styles for content lists -------------------------------------- + Posts, events and FAQs appear both on their own page (PostList/EventList/ + FaqList) AND embedded on the home page (the *Embed components), with the same + class names. These rules live here (global) so both places share one look + instead of duplicating the CSS per component. The card look: a white surface + lifted off the tinted page background with a soft shadow and a gentle hover. */ + +.post-list, +.event-list { + list-style: none; + padding: 0; + margin: 0; + display: grid; + gap: var(--space-3); +} + +.post-list li, +.event-list li { + background: var(--color-bg); + border-radius: var(--radius); + box-shadow: var(--shadow); + padding: var(--space-3) var(--space-4); + transition: box-shadow var(--transition), transform var(--transition); +} + +.post-list li:hover, +.event-list li:hover { + box-shadow: var(--shadow-hover); + transform: translateY(-2px); +} + +/* Posts */ +.post-list a { + font-size: 1.15rem; + font-weight: 600; + text-decoration: none; +} + +.post-list time { + display: block; + margin-top: var(--space-1); + color: var(--color-text-soft); + font-size: 0.85rem; +} + +/* Events: the date leads in the warm accent colour. */ +.event-list time { + display: block; + color: var(--color-accent-dark); + font-weight: 600; + font-size: 0.9rem; +} + +.event-title { + display: block; + margin-top: var(--space-1); + font-size: 1.15rem; + font-weight: 600; + color: var(--color-brand); +} + +.event-location::before { + content: " · "; + color: var(--color-text-soft); +} + +.event-location { + color: var(--color-text-soft); +} + +.event-note { + margin: var(--space-2) 0 0; + color: var(--color-text); +} + +/* Past events (only on the full events page): same cards, quieter and flat. */ +.event-list-past li { + box-shadow: none; + border: 1px solid var(--color-border); + background: var(--color-surface); +} + +.event-list-past li:hover { + transform: none; + box-shadow: none; +} + +.event-list-past time { + color: var(--color-text-soft); + font-weight: 400; +} + +.event-list-past .event-title { + font-weight: 400; + color: var(--color-text-soft); +} + +/* FAQ: each question is an expandable card with an accent +/– marker. */ +.faq-item { + background: var(--color-bg); + border-radius: var(--radius); + box-shadow: var(--shadow); + padding: var(--space-3) var(--space-4); + margin-bottom: var(--space-3); + transition: box-shadow var(--transition); +} + +.faq-item:hover { + box-shadow: var(--shadow-hover); +} + +.faq-item summary { + cursor: pointer; + font-weight: 600; + color: var(--color-brand); + list-style: none; + display: flex; + align-items: baseline; + gap: var(--space-2); +} + +.faq-item summary::-webkit-details-marker { + display: none; +} + +.faq-item summary::before { + content: "+"; + color: var(--color-accent-dark); + font-weight: 700; + font-size: 1.2rem; + line-height: 1; +} + +.faq-item[open] summary::before { + content: "–"; +} + +.faq-answer { + padding-top: var(--space-2); + color: var(--color-text); +} + +/* "Alle Beiträge/Termine/Fragen →" links below an embedded list on the home page. + Set apart from the cards with extra space above, and styled as a distinct call + to action in the accent colour rather than looking like body text. The arrow + nudges right on hover for a subtle bit of life. */ +.embed-more { + margin-top: var(--space-4); + max-width: none; /* override the global paragraph reading cap */ +} + +.embed-more a { + display: inline-flex; + align-items: center; + gap: var(--space-2); + font-weight: 600; + color: var(--color-accent-dark); + text-decoration: none; +} + +.embed-more a:hover { + color: var(--color-brand); +} + +.embed-more a span { + transition: transform var(--transition); +} + +.embed-more a:hover span { + transform: translateX(3px); +} + .valid.modified:not([type=checkbox]) { outline: 1px solid #26b050; }