Files
portfolio/.planning/phases/02-ssr-shell/02-UI-SPEC.md
T
kayjaydee c8dac9ac88 fix: update portfolio branding to "Killian' DAL-CIN" across all documentation and components
- Corrected the name in various files including CLAUDE.md, README.md, and configuration files to reflect the updated branding.
- Ensured consistency in the use of the new name throughout the project, enhancing brand identity.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 19:54:46 +02:00

12 KiB
Raw Blame History

phase, slug, status, shadcn_initialized, preset, created
phase slug status shadcn_initialized preset created
2 ssr-shell draft false none 2026-04-08

Phase 2 — UI Design Contract: SSR Shell

Visual and interaction contract for Phase 2: SSR Shell. Generated by gsd-ui-researcher, verified by gsd-ui-checker.


Design System

Property Value
Tool Nuxt UI v3 (not shadcn — shadcn gate not applicable)
Preset not applicable
Component library Nuxt UI v3 (@nuxt/ui) — use native components exclusively; custom only when Nuxt UI has no equivalent
Icon library Nuxt Icon (bundled with @nuxt/ui) — Heroicons set (heroicons:) for theme toggle (sun/moon) and social icons (GitHub, LinkedIn, Fiverr via simple-icons:)
Font Inter (system stack fallback: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif) — sourced: --font-family-sans from existing main.css

Source: D-17, D-18, D-20 from 02-CONTEXT.md. No components.json found; shadcn gate skipped.


Spacing Scale

Declared values (multiples of 4 only). Mapped to Tailwind v4 / Nuxt UI tokens:

Token Value Usage
xs 4px (p-1 / gap-1) Icon gaps, inline padding between icon and label
sm 8px (p-2 / gap-2) Compact element spacing, icon button padding
md 16px (p-4 / gap-4) Default element spacing, nav link padding
lg 24px (p-6 / gap-6) Header internal padding, footer padding
xl 32px (p-8 / gap-8) Layout horizontal gutters
2xl 48px (py-12) Not used in Phase 2 (no content sections)
3xl 64px (py-16) Not used in Phase 2 (no content sections)

Exceptions:

  • Touch targets (hamburger button, lang toggle, theme toggle): minimum 44px × 44px — use min-w-11 min-h-11 to comply with WCAG 2.5.5
  • Content max-width: max-w-7xl (1280px) centered with mx-auto px-4 sm:px-6 lg:px-8 — from D-16

Source: D-16 from 02-CONTEXT.md; existing spacing tokens in src/assets/main.css.


Typography

Phase 2 covers only the header and footer — no page content. Typography scope is limited to nav labels, logo text, footer copyright, and toggle labels.

Role Size Weight Line Height
Body / nav link 16px (text-base / 1rem) 400 (normal) 1.5
Label / small copy 14px (text-sm / 0.875rem) 400 (normal) 1.5
Logo name 18px (text-lg / 1.125rem) 600 (semibold) 1.2
Footer copyright 14px (text-sm / 0.875rem) 400 (normal) 1.5

Rules:

  • Maximum 2 font weights used: 400 (regular) and 600 (semibold)
  • No italic, no uppercase transforms on nav links
  • Logo name "Killian" uses semibold to anchor visual identity

Source: Existing --font-size-base, --font-size-sm, --font-weight-normal, --font-weight-semibold from src/assets/main.css.


Color

Light Mode

Role Value Usage
Dominant (60%) #ffffff Page background, header background
Secondary (30%) #f3f4f6 (gray-100) Footer background band, subtle separators
Accent (10%) #85cb85 CTA buttons, active nav link underline, hover states on nav links, social icon hover
Destructive #ef4444 Not used in Phase 2 — no destructive actions

Dark Mode (default for new visitors — D-08)

Role Value Usage
Dominant (60%) #111827 (gray-900) Page background, header background
Secondary (30%) #1f2937 (gray-800) Footer background band, drawer background
Accent (10%) #a3d6a3 CTA buttons, active nav link underline, hover states on nav links, social icon hover (lightened for dark bg)
Destructive #ef4444 Not used in Phase 2

