fix(confidence-engine): correct legal footer layout

This commit is contained in:
2026-09-10 06:40:30 +01:00
parent 27ded883e5
commit b9d49ff277
6 changed files with 18 additions and 8 deletions
+3 -2
View File
@@ -1,3 +1,4 @@
import React from "react";
import "./globals.css"; import "./globals.css";
import ThemeToggle from "@/components/theme-toggle"; import ThemeToggle from "@/components/theme-toggle";
import LogoutButton from "@/components/logout-button"; import LogoutButton from "@/components/logout-button";
@@ -11,7 +12,7 @@ export const metadata = {
export default function RootLayout({ children }) { export default function RootLayout({ children }) {
return ( return (
<html lang="en"> <html lang="en">
<body className="min-h-screen bg-gray-50 text-gray-900"> <body className="flex min-h-screen flex-col bg-gray-50 text-gray-900">
<script <script
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: `try { const saved = localStorage.getItem('confidence-engine-theme'); const theme = saved === 'dark' || saved === 'light' ? saved : (matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'); document.documentElement.dataset.theme = theme; document.documentElement.style.colorScheme = theme; } catch (_) {}`, __html: `try { const saved = localStorage.getItem('confidence-engine-theme'); const theme = saved === 'dark' || saved === 'light' ? saved : (matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'); document.documentElement.dataset.theme = theme; document.documentElement.style.colorScheme = theme; } catch (_) {}`,
@@ -26,7 +27,7 @@ export default function RootLayout({ children }) {
</div> </div>
</div> </div>
</header> </header>
{children} <div className="flex-1">{children}</div>
<footer className="app-chrome border-t border-gray-200/80 px-6 py-5"> <footer className="app-chrome border-t border-gray-200/80 px-6 py-5">
<LegalNavigation /> <LegalNavigation />
</footer> </footer>
+1 -1
View File
@@ -2,7 +2,7 @@ import LoginForm from "@/components/login-form";
export default function LoginPage() { export default function LoginPage() {
return ( return (
<main className="mx-auto min-h-[calc(100vh-57px)] max-w-[1200px] px-6 py-16"> <main className="mx-auto max-w-[1200px] px-6 py-16">
<div className="mx-auto flex flex-col gap-y-8 md:grid md:grid-cols-[minmax(0,1fr)_420px] md:gap-x-8 md:items-start"> <div className="mx-auto flex flex-col gap-y-8 md:grid md:grid-cols-[minmax(0,1fr)_420px] md:gap-x-8 md:items-start">
{/* Left column wrapper — contents on mobile for flex ordering, block on desktop as one grid cell */} {/* Left column wrapper — contents on mobile for flex ordering, block on desktop as one grid cell */}
<div className="contents md:block"> <div className="contents md:block">
+1 -5
View File
@@ -1,10 +1,9 @@
import React from "react"; import React from "react";
import Link from "next/link"; import Link from "next/link";
import LegalNavigation from "@/components/legal-navigation";
export default function LegalPageLayout({ title, children }) { export default function LegalPageLayout({ title, children }) {
return ( return (
<main className="mx-auto min-h-[calc(100vh-57px)] max-w-3xl px-6 py-12 sm:py-16"> <main className="mx-auto max-w-3xl px-6 py-12 sm:py-16">
<Link href="/login" className="text-sm font-medium text-teal-700 hover:underline"> <Link href="/login" className="text-sm font-medium text-teal-700 hover:underline">
Back to sign in Back to sign in
</Link> </Link>
@@ -14,9 +13,6 @@ export default function LegalPageLayout({ title, children }) {
{children} {children}
</div> </div>
</article> </article>
<footer className="mt-8 border-t border-gray-200 pt-5">
<LegalNavigation />
</footer>
</main> </main>
); );
} }
+1
View File
@@ -1,5 +1,6 @@
"use client"; "use client";
import React from "react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { import {
readThemePreference, readThemePreference,
+1
View File
@@ -8,6 +8,7 @@
- Public Privacy, Terms, and Cookie pages and shared legal navigation are available at `/privacy`, `/terms`, and `/cookies`. - Public Privacy, Terms, and Cookie pages and shared legal navigation are available at `/privacy`, `/terms`, and `/cookies`.
- The service is positioned as 18+. No cookie-consent banner is used because current browser storage is limited to essential authentication/session storage and functional UI preferences. - The service is positioned as 18+. No cookie-consent banner is used because current browser storage is limited to essential authentication/session storage and functional UI preferences.
- Legal wording remains subject to appropriate professional review. - Legal wording remains subject to appropriate professional review.
- Legal navigation is a single global normal-flow footer: it rests at the viewport bottom on short pages, long legal pages push it naturally below their content, and duplicate legal navigation was removed.
## Focused-investigation provider outage boundary ## Focused-investigation provider outage boundary
+11
View File
@@ -6,6 +6,7 @@ import PrivacyPage from "@/app/privacy/page.jsx";
import TermsPage from "@/app/terms/page.jsx"; import TermsPage from "@/app/terms/page.jsx";
import CookiesPage from "@/app/cookies/page.jsx"; import CookiesPage from "@/app/cookies/page.jsx";
import LegalNavigation from "@/components/legal-navigation.jsx"; import LegalNavigation from "@/components/legal-navigation.jsx";
import RootLayout from "@/app/layout.jsx";
import { middleware } from "@/middleware.js"; import { middleware } from "@/middleware.js";
describe("public legal pages", () => { describe("public legal pages", () => {
@@ -16,6 +17,16 @@ describe("public legal pages", () => {
])("renders the %s heading", (heading, Page) => { ])("renders the %s heading", (heading, Page) => {
const html = renderToStaticMarkup(<Page />); const html = renderToStaticMarkup(<Page />);
expect(html).toContain(`<h1 class="text-3xl font-bold tracking-tight text-teal-700">${heading}</h1>`); expect(html).toContain(`<h1 class="text-3xl font-bold tracking-tight text-teal-700">${heading}</h1>`);
expect(html).not.toContain('aria-label="Legal information"');
});
it("keeps the root layout as the single legal navigation owner", () => {
const html = renderToStaticMarkup(
<RootLayout>
<PrivacyPage />
</RootLayout>,
);
expect(html.match(/aria-label="Legal information"/g)).toHaveLength(1);
}); });
it("exposes all public legal links without cookie consent controls", () => { it("exposes all public legal links without cookie consent controls", () => {