SB Sommar – Requirements: Design and Content
Visual and editorial polish: hero redesign, link colors, modal styling, registration banner, locale overview page, index design.
Part of the requirements index. Section IDs (02-§N.M) are stable and cited from code; they do not encode the file path.
30. Hero Section Redesign
The homepage hero section is redesigned from a full-width image to a two-column
layout with a title, social links, and a camp countdown. The goal is a warmer,
more inviting first impression that immediately communicates the site’s purpose
and connects visitors to community channels.
30.1 Layout structure
- The hero section is a two-column layout: image area (approximately 2/3 width)
and a sidebar panel (approximately 1/3 width).
- On mobile (below 690px), the layout stacks vertically: title, then image,
then sidebar content.
30.2 Title
- A heading “Sommarläger i Sysslebäck” is displayed above the hero image,
left-aligned within the image column.
- The title uses
var(--color-terracotta) as its text color.
- The title uses the existing H1 size (40px) and weight (700) from the
design system.
30.3 Hero image
- The hero image has rounded corners (a new
--radius-lg token is added
to the design system for this purpose).
- The image uses
object-fit: cover and is responsive.
- The image occupies approximately 2/3 of the hero section width on
desktop.
- The sidebar contains two social link icons stacked vertically:
a Discord icon and a Facebook icon.
- The Discord icon links to the project Discord channel.
- The Facebook icon links to the camp’s Facebook group.
- Icons are displayed at a recognizable size (approximately 64px) and are
vertically centered within their area of the sidebar.
30.5 Countdown
- Below the social icons, a countdown displays the number of days remaining
until the next camp starts.
- The countdown target date is derived automatically from
camps.yaml:
the start_date of the nearest future camp (or the active camp if its
start date is still in the future).
- The countdown shows two lines: the number (large, prominent) and the
label “Dagar kvar” beneath it.
- The countdown is rendered at build time as a
data-target attribute.
Client-side JavaScript calculates and updates the number on page
load.
- If no future camp exists, the countdown is hidden.
- The countdown area has a subtle background (cream/sand oval or rounded
rectangle) to visually separate it from the sidebar.
30.7 Visual refinements
- The countdown background color is
#FAF7EF (a near-white cream), not
semi-transparent.
- The Discord icon uses the image
discord-ikon.webp.
- The sidebar is vertically centered alongside the hero image (not
top-aligned).
30.6 Implementation constraints
- All styling uses CSS custom properties from
docs/07-DESIGN.md §7.
No hardcoded colors, spacing, or typography.
- The countdown client-side script is minimal — no framework, no external
dependency.
- Social icon images are stored in
source/content/images/ and copied
to public/images/ at build time.
- The Facebook link and Discord link are provided at build time from
configuration, not hardcoded in templates.
31. Inline Camp Listing and Link Styling
The camp listing is moved from a standalone section into the intro section,
appearing directly below the first heading. Link styling is updated site-wide.
31.1 Camp listing placement
- The camp listing (upcoming and recent camps) is rendered inside the intro
section, immediately after the first
<h4> heading.
- The camp listing is no longer a separate page section and does not have its
own heading or navigation entry.
31.2 Camp status icons
- Upcoming camps (end date in the future) display a sun icon (☀️) before the
camp name.
- Past camps (end date in the past) display a green checkbox icon (✅) before
the camp name.
- Status detection remains client-side via
data-end attributes and
JavaScript, as defined in 02-§28.14.
31.3 Camp item content
- Each camp item shows the camp name, location, and formatted date
range.
- Camp information text is no longer rendered in the listing.
31.4 Content link styling
- All links inside
.content use var(--color-terracotta) as their text
color.
- Content links have no underline by default; underline appears on
hover.
31.5 Markdown heading support
- The markdown converter supports
#### (h4) headings in addition to
h1–h3.
31.6 Implementation constraints
- All styling must use CSS custom properties from the design system.
No hardcoded colors, spacing, or typography.
- No additional runtime JavaScript beyond the existing client-side date
detection.
47. Heading and Link Color Update
47.1 Site requirements
All heading elements (h1–h6) use terracotta (var(--color-terracotta))
as their text color.
Content links (.content a) are styled with terracotta color and a
permanent underline (text-decoration: underline), not only on
hover.
Navigation links, back-links, and other non-content links retain their
existing styles.
55. Submit modal design polish
The submit progress modal (used in add-activity and edit-activity forms) needs
visual polish to look clean and consistent with the rest of the site design.
55.1 Site requirements
- The modal heading must not show a browser focus outline when
programmatically focused. The heading uses
tabindex="-1" for
programmatic focus only and is not an interactive element.
- The modal box must use
--radius-lg (16 px) border-radius for a softer,
more modern appearance.
- The modal box must use
--space-lg top/bottom padding for more generous
internal spacing.
- The modal heading and progress steps must be center-aligned.
- The modal box must appear with a subtle entry animation (fade in + slide
up) lasting no more than 300 ms, consistent with the design constraint
in
07-§10.2.
64. Index Page Design Improvements
Visual polish for the main landing page to reduce monotony on a long single-page
scroll and better leverage existing design tokens.
64.1 Testimonial cards
- Each testimonial (name, photo, quote) must be wrapped in a white card with
box-shadow, border-radius, and padding matching the card component
spec (07-§6.19–22).
- The testimonial photo must be displayed as a circular thumbnail (~60 px)
beside the name, matching 07-§6.23.
- Testimonial cards must be constrained to
--container-narrow max-width.
- The card structure must be generated at build time from the existing
Markdown format (## Name + image + blockquote). Content files must not
need to change.
64.2 Alternating section backgrounds
- Every other content section on the index page must have a cream-light
background (
--color-cream-light) using a full-bleed pseudo-element,
creating edge-to-edge colour bands.
- The first section (section-first) is excluded from alternation.
- Alternating sections must not display the default border-top divider.
64.3 Decorative section headings
- Section headings use the existing terracotta colour; no additional
decorative line is rendered.
64.4 RFSB logo placement
- The RFSB logo in the first section must be displayed as a small inline
floated image (~70 px wide) beside the opening paragraph.
64.6 Consistent content image widths
- General content images (
.content-img) must be constrained to
max-width 500 px.
- Accommodation images (Stuga, Vandrarhem, Campingplats, Klarälvsbyn)
must be constrained to 250 px.
- The Servicehus image must match the hero width
(
--container-narrow).
64.7 Compact section spacing
- Content sections must use compact vertical spacing (padding-top and
margin-bottom ≤
--space-md).
- Section-alt padding must match regular section spacing.
- The site footer must use a full-bleed pseudo-element background
(sage/cream mix) with no gap below the last content section.
- Body must have no bottom padding.
- A scroll-to-top button must appear on mobile viewports (≤ 767 px)
after scrolling 300 px.
- The button must match the hamburger toggle in size (42 × 42 px),
colour, and border-radius.
- The button must be a child of
<nav class="page-nav">, centred
horizontally (position: absolute; left: 50%;
transform: translateX(-50%)).
- The button must smooth-scroll to the top on click.
94.1 Context
The homepage must signal, at a glance, that registration is open for each
upcoming camp. The “Hur anmäler jag oss?” section contains an inline bold
markdown link that first-time visitors easily miss, and nothing else on the
page communicates whether registration is currently open or when it closes.
Two changes address this: a banner below the hero that announces the open
registration window and links to the section, and a prominent CTA button
inside the section itself that replaces the inline markdown link.
94.2 User requirements
- A prospective family visiting the homepage during an open registration
period for a camp sees a banner directly below the hero image announcing
that registration for that camp is open, together with the last
registration date.
- Clicking the banner navigates to the
#anmalan section on the same
page.
- One banner is rendered per non-archived camp that has a registration
window, ordered by the camp’s
start_date ascending (closest camp
first).
- The “Hur anmäler jag oss?” section contains a visually prominent
“Anmäl er här” button that opens the external booking site in a new
tab. The specific URL lives in the build code, not in this
requirement.
- The “Anmäl er här” button sits on its own line directly under the
“Hur anmäler jag oss?” heading, as an inline-block element sized to
its own content.
- The button layout is identical on desktop and mobile — no float, no
breakpoint-dependent width change.
- The registration section contains no inline bold markdown link labelled
“Anmäl er här”; the CTA button is the single call-to-action.
94.3 Data requirements
- Each non-archived camp in
camps.yaml declares the fields
registration_opens and registration_closes, both as ISO dates
(YYYY-MM-DD, inclusive).
- The data validator rejects a non-archived camp that is missing either
field, has a non-ISO date, has
registration_opens > registration_closes,
or has registration_closes >= start_date.
- Archived camps may omit the registration fields; the validator does not
require them.
94.4 Banner visibility rules
- Each banner is rendered in the static HTML at build time with the
hidden attribute and the data attributes data-opens and
data-closes carrying the camp’s registration window.
- A client-side script removes
hidden only when the current
Europe/Stockholm date satisfies
data-opens <= today <= data-closes.
- Outside the registration window the banner remains hidden; it does not
briefly flash visible, and its container reserves no visible space when
empty.
- Banners are only rendered for non-archived camps.
- The CTA button is injected by the homepage renderer into the
anmalan
section, not authored in the markdown source, following the same
injection pattern as wrapTestimonialCards in
source/build/render-index.js.
- The button opens in a new browser tab with
target="_blank" and
rel="noopener noreferrer", consistent with other external links on
the site.
- The button reuses the existing
.btn-primary class from
source/assets/cs/style.css; no new colour, typography, or spacing
tokens are introduced.
94.6 Analytics
- Each banner carries
data-goatcounter-click="click-register-banner-<camp-id>", where
<camp-id> is the camp’s id from camps.yaml.
- The CTA button carries
data-goatcounter-click="click-register-section".
94.7 Constraints
- All user-facing text is in Swedish.
- CSS uses existing design tokens from
07-DESIGN.md §7; no hardcoded
colours, spacing, or typography.
- No new JavaScript files are added; the visibility script is inline in
the generated
index.html, consistent with §71.11.
- No new npm or Composer dependencies.
98. Locale Overview Page
98.1 Context
When participants add activities to the schedule via /lagg-till.html,
there is no direct way to see which locales are already booked at which
times. The form links to /schema.html with a reminder to “check the
schedule before adding”, but on mobile this is awkward and an entire
week of bookings is hard to hold in memory. As a result, bookings that
unintentionally clash with existing activities can be submitted.
A dedicated Locale Overview page shows the active camp’s events laid
out as a visual time-grid — one row per locale, blocks placed at their
scheduled times — so a person picking a time and place can see at a
glance which locales are already taken and which are free.
This section describes the overview page only. A soft conflict warning
rendered inside the add- and edit-activity forms, linking to this
overview, is covered in a later section. Issue #332.
98.2 Page existence and content
- A page at
/lokaler.html exists on the built site and is regenerated
on every build.
- The page displays every locale defined in
source/data/local.yaml,
in the same order as they appear in that file.
- Each locale is represented as one row in a visual time-grid that
spans from the current date (inclusive) through the active camp’s
end_date. When the camp has not yet started (today before
start_date), the grid spans start_date through end_date. When
the active camp is fully in the past, the grid falls back to the
full camp span so the page still renders. Past dates within an
in-progress camp are hidden — there is no point showing yesterday
when planning a new activity.
- Events from the active camp are rendered as time-blocks positioned
horizontally within each locale row according to their date and
start/end times.
- Each event-block displays the activity’s title, start time, end
time, and responsible person.
- Locales that have no events during the active camp are shown with
the text “Inga bokningar” on that row.
- Events whose
location value does not match any name in
local.yaml are rendered in the “Annat” row.
98.3 Navigation
- The page is reached from
/schema.html and /lagg-till.html via a
text link labelled “Se lokalöversikt →”. On /lagg-till.html the
link sits inside the intro paragraph that reminds the participant to
check the schedule for clashes before submitting.
- The page does not appear as an entry in the top navigation.
98.4 Accessibility and user-facing text
- The page heading is “Lokalöversikt” and all user-facing text is in
Swedish, consistent with §14.
- Each event-block is focusable with the keyboard and carries an
aria-label that communicates locale, date, time range, title, and
responsible person, so a screen-reader user does not depend on
visual grid positioning to understand the booking.
- The page includes a short legend above the grid explaining that
blocks represent booked times and rows marked “Inga bokningar”
mean the locale is free for the entire camp. The legend is placed
above rather than below so it stays within the reader’s first
viewport — the grid itself is often taller than the screen.
98.5 Rendering
- The grid markup is generated server-side at build time by a new
renderer
source/build/render-lokaler.js. The page requires no
client-side JavaScript to render or position the grid.
- The grid’s visual styling — colors, spacing, and typography — uses
the custom properties defined in
docs/07-DESIGN.md §7; no colors,
spacing, or font sizes are hardcoded.
98.6 Mobile behaviour
- On viewport widths below 600px the grid wrapper scrolls horizontally
so that the full camp week remains viewable without breaking the
surrounding page layout. The rest of the page flows normally.
98.7 Clash visualisation
- When two or more events in the same locale on the same day overlap
in time, they are stacked in separate vertical lanes within the day
band. Each event remains independently visible; one event never
covers another.
- Every event that overlaps at least one other event in the same
locale is visually marked as a clash: a distinct accent colour on
the block (differentiated from the default booking colour) so that
clashes stand out at a glance without requiring the reader to
interact with the block.
- Back-to-back events (one’s end time equals another’s start time)
are not treated as clashes.
- Non-overlapping events in a day that contains other clashing events
retain the full height of their row. Only events that actually
overlap one another share vertical space with each other.
- The top-left corner cell of the grid labels both axes with the text
“Lokaler \ Dag” (the backslash reads as a diagonal separator between
the row axis and the column axis).
- Events whose
start time equals their end time (zero duration,
typically legacy “sista för idag 23:59–23:59” markers) are not
rendered as blocks on the grid — they have no duration to visualise
and cannot conflict with anything. They remain visible through the
regular schedule views.
- Cross-midnight events (events where the
end time falls strictly
before the start time, allowed up to 17 hours per §9) are split
into two visual blocks — one on their own date from start to
24:00, and one on the following date from 00:00 to end. Both
blocks link to the same per-event detail page and carry aria-labels
describing the continuation so a screen-reader user understands
they are the same booking.
- The grid markup uses native table elements so assistive tech can
announce the two-axis structure:
<table> as the grid container,
<tr> for each row, <th scope="row"> for locale labels, <th
scope="col"> for day headers and the top-left corner cell, and
<td> for each day band. CSS Grid still drives the visual
layout via display: grid on the <table> and display:
contents on the <tr>s.