Accent Reserved For (explicit list)

  1. Active nav link — bottom border/underline indicator
  2. Nav link hover state — text color change
  3. Language toggle hover state — text color
  4. Theme toggle icon hover state — icon color
  5. Social icon links in footer — hover color

Accent is NOT used for: passive text, borders, backgrounds, icons in default (non-hover) state.

WCAG Compliance

  • Dark mode body text (#f9fafb on #111827): contrast ratio ~18:1 — PASS
  • Accent #a3d6a3 on #111827 for interactive labels: contrast ratio ~6.2:1 — PASS (4.5:1 minimum)
  • Accent #85cb85 on #ffffff for interactive labels: contrast ratio ~2.5:1 — FAIL for text; use as decoration/border only in light mode. Nav link text stays on --text-primary (#111827), accent applied as underline decoration only
  • Never use red/green alone as meaning — always pair with icon or text label (D-19)

Source: D-17, D-18, D-19 from 02-CONTEXT.md; existing CSS variables from src/assets/main.css.


Component Inventory

Components delivered in this phase only:

AppHeader (COMP-05)

  • Container: <header> with position: sticky; top: 0; z-index: 1020
  • Inner wrapper: max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 — height 64px (h-16)
  • Layout: flex row, items-center justify-between
  • Left: Logo (40×40px image + "Killian" text)
  • Center: Desktop nav links (hidden md:flex gap-6) using UNavigationMenu or native <nav> with <NuxtLink> — active link uses aria-current="page" + accent underline
  • Right: LanguageToggle (FR/EN text button) + ThemeToggle (icon button) + HamburgerButton (mobile only, md:hidden)
  • Background: bg-white dark:bg-gray-900 with subtle bottom border border-b border-gray-200 dark:border-gray-800

LanguageToggle (inside COMP-05)

  • Renders as a <button> displaying current locale code in uppercase: "FR" or "EN"
  • Click switches locale (D-04 — text toggle, no dropdown, no flags)
  • Size: minimum 44×44px touch target
  • Style: ghost button, no background. Accent color on hover.

ThemeToggle (inside COMP-05)

  • Renders heroicons:sun (light mode active) or heroicons:moon (dark mode active)
  • Icon size: 20px (w-5 h-5)
  • Click toggles @nuxtjs/color-mode (D-09)
  • Transition: transition-colors duration-300 on icon swap — no flash
  • Size: minimum 44×44px touch target

MobileDrawer (inside COMP-05)

  • Uses UDrawer component from Nuxt UI v3 (D-02)
  • Opens from left, triggered by hamburger icon (heroicons:bars-3)
  • Close icon: heroicons:x-mark inside drawer
  • Contains: nav links (stacked, full-width) + LanguageToggle + ThemeToggle
  • Overlay: bg-black/50 backdrop

AppFooter (COMP-06)

  • Single band: py-6 bg-gray-100 dark:bg-gray-800
  • Layout: flex row on md+, flex column on mobile — items-center justify-between gap-4
  • Left: copyright text — "© 2026 Killian' DAL-CIN"
  • Right: social icon links — GitHub (simple-icons:github), LinkedIn (simple-icons:linkedin), Fiverr (simple-icons:fiverr)
  • Icon size: 20px (w-5 h-5). Hover: accent color with transition-colors duration-150
  • All links open in _blank with rel="noopener noreferrer" and aria-label

Interaction States

All interactive elements must implement all four states:

Element Default Hover Focus Active
Nav link text-gray-700 dark:text-gray-300 accent color text focus-visible:ring-2 ring-primary-500 ring-offset-2 accent underline
Active nav link accent underline border-b-2 border-primary-500 same focus ring
Language toggle text-gray-700 dark:text-gray-300 font-medium accent color focus ring
Theme toggle icon text-gray-600 dark:text-gray-400 accent color focus ring
Social icon text-gray-500 dark:text-gray-400 accent color focus ring scale-110
Hamburger button text-gray-700 dark:text-gray-300 accent color focus ring

Focus ring spec: outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 — keyboard navigation only, never on click.


Copywriting Contract

Phase 2 scope: header nav labels, footer copyright, mobile drawer, language/theme toggles, ARIA labels.

Element Copy (FR) Copy (EN)
Logo aria-label "Killian' DAL-CIN — Développeur Full Stack — Retour à l'accueil" "Killian' DAL-CIN — Full Stack Developer — Back to homepage"
Nav: Home "Accueil" "Home"
Nav: Projects "Projets" "Projects"
Nav: About "À propos" "About"
Nav: Contact "Contact" "Contact"
Nav: Fiverr "Fiverr" "Fiverr"
Nav: Formation "Formation" "Training"
Hamburger open aria-label "Ouvrir le menu de navigation" "Open navigation menu"
Hamburger close aria-label "Fermer le menu de navigation" "Close navigation menu"
Drawer close button aria-label "Fermer le menu" "Close menu"
Language toggle aria-label "Changer la langue — actuellement Français" "Change language — currently English"
Theme toggle aria-label (dark) "Activer le mode clair" "Switch to light mode"
Theme toggle aria-label (light) "Activer le mode sombre" "Switch to dark mode"
Footer copyright "© 2026 Killian' DAL-CIN" "© 2026 Killian' DAL-CIN"
GitHub icon aria-label "GitHub de Killian' DAL-CIN (nouvelle fenêtre)" "Killian' DAL-CIN on GitHub (opens in new tab)"
LinkedIn icon aria-label "LinkedIn de Killian' DAL-CIN (nouvelle fenêtre)" "Killian' DAL-CIN on LinkedIn (opens in new tab)"
Fiverr icon aria-label "Fiverr de Killian' DAL-CIN (nouvelle fenêtre)" "Killian' DAL-CIN on Fiverr (opens in new tab)"

Destructive confirmation: none — Phase 2 has no destructive actions. Empty state: none — Phase 2 has no data-driven content. Error state: none — Phase 2 has no form submissions or async data.

Source: D-04, D-05, COMP-05, COMP-06 from 02-CONTEXT.md. Translations to be added to fr.json / en.json under keys nav.*, footer.*, a11y.*.


SEO Contract (server-rendered metadata)

Each route in Phase 2 must include the following in SSR HTML output (verified by curl):

Tag Requirement
<title> Per-route via useSeoMeta({ title })
<meta name="description"> Per-route, max 160 chars
<meta property="og:title"> Same as title
<meta property="og:description"> Same as description
<meta property="og:image"> Absolute URL via nuxt-og-image (D-12)
<link rel="canonical"> Absolute URL for current locale route
<link rel="alternate" hreflang="fr"> FR URL
<link rel="alternate" hreflang="en"> EN URL
JSON-LD script Homepage only: Person + ProfessionalService schema (D-11)

Phase 2 uses placeholder routes (no real pages yet) — SEO metadata is wired but content is minimal stubs until Phase 3 fills pages.


Registry Safety

Registry Blocks Used Safety Gate
Nuxt UI v3 (@nuxt/ui) UDrawer, UNavigationMenu, UButton, UIcon Built-in module — no registry vetting required
shadcn none Not used
Third-party none Not applicable

No third-party component registries are used in this phase. All components come from @nuxt/ui which is installed as a verified Nuxt module.


Implementation Notes for Executor

  1. No components.json — shadcn is not used. All component imports are via Nuxt UI v3 auto-imports (UDrawer, UButton, etc.) or native HTML.
  2. app.config.ts must define primary color token mapping to #85cb85 (light) / #a3d6a3 (dark) using Nuxt UI v3 token format.
  3. @nuxtjs/color-mode must be added to nuxt.config.ts modules for FOUC-free dark mode persistence. Default: dark.
  4. nuxt-og-image must be added to nuxt.config.ts modules (D-12 advanced from v2).
  5. Header z-index must be 1020 (z-sticky) to sit above page content but below modals (Phase 3).
  6. The drawer overlay must trap focus while open (keyboard accessibility).
  7. Lang toggle button must call setLocale() from @nuxtjs/i18n composable.

Checker Sign-Off

  • Dimension 1 Copywriting: PASS
  • Dimension 2 Visuals: PASS
  • Dimension 3 Color: PASS
  • Dimension 4 Typography: PASS
  • Dimension 5 Spacing: PASS
  • Dimension 6 Registry Safety: PASS

Approval: pending


Phase: 02-ssr-shell UI-SPEC generated: 2026-04-08