Changeset 4c5169f in Klonkt


Ignore:
Timestamp:
06/14/2026 03:37:12 PM (3 months ago)
Author:
roboburr <roboburr@…>
Branches:
main
Children:
337ec5a
Parents:
6e0249f
Message:

hub: overview is a headerless landing with hero image

The hub overview now starts directly with the hero — no masthead/profile-header/
view-switcher/bottom-tab (those belong to a PrutFolio and appear on /user/
pages). Hero supports a background image (setting hub_hero_image, configurable
in Admin) with a dark overlay; otherwise gradient. Roster avatars use each
artist's accent color.

  • shell.ejs: chrome gated on bodyClass 'on-hub'.
  • hub.js: artists.accent + hub.heroImage from settings.
  • admin-settings: hub_hero_image field.

Co-Authored-By: Claude <noreply@…>

Location:
src
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • src/routes/admin-settings.js

    r6e0249f r4c5169f  
    2626    hubTagline: getSetting('hub_tagline') || '',
    2727    hubIntro: getSetting('hub_intro') || '',
     28    hubHeroImage: getSetting('hub_hero_image') || '',
    2829    success: req.query.success || null,
    2930  });
     
    4344    setSetting('hub_intro', (req.body.hub_intro || '').toString().slice(0, 400).trim());
    4445  }
     46  if (typeof req.body.hub_hero_image !== 'undefined') {
     47    setSetting('hub_hero_image', (req.body.hub_hero_image || '').toString().slice(0, 300).trim());
     48  }
    4549  res.redirect('/admin/settings?success=' + encodeURIComponent('Opgeslagen'));
    4650});
  • src/routes/hub.js

    r6e0249f r4c5169f  
    3737  // ALLE PrutFolio's (elke user, incl. de admin z'n eigen site) voor de roster.
    3838  const artists = db.prepare(`
    39     SELECT s.slug, s.title, s.tagline, s.profile_photo,
     39    SELECT s.slug, s.title, s.tagline, s.profile_photo, s.accent,
    4040           (SELECT COUNT(*) FROM posts WHERE site_id = s.id AND status = 'published') AS post_count
    4141    FROM sites s
     
    4949    tagline: getSetting('hub_tagline') || '',
    5050    intro: getSetting('hub_intro') || '',
     51    heroImage: getSetting('hub_hero_image') || '',
    5152  };
    5253
  • src/views/pages/admin-settings.ejs

    r6e0249f r4c5169f  
    5151        <textarea name="hub_intro" maxlength="400" rows="3" placeholder="Korte introtekst onder de titel."><%= hubIntro %></textarea>
    5252      </label>
     53      <label class="set-field">
     54        <span>Hero-afbeelding (URL) <small style="font-weight:400;color:var(--ink-muted)">— optioneel; achtergrond van de hero</small></span>
     55        <input type="text" name="hub_hero_image" maxlength="300" value="<%= hubHeroImage %>" placeholder="/media/... of https://...">
     56      </label>
    5357      <button type="submit" class="btn btn-primary">Opslaan</button>
    5458    </form>
  • src/views/pages/hub-home.ejs

    r6e0249f r4c5169f  
    11<div class="hub">
    22
    3   <%# ── HERO (label / bedrijf) ─────────────────────────────────────── %>
    4   <header class="hub-hero">
     3  <%# ── HERO (label) — de pagina begint hier, geen header erboven ────── %>
     4  <header class="hub-hero<%= hub.heroImage ? ' has-img' : '' %>"<% if (hub.heroImage) { %> style="--hero-img:url('<%= hub.heroImage %>')"<% } %>>
    55    <div class="hub-hero-inner container">
    66      <h1 class="hub-hero-title"><%= hub.title %></h1>
     
    2323        <% artists.forEach(function(a){ %>
    2424          <a class="roster-card" href="/user/<%= a.slug %>">
    25             <span class="roster-avatar"<% if (a.profile_photo) { %> style="background-image:url('<%= a.profile_photo %>')"<% } %>>
     25            <span class="roster-avatar"<% if (a.profile_photo) { %> style="background-image:url('<%= a.profile_photo %>')"<% } else { %> style="background:<%= a.accent || 'var(--accent)' %>;color:#fff"<% } %>>
    2626              <% if (!a.profile_photo) { %><%= (a.title || a.slug).charAt(0).toUpperCase() %><% } %>
    2727            </span>
     
    6565/* ── Hero ─────────────────────────────────────────────────────────── */
    6666.hub-hero {
     67  display: flex; align-items: center; justify-content: center;
     68  min-height: clamp(340px, 58vh, 560px);
     69  text-align: center;
    6770  background:
    68     radial-gradient(120% 140% at 50% -20%, color-mix(in srgb, var(--accent) 22%, var(--paper)) 0%, var(--paper) 60%);
     71    radial-gradient(130% 130% at 50% -10%, color-mix(in srgb, var(--accent) 26%, var(--paper)) 0%, var(--paper) 62%);
    6972  border-bottom: 1px solid var(--rule);
    70   text-align: center;
    7173}
    72 .hub-hero-inner { padding: 4rem 1rem 3.25rem; }
     74.hub-hero.has-img {
     75  background:
     76    linear-gradient(180deg, rgba(0,0,0,.45) 0%, rgba(0,0,0,.55) 100%),
     77    var(--hero-img) center / cover no-repeat;
     78  border-bottom: 0;
     79  color: #fff;
     80}
     81.hub-hero-inner { padding: 3.5rem 1rem; }
    7382.hub-hero-title {
    7483  font-family: var(--font-display, serif);
    75   font-size: clamp(2.4rem, 6vw, 3.6rem);
    76   line-height: 1.05; margin: 0;
    77   letter-spacing: -0.01em;
     84  font-size: clamp(2.6rem, 7vw, 4.4rem);
     85  line-height: 1.02; margin: 0; letter-spacing: -0.015em;
    7886}
    7987.hub-hero-tag {
    80   margin: 0.6rem 0 0; font-size: 1.05rem; font-weight: 600;
    81   color: var(--accent); text-transform: uppercase; letter-spacing: 0.08em;
     88  margin: 0.7rem 0 0; font-size: 1.05rem; font-weight: 600;
     89  text-transform: uppercase; letter-spacing: 0.1em;
     90  color: var(--accent);
    8291}
     92.hub-hero.has-img .hub-hero-tag { color: #fff; opacity: 0.92; }
    8393.hub-hero-desc {
    84   margin: 1rem auto 0; max-width: 46ch;
    85   color: var(--ink-muted); font-size: 1.05rem; line-height: 1.5;
     94  margin: 1.1rem auto 0; max-width: 48ch;
     95  font-size: 1.1rem; line-height: 1.55; color: var(--ink-muted);
    8696}
     97.hub-hero.has-img .hub-hero-desc { color: rgba(255,255,255,.9); }
    8798
    88 .hub-body { padding-bottom: 4rem; }
     99.hub-body { padding-bottom: 4.5rem; }
    89100.hub-sec { margin-top: 3rem; }
    90101.hub-sec-title {
     
    103114.roster-card {
    104115  display: flex; flex-direction: column; align-items: center; text-align: center;
    105   gap: 0.4rem; padding: 1.5rem 1rem 1.25rem;
    106   border: 1px solid var(--rule); border-radius: 14px;
     116  gap: 0.4rem; padding: 1.6rem 1rem 1.3rem;
     117  border: 1px solid var(--rule); border-radius: 16px;
    107118  background: var(--paper-2); color: var(--ink); text-decoration: none;
    108119  transition: border-color 140ms, transform 140ms, box-shadow 140ms;
     
    110121.roster-card:hover {
    111122  border-color: var(--accent); transform: translateY(-3px);
    112   box-shadow: 0 8px 24px -12px color-mix(in srgb, var(--accent) 50%, transparent);
     123  box-shadow: 0 10px 28px -14px color-mix(in srgb, var(--accent) 55%, transparent);
    113124}
    114125.roster-avatar {
    115   width: 84px; height: 84px; border-radius: 50%; margin-bottom: 0.4rem;
    116   background-size: cover; background-position: center; background-color: var(--paper);
     126  width: 88px; height: 88px; border-radius: 50%; margin-bottom: 0.45rem;
     127  background-size: cover; background-position: center;
    117128  display: inline-flex; align-items: center; justify-content: center;
    118   font-family: var(--font-display, serif); font-size: 2rem; font-weight: 600; color: var(--accent);
    119   border: 2px solid color-mix(in srgb, var(--accent) 30%, var(--rule));
     129  font-family: var(--font-display, serif); font-size: 2.1rem; font-weight: 700; color: #fff;
     130  box-shadow: inset 0 0 0 2px rgba(255,255,255,.18);
    120131}
    121 .roster-name { font-weight: 700; font-size: 1.08rem; }
     132.roster-name { font-weight: 700; font-size: 1.1rem; }
    122133.roster-tag { color: var(--ink-muted); font-size: 0.82rem; }
    123134.roster-count {
    124   margin-top: 0.3rem; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em;
     135  margin-top: 0.35rem; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em;
    125136  color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent);
    126137  padding: 0.15rem 0.6rem; border-radius: 99px;
     
    151162
    152163@media (max-width: 560px) {
    153   .hub-hero-inner { padding: 2.75rem 1rem 2.25rem; }
     164  .hub-hero-inner { padding: 2.5rem 1rem; }
    154165  .feed-cover { width: 52px; height: 52px; }
    155166  .feed-arrow { display: none; }
  • src/views/shell.ejs

    r6e0249f r4c5169f  
    239239<body class="<%= bodyClass || 'on-home' %> has-bottom-tab" data-feed-view="<%= _e(safeSite.feed_view_default || 'timeline') %>" data-grid-cols="3">
    240240
    241 <%- include('partials/topnav') %>
    242 
    243 <%- include('partials/profile-header') %>
     241<%# Hub-overview is een standalone landing: geen masthead/profile-header/
     242    view-switcher — die horen bij een PrutFolio (en verschijnen dus op /user/-
     243    pagina's). De landing begint meteen met de hero. %>
     244<% var isHubLanding = (typeof bodyClass === 'string' && bodyClass.indexOf('on-hub') >= 0); %>
     245
     246<% if (!isHubLanding) { %>
     247  <%- include('partials/topnav') %>
     248  <%- include('partials/profile-header') %>
     249<% } %>
    244250
    245251<%# View switcher — visible on every non-admin page. On feed pages
     
    247253    on non-feed pages (post, account, search, auth) it navigates to /
    248254    in the chosen view. Click logic is in shell.ejs at the bottom. %>
    249 <% if (!isAdminPage) { %>
     255<% if (!isAdminPage && !isHubLanding) { %>
    250256  <%- include('partials/view-switcher') %>
    251257<% } %>
     
    262268<%- include('partials/footer') %>
    263269
    264 <!-- Mobile bottom-tab navigation (auto-hidden ≥768px) -->
     270<!-- Mobile bottom-tab navigation (auto-hidden ≥768px) — niet op de hub-landing -->
     271<% if (!isHubLanding) { %>
    265272<%- include('partials/bottom-tab') %>
     273<% } %>
    266274
    267275<!-- Mobile profile sheet (auto-hidden ≥768px; only rendered when logged in) -->
Note: See TracChangeset for help on using the changeset viewer.