The floor-accordion was rendering as a stack of plain text rows separated
by 1px hairline dividers with no visual affordance indicating they were
clickable tabs. CSS for .floor-icon (a 24px circle with a '+' that
rotates 45° to '×' on open) already existed, but the corresponding
<span class="floor-icon"> element had never been added to the template
— the affordance was completely invisible.
Changes:
1. Template (app/views/home/index.php): add
<span class="floor-icon" aria-hidden="true">+</span> inside the
<summary>, enabling the existing rotation/size CSS to take effect.
2. CSS (public/css/haus-schleusingen.css):
a. Restyle .floor-item from a thin divider to a real card
(1px --warm border, 4px radius, --white background) with a
0.75rem gap between items via flexbox column on .floors-accordion.
b. .floor-header: hover background in --cream, focus-visible
outline, list-style markers suppressed, padding tightened to
1.25rem 1.5rem.
c. .floor-icon: enlarged from 24px to 32px, thicker 1.5px border,
font-size 1.15rem so the '+' is clearly legible, white
background that goes cream on hover/open.
d. .floor-item.open: --accent border, subtle box-shadow
(0 4px 20px 6% black), and a 3px-wide ::before accent stripe on
the left edge — the canonical 'active tab' indicator.
e. .floor-icon rotation on open (45°) now also flips border-color
and background to --accent / --cream, plus hover state for the
closed icon.
f. Renamed dead-code class .floor-size → .floor-area to match the
template (refactor mismatch left over from the i18n work).
g. Renamed dead-code class .floor-accordion → .floors-accordion to
match the template.
Measured on 1265px viewport:
- 4 items, 4 icons (one per tab)
- Each item: 1104x74, white bg, 4px radius, --warm border
- Icon: 32x32 at x=1128 (right edge), white bg, '+' glyph
- Open state (item[1]): border → --accent, left 3px accent stripe,
icon rotates 45° to '×', subtle box-shadow
Sweep targets for visual confirmation:
- Closed: white card + warm border + '+' icon, hover lifts background
to cream and icon border to accent.
- Open: accent border + left stripe + '×' icon + soft shadow, body
expands below to show the floor-plan image and room list.
- Switching tabs: clicking another header collapses the current
open one (existing JS accordion behaviour).
Martin feedback round 3: dropdown still looked 'fuerchterlich' even
with the official flag-icons. Root cause: 14px vertical padding
around an 18px-tall flag meant the flag occupied only 39% of the
trigger height and was dwarfed by whitespace. Plus a 1px black
box-shadow border made flags look 'boxy', and loading='lazy' caused
empty boxes on the four menu flags the moment the <details> opened.
Changes:
- Flag size 24x18 -> 32x24 (+78% area, ~4:3 matches flag-icons)
- Trigger padding 14px 8px -> 6px (flag now 73% of trigger width,
55% of trigger height, was 46%/39%)
- Drop the artificial 1px black box-shadow outline on flags
- Drop border-radius on flags (real flag-icons look better as
crisp rectangles)
- Drop object-fit: cover (no longer needed for SVG)
- Drop loading='lazy' and decoding='async' (4 small SVGs, must
be ready the moment <details> opens, not flash empty boxes)
- min-height: 44px restored on trigger for WCAG 2.5.5 touch target
- Menu border-radius 8 -> 10px, padding tightened, font-size 0.85
-> 0.9rem for label legibility
- Two-layer box-shadow on menu for subtle elevation
The previous inline flag SVGs were visually broken — most notably the
'en' Union Jack, which was reduced to a single X plus a cross and did
not resemble the real flag at all. The 'de' and 'ru' stripes also had
slight off-by-pixel rounding errors.
Switched to lipis/flag-icons (CC-BY 4.0) shipped as static files under
public/img/flags/. These are the canonical, professionally-designed
flag icons with correct proportions and all the details of the real
flags. Loaded via plain <img> tags (no JS, no external CDN at
runtime, no FOUC, no extra request after the page is cached).
Locale code mapping: en -> gb (per ADR-002, en = en-GB). Unknown
locales fall back to a 1x1 transparent gif so the layout stays
intact.
The nav previously showed 4 inline flag buttons (DE/EN/UK/RU) on desktop
and a details-based dropdown on mobile. Martin asked for one dropdown with
a trigger the size of a single flag, and the 4 inline flags to go away.
- LocaleSwitcher: render a single <details class='locale-switcher'>
everywhere; trigger is one flag + tiny caret; menu lists all 4 with labels.
- Drop the 4-inline <ul> and the locale-switcher-mobile duplicate.
- CSS: replace both blocks with one compact dropdown (flag-sized trigger,
44px touch target via padding, scrolled/transparent-nav variants).
- Tests: assert 4 menu options, 5 flag SVGs, single <details> dropdown,
active locale is a <span aria-current>, others are <a> with hreflang.
- 141/141 PHPUnit green.
- mobile (≤900px): hide .nav-cta (was overflowing viewport by 65px)
- .nav-hamburger span: center inside button via left:50%; top:50%
- .nav-hamburger.active states: add translate(-50%,-50%) so the X
lines stay centered after rotation
- .hero-bg: switch from background-* to object-fit/object-position
(was an <img> but CSS targeted background-image → 1024px wide
hero-bg broke mobile layout)
- responsive: also override nav.scrolled padding on mobile
(specificity 0,1,1 > 0,0,1 → 3rem padding stayed in effect,
pushing the hamburger off-screen with width:0 on scroll)
- home/index.php: rename lage-map → lage-map-wrapper so the
existing .lage-map-wrapper CSS (border, margin-top: 3rem,
overflow: hidden, full-width 450px iframe) actually applies
- Replace all 3 occurrences of $_SERVER['REQUEST_URI'] with '/'
- Prevents potential open redirect via client-controlled REQUEST_URI
- Safe since contact form only exists on homepage
Fix#43
- Generate CSRF token (32 bytes) on GET requests
- Add hidden csrf_token field to contact form
- Validate token with hash_equals() (timing-safe) on POST
- Reject invalid/missing tokens with user-friendly error
Fix#42