feat: plakat redesign for subpages (projects, books, legal, privacy)

- Shared SubpageShell (HeaderClient paper strip + ink footer) and SubpageIntro,
  btn/ghost/card helpers in app/_ui/subpage.tsx
- Projects list: poster cards with hard ink shadows, filter pills, ink search field;
  no fake 800ms skeleton and no framer-motion opacity:0 in SSR
- Project detail: poster hero, .plakat-prose markdown, links/stack aside,
  back link works without JS
- Books: server-side fetch (visible without JS), cover + rating pills
- Legal notice / privacy: moved to app/_ui/*Client.tsx, root routes removed
  (middleware always redirects to /[locale]), consent reset via useConsent
- ProjectThumbnail rebuilt on Plakat tokens
- Old Header.tsx + test removed; brown/cream/sand palettes and unused
  glass-panel/admin-glass(-light)/liquid-bg-blob/float CSS dropped
  (liquid palette, glass-card, admin-glass-card stay for admin + 404)
- .plakat-prose replaces the missing typography plugin

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
denshooter
2026-10-16 00:00:58 +02:00
parent 9e6ae866d0
commit 486555dbf2
22 changed files with 918 additions and 1005 deletions
-34
View File
@@ -1,34 +0,0 @@
import { render, screen } from '@testing-library/react';
import Header from '@/app/components/Header';
// Mock next-intl
jest.mock('next-intl', () => ({
useLocale: () => 'en',
useTranslations: () => (key: string) => {
const messages: Record<string, string> = {
home: 'Home',
about: 'About',
projects: 'Projects',
contact: 'Contact'
};
return messages[key] || key;
},
}));
// Mock next/navigation
jest.mock('next/navigation', () => ({
usePathname: () => '/en',
}));
describe('Header', () => {
it('renders the header with the dk logo', () => {
render(<Header />);
expect(screen.getByText('dk0')).toBeInTheDocument();
// Check for navigation links (appear in both desktop and mobile menus)
expect(screen.getAllByText('Home').length).toBeGreaterThan(0);
expect(screen.getAllByText('About').length).toBeGreaterThan(0);
expect(screen.getAllByText('Projects').length).toBeGreaterThan(0);
expect(screen.getAllByText('Contact').length).toBeGreaterThan(0);
});
});