Files
the-collective-hub/.github/instructions/public-site-theming.instructions.md
T
KungRaseri b192cd53ba docs(copilot): add Copilot instructions for The Collective Hub
- Add comprehensive project overview and core philosophy
- Document file structure reference for the codebase
- Create key files reference table for task-specific guidance
- Include multi-tenant guidelines and site resolution flow
2026-06-05 23:46:15 -07:00

5.7 KiB


description: 'Use when building or modifying the public-facing landing page for The Collective Hub. Covers SSR-only rendering, section structure, CSS custom properties for theming, theme presets, and dynamic branding from settings.' applyTo: 'src/routes/+page.svelte', 'src/routes/+layout.svelte', 'src/routes/layout.css', 'src/routes/+page.server.ts'

Public Site & Theming

Architecture

The public site is a single SSR-only landing page. There is no client-side routing beyond the initial page load, no SPA navigation, and no JavaScript-driven content switching.

Single page structure:
    Hero → About → Events → Social Links → Footer

Section Structure

The page is composed of clearly separated sections:

<!-- src/routes/+page.svelte (conceptual) -->
<script lang="ts">
    let { data } = $props();
    let { settings } = data;
</script>

<Hero {settings} />
<About {settings} />
<Events {settings} />
<SocialLinks {settings} />
<Footer {settings} />

Each section is a Svelte component in src/lib/components/ (for the public site) or defined locally in the route if it's too specific.

Data Loading

The homepage data is loaded in +page.server.ts:

// src/routes/+page.server.ts
import type { PageServerLoad } from './$types';

export const load: PageServerLoad = async ({ locals }) => {
    const settings = locals.siteSettings;

    // Optionally load events, nav links, etc.
    // const events = await loadEvents(locals.site.id);

    return {
        settings,
        // events,
    };
};

Theme System

The site uses CSS custom properties generated from the site's theme settings. These are applied to the HTML element and cascade through all components.

Theme Settings Shape

interface ThemeSettings {
    preset: 'dark' | 'light' | 'custom';
    accentColor: string;     // e.g., "#e63946"
    backgroundColor: string; // e.g., "#1a1a2e"
    textColor: string;       // e.g., "#eaeaea"
}

Applying Theme in Layout

In src/routes/+layout.svelte or the root layout, apply theme as inline styles:

<script lang="ts">
    let { data, children } = $props();
    let theme = $derived(data.settings?.theme ?? { preset: 'dark', accentColor: '#e63946', backgroundColor: '#1a1a2e', textColor: '#eaeaea' });
</script>

<div
    style="--accent: {theme.accentColor}; --bg: {theme.backgroundColor}; --text: {theme.textColor};"
    class="min-h-screen"
    style="background-color: var(--bg); color: var(--text);"
>
    {@render children()}
</div>

Theme Presets

Built-in presets provide sensible defaults:

Preset Background Text Accent Vibe
dark (default) #1a1a2e #eaeaea #e63946 Cinematic, theater-like
light #ffffff #1a1a2e #e63946 Clean, readable
custom Per-site config Per-site config Per-site config Fully customized

Dynamic Branding

Branding settings control the visual identity:

<script lang="ts">
    let { branding }: { branding: BrandingSettings } = $props();
    let logoSrc = $derived(branding.logoCdnKey ? cdnUrl(branding.logoCdnKey) : null);
</script>

{#if logoSrc}
    <img src={logoSrc} alt="{branding.siteName} logo" class="h-12 w-auto" />
{:else}
    <h1 class="text-2xl font-bold">{branding.siteName}</h1>
{/if}

Favicon

Set in src/app.html via the loaded settings:

<link rel="icon" href="{faviconUrl || '/favicon.png'}" />

Section Components

Hero Section

<script lang="ts">
    let { settings }: { settings: SiteSettingsData } = $props();
    let { homepage, branding } = settings;
</script>

<section class="py-20 text-center">
    <h1 class="text-4xl md:text-6xl font-bold" style="color: var(--accent)">
        {homepage.heroTitle || branding.siteName}
    </h1>
    <p class="mt-4 text-lg">{homepage.heroSubtitle || branding.tagline}</p>
    {#if homepage.primaryButtonLink}
        <a
            href={homepage.primaryButtonLink}
            class="mt-8 inline-block px-6 py-3 rounded-lg font-semibold"
            style="background-color: var(--accent); color: white;"
        >
            {homepage.primaryButtonText || 'Join Us'}
        </a>
    {/if}
</section>

About Section

Displays the homepage.aboutText content. Supports markdown rendering for basic formatting.

Events Section

Shows upcoming events if homepage.showNextEvent or homepage.showSchedule is enabled. Events are loaded server-side and passed through data.

Renders social platform links from the socialLinks table with platform-appropriate icons.

CSS Custom Properties Reference

/* Defined at runtime via inline styles on the root element */
:root {
    --accent: #e63946;       /* Primary accent color */
    --bg: #1a1a2e;           /* Page background */
    --text: #eaeaea;         /* Text color */
    --accent-hover: #ff6b6b; /* Optional: accent hover state */
}

Use these in components instead of hardcoding colors:

/* ✅ */
.button {
    background-color: var(--accent);
    color: white;
}

/* ❌ */
.button {
    background-color: #e63946;
    color: white;
}

Rendering Rules

  1. SSR-only: All content is rendered on the server. No client-side data fetching for page content.
  2. No JavaScript required: The public page should be fully functional without JS (links, text, images).
  3. Theme-first: Always use CSS custom properties for colors. Never hardcode theme colors.
  4. Responsive: The single layout must work on mobile and desktop.
  5. Accessible: Proper heading hierarchy, alt text on images, sufficient color contrast.