Skip to content

feat: placeholder frontpage (#40) + do-not-merge label workflow#43

Merged
martinyde merged 25 commits into
developfrom
feature/issue-40-frontpage-placeholder
Jun 11, 2026
Merged

feat: placeholder frontpage (#40) + do-not-merge label workflow#43
martinyde merged 25 commits into
developfrom
feature/issue-40-frontpage-placeholder

Conversation

@martinydeAI

@martinydeAI martinydeAI commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

Frontpage for #40 — what started as a static placeholder is now a translatable, component-based design preview behind a 100 % coverage gate.

  • New App\Controller\FrontpageController at GET / — composes frontpage/index.html.twig from hardcoded sample assistants and derived stats; no service layer yet.
  • Frontpage + base layout refactored into 17 anonymous symfony/ux-twig-component components under templates/components/ (Layout, Nav, Hero, Stats, SearchBox, CardRail, Box, StepList).
  • Heading hierarchy h1h2h3 (hero, section eyebrows via Eyebrow's as prop, card titles). Cards wrap onto multiple rows via flex flex-wrap, not a snap-scroll rail.
  • Symfony translator added with default_locale: da. All UI strings live in translations/messages.da.yaml with dot-notation keys; components apply |trans to text props internally.
  • Brand identity (BRAND_NAME, BRAND_TAGLINE, BRAND_INITIALS) from env, exposed as Twig globals; parameter-backed defaults in config/services.yaml keep the page working without .env.
  • App\Twig\DevTemplateMarkerNodeVisitor wraps every project template at compile time with <!-- name --> HTML comments in dev only — no-op in prod.
  • PHPUnit + Xdebug coverage gate enforced at 100 % via rregeer/phpunit-coverage-check. CI runs tailwind:build after composer install so AssetMapper can resolve @import "tailwindcss".
  • .github/workflows/block-on-label.yaml adds a per-PR merge gate that fails the check while a do-not-merge label is on the PR.

Screenshots

Frontpage:

Skærmbillede 2026-06-09 kl  14 25 18

TemplateMarkerNodeVisitor:

Skærmbillede 2026-06-09 kl  14 24 59

Test plan

  • https://ai-lib.local.itkdev.dk/ returns 200 with hero, search, card rail, "Sådan virker det" steps, and footer.
  • Heading hierarchy on the page is h1h2h3 (verified via rendered HTML).
  • Mobile (320–600 px): card rail wraps to multiple rows, stats stack to a single column, no page-level horizontal scroll.
  • Card hover lifts ~2 px; top border and shadow remain visible.
  • Hamburger toggles the mobile nav (Stimulus nav-toggle); skip-link appears on first Tab and focuses #main.
  • task test-coverage → 100 % (12 tests, 18 assertions).
  • task coding-standards-check → green across PHP, Twig, YAML, JS, CSS, Markdown, Composer.
  • CI: yaml-lint, composer-normalized, and PHPUnit + coverage gate all pass on the latest push.

Closes #40

🤖 Generated with Claude Code

Adds a thin `FrontpageController` mounted at `GET /` that renders a
placeholder Twig template, identifying the project as ai-lib and
signalling that the application is under construction. The template
extends `templates/base.html.twig` from PR #41 and styles itself with
Tailwind utilities — the asset pipeline introduced there is its
first real consumer.

A `WebTestCase` smoke test asserts `/` returns 200 and the response
contains "ai-lib". The test file is committed ahead of #31; it will
start running automatically once PHPUnit lands.

Also adds `.github/workflows/block-on-label.yaml`: a per-PR merge
gate that fails the check while a `do-not-merge` label is applied.
Useful when a PR depends on another PR/release/review that needs to
land first. The workflow only fails the check — making the merge
button itself unavailable requires marking this check as a required
status check in branch protection (out of scope for this PR).

Refs #40

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@martinydeAI martinydeAI added the do-not-merge Block merging until external dependency lands label Jun 9, 2026
@martinyde martinyde added do-not-merge Block merging until external dependency lands and removed do-not-merge Block merging until external dependency lands labels Jun 9, 2026
@martinyde martinyde changed the base branch from main to develop June 9, 2026 06:42
@martinyde martinyde self-requested a review June 9, 2026 06:49
martinydeAI and others added 14 commits June 9, 2026 10:08
Evolves the placeholder frontpage from a single "under construction"
message into a faithful preview of the AI Bibliotek prototype, so
visitors get a first-glance impression of the catalogue before any
real data exists.

Mirrors the home view from
`itk-dev/research-projects/docs/public/projects/ai-bibliotek/mocks`:

- Hero with eyebrow, headline, lede paragraph, and three-stat block
  (assistant / kommune / model counts driven by the sample data).
- Non-functional search prompt.
- Horizontal rail of five sample assistants drawn from the prototype's
  seed data (Borgerservice-vejviser, Mødereferent, Journaliserings-
  assistent, Skole- og dagtilbudssvar, Tilsynsrapport-assistent).
- "Sådan virker det" four-step ordered list.
- "Kommer snart" chip grid for teased future features.

The prototype's CSS is **converted to Tailwind v4 utilities** rather
than imported verbatim. Design tokens (palette, fonts) move into
`@theme` blocks in `assets/styles/app.css`, so they become Tailwind
variants (`bg-surface`, `text-ink`, `font-display`, etc.). A small
`@layer components` block keeps the pseudo-element and keyframe
patterns that utility classes can't express (`eyebrow::before`,
fade-up stagger, hamburger toggle).

`templates/base.html.twig` gets the prototype's chrome — header with
brand mark + responsive nav, footer, Fraunces/Geist fonts preloaded
from Google Fonts. A new `nav_toggle_controller` Stimulus controller
drives the mobile menu.

Sample data lives in `FrontpageController` as `const SAMPLE_ASSISTANTS`
— intentionally inline so the day this is replaced by a real
`AssistantRepository`, the diff is small and obvious.

Refs #40

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Install symfony/ux-twig-component and refactor the placeholder frontpage and base layout into 17 anonymous components under templates/components/ (Layout, Nav, Hero, Stats, SearchBox, CardRail, Box, StepList, TagList, Eyebrow). Rendered HTML and Stimulus / CSS hooks are unchanged; future sections can compose the same primitives instead of duplicating utility lists.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wrap each template's output in begin + end HTML comments that print the template path via Twig's _self variable. Gated on app.environment == 'dev' so prod output is unchanged. Makes it trivial to identify which template produced any DOM region from browser DevTools.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removes the Coming Soon chip list from templates/frontpage/index.html.twig and the now-unused COMING_SOON const + template variable from FrontpageController. The TagList components stay in templates/components/ as reusable primitives.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Documents the anonymous-component-first structure under templates/components/, the {% props %} + named-slot contract, the dev-mode {{ _self }} begin/end markers, and when to extract vs. inline. Intended as the reference future contributors (and future Claude sessions) read before adding new templates.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Install symfony/translation with default locale da and a single translations/messages.da.yaml file holding every user-facing label, ARIA string, placeholder, page title, and copy block. Components translate text props internally via |trans, so call sites pass keys (e.g. label="frontpage.stats.assistants") rather than literal Danish text. The hero heading and footer caption use _html-suffixed keys rendered with |raw.

Brand identity (name, tagline, initials) is sourced from BRAND_* environment variables, exposed as Twig globals (brand_name, brand_tagline, brand_initials) with parameter-backed defaults in config/services.yaml so the page works whether or not .env defines them. Brand strings are configuration, not localization, and are not translated.

CLAUDE.md documents the new Translations and Brand-identity sections.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CardRail container: add pt-2 so the card's -translate-y-0.5 lift and its shadow-md aren't clipped by overflow-y:auto (which the spec forces on us via overflow-x:auto). Stats list: switch grid-cols-3 to grid-cols-1 sm:grid-cols-3 so the long single-word Danish labels (SPROGMODELLER) get their own row below the sm breakpoint instead of overflowing the column and pushing the page horizontally.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds BRAND_NAME, BRAND_TAGLINE, BRAND_INITIALS as committed defaults so the parameter fallbacks in config/services.yaml are only a safety net. .env.local can override per-deployment.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There is no live watcher and cache:clear does not rebuild the stylesheet — new utility classes only apply after tailwind:build runs. CLAUDE.md gets a dedicated subsection so future sessions stop being puzzled; README.md gets a heads-up callout next to the build commands. Also fixes a stale hello_controller.js reference in README.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drops the hardcoded github.com/itk-dev/ai-lib and prototype-mockup URLs from base.html.twig so the placeholder frontpage doesn't ship pointers we haven't committed to yet. Both footer link href and the %link% substitution in the caption become '#' and can be wired to real targets (env var or route) later.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The CardRail was a horizontal snap-scroll rail. Its flex children with min-w-[260px] forced the rail's min-content to ~1364px, which propagated up through the section into the view-root's implicit grid column and dragged the entire page horizontally on narrow viewports.

Switching the container to 'flex flex-wrap gap-4' and the card to 'grow basis-[260px] max-w-[400px]' lets cards reflow onto new rows as the viewport narrows. Also adds grid-cols-1 to view-root so its implicit column gets minmax(0, 1fr) and no future overflow-x child triggers the same propagation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Eyebrow gains an 'as' prop defaulting to h2 — section kickers are now real subheadings under the hero's h1. Hero overrides with as="p" because its eyebrow sits above the h1 and isn't a heading. Card titles become h3, completing the h1 -> h2 -> h3 hierarchy.

To keep the visual identical, .eyebrow declares font-family: var(--font-sans) (the @layer base h1..h4 rule would otherwise force the display serif onto a <h2 class="eyebrow">), and the card title gets tracking-normal to cancel the -0.01em letter-spacing applied to every heading by that same base rule.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@martinyde martinyde removed the do-not-merge Block merging until external dependency lands label Jun 11, 2026
martinydeAI and others added 4 commits June 11, 2026 10:29
Three failing PR checks addressed:

- yaml-lint: translations/messages.da.yaml reformatted with Prettier.

- composer-normalized: pin symfony/translation as ~8.1.0 instead of 8.1.* so composer normalize stops rewriting it on every CI run; composer.lock refreshed.

- PHPUnit + coverage gate: assets/styles/app.css imports tailwindcss via Tailwind v4's directive, which AssetMapper can't resolve until bin/console tailwind:build has run. The Tests workflow now runs tailwind:build after composer install and before phpunit, mirroring the local-dev flow documented in README/CLAUDE.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PHPUnit harness and the frontpage smoke test stay in the codebase; having tests is the default expectation for the project, not a noteworthy user-facing change. Drops the two related bullets from the [Unreleased] block.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@martinyde martinyde requested review from tuj and yepzdk June 11, 2026 08:48
Comment thread templates/components/SearchBox.html.twig
yepzdk
yepzdk previously approved these changes Jun 11, 2026

@yepzdk yepzdk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One miner thing to consider. But element might be subject to change later on, so not a blocker.

Comment thread config/services.yaml Outdated
Comment thread config/services.yaml Outdated
Comment thread src/Kernel.php
Comment thread templates/base.html.twig
Comment thread CHANGELOG.md
martinyde and others added 5 commits June 11, 2026 11:32
Drops the brand.default_* parameters in config/services.yaml and the corresponding default: fallbacks in the Twig globals. .env already ships the BRAND_NAME/BRAND_TAGLINE/BRAND_INITIALS values, so the parameter-backed safety net just duplicated that source and would silently drift if one side changed without the other. Addresses tuj's review on PR #43.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The <label> and the input's aria-label both echoed submitLabel ("Søg"), so the field's accessible name duplicated the submit button rather than describing the input. The placeholder isn't a substitute for a label — it disappears on input and has weak screen-reader support. Use legend ("Find en assistent") for the <label> and drop the now-redundant aria-label. Addresses yepzdk's review on PR #43.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread CHANGELOG.md
Comment thread CHANGELOG.md
Comment thread src/Kernel.php
@martinyde martinyde merged commit a41dc67 into develop Jun 11, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement simple frontpage with placeholder content

4 participants