feat: placeholder frontpage (#40) + do-not-merge label workflow#43
Merged
Conversation
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>
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>
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>
yepzdk
reviewed
Jun 11, 2026
yepzdk
previously approved these changes
Jun 11, 2026
yepzdk
left a comment
Contributor
There was a problem hiding this comment.
One miner thing to consider. But element might be subject to change later on, so not a blocker.
tuj
requested changes
Jun 11, 2026
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>
tuj
approved these changes
Jun 11, 2026
martinyde
approved these changes
Jun 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Frontpage for #40 — what started as a static placeholder is now a translatable, component-based design preview behind a 100 % coverage gate.
App\Controller\FrontpageControlleratGET /— composesfrontpage/index.html.twigfrom hardcoded sample assistants and derived stats; no service layer yet.symfony/ux-twig-componentcomponents undertemplates/components/(Layout, Nav, Hero, Stats, SearchBox, CardRail, Box, StepList).h1→h2→h3(hero, section eyebrows via Eyebrow'sasprop, card titles). Cards wrap onto multiple rows viaflex flex-wrap, not a snap-scroll rail.default_locale: da. All UI strings live intranslations/messages.da.yamlwith dot-notation keys; components apply|transto text props internally.BRAND_NAME,BRAND_TAGLINE,BRAND_INITIALS) from env, exposed as Twig globals; parameter-backed defaults inconfig/services.yamlkeep the page working without.env.App\Twig\DevTemplateMarkerNodeVisitorwraps every project template at compile time with<!-- name -->HTML comments indevonly — no-op in prod.rregeer/phpunit-coverage-check. CI runstailwind:buildaftercomposer installso AssetMapper can resolve@import "tailwindcss"..github/workflows/block-on-label.yamladds a per-PR merge gate that fails the check while ado-not-mergelabel is on the PR.Screenshots
Frontpage:
TemplateMarkerNodeVisitor:
Test plan
https://ai-lib.local.itkdev.dk/returns 200 with hero, search, card rail, "Sådan virker det" steps, and footer.h1→h2→h3(verified via rendered HTML).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.Closes #40
🤖 Generated with Claude Code