Skip to content

#24 — Shared Shell + JSON Data Foundation - #24

Merged
UAJOP merged 7 commits into
mainfrom
feat/shared-shell-json-data-v1
Aug 23, 2026
Merged

#24 — Shared Shell + JSON Data Foundation#24
UAJOP merged 7 commits into
mainfrom
feat/shared-shell-json-data-v1

Conversation

@UAJOP

@UAJOP UAJOP commented Aug 23, 2026

Copy link
Copy Markdown
Owner

#24 — Shared Shell + JSON Data Foundation

Three things: portfolio truth becomes a canonical JSON layer, the React shell becomes production-intended, and the V3 design system gets its foundation.

DO NOT MERGE — draft, pending architecture review and visual/product testing.

No public page is React. Home and About are untouched; that is #25.

Data migration parity

The canonical JSON was extracted programmatically from the live registry — not re-typed — then composed back and compared against baseline 23345d6:

baseline JSON bytes: 17347
composed JSON bytes: 17347
semantic differences: 0

Zero differences, key order included. The only intentional change afterwards is the truthful #24 Build Log entry (8 → 9 checkpoints). Nothing was reworded.

experience was not created — no structured experience data existed in the registry, and inventing an empty dataset to match a roadmap word would be fake completeness.

Legacy adapter

portfolio-data.js is now generated from data/portfolio/ and committed. GitHub Pages serves repository files directly, so the legacy pages must not need CI to boot.

A runtime fetch was rejected deliberately: every consumer — Recruiter Mode, Ajoop, Build Log, Labs, the SINAMA Evidence Explorer — reads window.KAAN_PORTFOLIO synchronously during boot, so fetching would have made the entire legacy runtime asynchronous to solve a build-time problem.

Property Result
Deterministic byte-identical across runs (27,012 B)
Synchronous classic script yes
Top-level frozen yes, matching the previous artifact
Deep-equals composed JSON yes

qa:data verifies and never regenerates. A stale artifact fails CI, because the committed file is what Pages actually serves.

Failure injection

Five deliberate regressions, each reverted. All exited non-zero:

Injected Caught by
stale generated portfolio-data.js staleness comparison
wrong primaryTitle protected truth check
non-canonical social URL canonical socials check
recruiter evidence → missing project reference integrity
malformed JSON parse check

Writing the guard also caught a real bug: it asserted recruiter capabilities were bilingual when they are plain strings — which exposed that the preview was rendering them through the bilingual helper and producing empty entries. The guard was corrected to match the data and the page was fixed, not the reverse.

Shared shell

SiteShell / SiteHeader / SiteFooter hold no route literals. Navigation arrives via navItems, brand destination via brandTo. That is the single property letting #25 mount production routes in these same components without editing them — and qa-react-foundation.js now enforces it, along with keeping preview chrome out of components/shell/.

Four primitives, each with more than one caller: Container, Surface, Badge, Action, plus SectionHeading. Action renders whichever element its behavior requires (to → Link, href → anchor, onClick → button), which is why the system contains no simulated controls.

V3 design system

Six stylesheets split by responsibility. components.css contains no literal color values — that is what makes both themes independently designable rather than one being the other inverted.

Two tokens exist because one value could not serve both themes: --accent-text (accent as text, since --accent is tuned for fills and focus rings) and --text-on-accent.

Contrast was measured with alpha compositing against the real backdrop, in both themes, after a genuine page load. Three light-theme near-misses were fixed rather than accepted: primary action fill 4.33:1, accent chip 4.43:1, warning chip 4.38:1.

Theme Minimum contrast
Dark 5.10:1
Light 4.61:1

Preview

A labelled specimen of the system — type scale, actions, signals, evidence surfaces, capability chips — rendered from real canonical data (SINAMA, Merge Rush, recruiter focuses). Deliberately not a home page layout.

Route Pre-rendered markup
/react-preview/ 13,261 B
/react-preview/about 6,800 B
404.html 4,518 B

Verified at 1440×900, 1366×768, 768×768, 844×390, 390×844 and 360×800, both themes, EN and TR (long Turkish copy is the overflow worst case): no horizontal overflow anywhere, clean hydration, client nav / back / forward confirmed as real client transitions.

QA

Check Result
npm run qa:data pass
npm run qa pass — 19/19 JS, 0 HTML errors, 0 spelling
npm run build:react pass
npm run qa:react pass
npm run qa:a11y (11 production pages) 11/11, 0 errors
npm run qa:a11y:react (3 routes) 3/3, 0 errors
Production Lighthouse 95–98 perf · 100/100/100 a11y/BP/SEO
Console — preview 0 errors, 0 React/hydration warnings
Console — production 0 errors
git diff --check clean

qa:data is blocking and runs first in CI. No existing gate weakened; Lighthouse and the external link scan remain the only report-only checks.

Production isolation

git diff --name-only main touches zero production HTML, CSS or runtime JS. The only production-visible change is portfolio-data.js becoming generated while keeping the identical runtime contract, plus the Build Log entry.

Verified in-browser across index, works, games, labs, now, sinama-case-study, merge-rush-case-study: registry boots frozen and synchronous, Recruiter Mode renders registry-backed capabilities, Build Log shows the new entry alongside existing history, Labs renders all four, the SINAMA Evidence Explorer renders from sinamaEvidence, Command Palette opens, theme and EN/TR behave as before, 5 canonical footer destinations everywhere, 0 broken images.

Bundle

316.5 KB raw / 109.0 KB gzip; 1 JS chunk, 1 CSS chunk, 3 HTML files, 1 imported image. No dependency added.

Against #23, as architecture-cost context only: JS gzip +6.5 KB, CSS gzip +2.0 KB, home pre-rendered markup +9.8 KB — the preview gained a design system, a shared shell and real data. Not production cost; no public page is React.

Documentation

V3_DESIGN_SYSTEM.md (new) · PORTFOLIO_ARCHITECTURE.md (canonical data layer, generated adapter, shared shell) · REACT_MIGRATION_PLAN.md §8 rewritten as implemented · README.md, SITE_PREFLIGHT.md, QA_BASELINE.md.

Locked #23–#33 roadmap unchanged — only the status markers on #23 and #24.

Deferred

#25 Home + About · #26 Works + Games · #27 Recruiter Mode + Build Log · #28 Ajoop + Command Palette · #29 Case Studies + Dynamic Project Routes · #30 Labs + Mini-game Shells · #31 Legacy Runtime Removal · #32 External Dependency + Bundle Cleanup · #33 Hardening + V3 Final.

🤖 Generated with Claude Code

UAJOP and others added 7 commits August 23, 2026 22:43
Splits the registry into eight canonical JSON files under data/portfolio/, one
per domain: meta, profile, socials, projects, recruiter-profiles, build-log,
labs and sinama-evidence.

The content was extracted PROGRAMMATICALLY by evaluating the existing
portfolio-data.js and serializing the live object, not re-typed. Re-typing ~17 KB
of bilingual product truth by hand would have risked exactly the kind of silent
drift this data layer exists to prevent. Punctuation, Turkish characters, arrays,
project proof, recruiter evidence, Labs and the sanitized SINAMA examples all
survive byte-for-byte.

Bilingual values keep the { en, tr } shape they already had, so this changed the
storage format and nothing else.

portfolio-data-model.mjs is the single definition of how those files compose back
into the legacy shape, including key order — the generated artifact is compared
byte-for-byte, so order is part of the contract rather than a formatting
preference. The generator and the QA guard both import it, so the file that ships
and the file that is checked cannot disagree about what correct means.

No `experience` dataset was created. No structured experience data existed in the
registry, and inventing an empty one to match a roadmap word would be fake
completeness. It enters the data layer when there is truthful content to extract.

Verified against baseline 23345d6: 0 semantic differences, key order included.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
portfolio-data.js is now generated from data/portfolio/ and committed.

Committed on purpose. GitHub Pages serves repository files directly with no build
step, so the legacy production pages must not need CI to boot. The alternative —
having the legacy runtime fetch the JSON — was rejected: every consumer
(Recruiter Mode, Ajoop, Build Log, Labs, the SINAMA Evidence Explorer) reads
window.KAAN_PORTFOLIO synchronously during boot, so fetching would have made the
whole legacy runtime asynchronous to solve a problem the generator solves at
build time.

The runtime contract is unchanged: a classic script assigning a frozen
window.KAAN_PORTFOLIO before anything reads it. Shallow freeze preserved to match
the previous artifact exactly. No consumer and no page changed.

Generation is deterministic — same JSON in, byte-identical file out, verified
across repeated runs. The output is emitted with LF; the guard normalizes line
endings when comparing, because this repository uses `* text=auto` and a Windows
checkout is CRLF, so a raw byte comparison would fail on one platform and pass on
the other.

Adds data:generate and qa:data. qa:data joins the aggregate qa command first,
since every later check reads the registry it guards.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replaces the #23 foundation proofs with production-intended components.

SiteShell owns the landmark structure — skip link, header, main, footer — plus a
`banner` slot. SiteHeader and SiteFooter hold NO route literals: navigation
arrives through `navItems` and the brand destination through `brandTo`. That is
the single property that lets #25 mount real production routes in these same
components without editing them, and qa-react-foundation.js now enforces it.

Preview-only chrome moved to components/preview/ and is passed through the banner
slot, so removing the preview will touch no shell component.

Four primitives, each with more than one caller: Container, Surface, Badge,
Action, plus SectionHeading. Action renders whichever element its behavior
requires — `to` a router Link, `href` an anchor, `onClick` a button — which is
why this system contains no simulated controls. External links get
rel="noopener noreferrer" automatically so a caller cannot forget it.

React now reads canonical JSON at build time through Vite's @DaTa alias, never by
fetching: the pre-render step needs the data while generating HTML, and a runtime
fetch would produce empty pre-rendered pages. src/react/data/foundation.js is
deleted as the migration plan promised, and shell strings moved to
data/i18n/react-shell.json.

Identity copy comes from canonical data rather than props, so a page cannot
rename the person.

Fixed while wiring the preview: recruiter `capabilities` are plain
language-neutral strings, not bilingual pairs, and were being rendered through
the bilingual helper. The data guard caught it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replaces the single preview-only stylesheet with six files split by
responsibility: tokens, base, typography, layout, components, motion.

Semantic tokens throughout. components.css contains no literal color values,
which is what makes both themes independently designable rather than one being
the other with the lightness inverted. Light is a designed surface system — white
cards on a cool paper canvas, real borders, shallow cool shadow — not bleached
dark.

Two tokens exist because one value could not serve both themes: --accent-text
(accent as text, since --accent is tuned for fills and focus rings where contrast
rules do not apply) and --text-on-accent (ink on an accent fill).

Contrast was measured with alpha compositing against the real backdrop, in both
themes, after a real page load rather than a scripted theme toggle — toggling
mid-script gave unreliable readings. Three light-theme near-misses were fixed
rather than accepted: primary action fill 4.33:1, accent chip 4.43:1, warning
chip 4.38:1. Minimums are now 5.10:1 dark and 4.61:1 light.

Motion is CSS only, 150–320ms, state feedback exclusively — no entrance
animations, no scroll effects, no dependency. Under prefers-reduced-motion the
one motion carrying meaning, the interactive surface lift, is removed rather than
merely accelerated.

The preview is now a labelled specimen of the system — type scale, actions,
signals, evidence surfaces, capability chips — rendered from real canonical data.
Deliberately not a home page layout; #25 designs the page.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
qa-portfolio-data.js is the contract for the new data layer, and blocking in CI.

It VERIFIES the committed portfolio-data.js against the canonical JSON and never
regenerates it. A stale artifact must fail the build: GitHub Pages serves the
committed file directly, so that file has to be correct in the repository, not
fixed up in CI where nobody would notice it had drifted.

Alongside staleness it enforces the protected truth — Forward Deployed Engineer,
the background descriptor, the five canonical social URLs, the bilingual footer
positioning — plus bilingual field structure, project id/key agreement, recruiter
evidence referring only to projects that exist, unique lab ids, resolvable
internal links, and that nothing resembling a local path or credential has
entered what is now public data.

Five deliberate regressions were injected and each exited non-zero: stale
artifact, wrong primary title, non-canonical social URL, recruiter evidence
citing a missing project, malformed JSON. All reverted.

Writing the guard also caught a real bug: it asserted recruiter `capabilities`
were bilingual when they are plain strings, which exposed that the preview was
rendering them through the bilingual helper and producing empty entries. The
guard was corrected to match the data, and the page was fixed — not the other way
round.

qa-react-foundation.js drops its foundation.js assumptions and now also proves
the shared shell carries no preview assumptions. Its exactly-once social check
moved from the whole document to the footer, whose contract it actually is, so a
page may legitimately link a canonical destination as well.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
V3_DESIGN_SYSTEM.md records the system as implemented: design objective, visual
personality, recruiter-first principles, the dark/light strategy and why two
accent tokens exist, the type scale, layout primitives, surfaces, color usage,
motion rules, accessibility rules, component principles, forbidden patterns, and
how #25 should consume it.

PORTFOLIO_ARCHITECTURE.md states the new source-of-truth rule and the workflow it
implies — edit JSON, regenerate, commit both — plus why the legacy registry is
generated rather than fetched, and why a stale artifact fails instead of being
regenerated in CI.

REACT_MIGRATION_PLAN.md §8 is rewritten as implemented rather than planned. The
locked #23–#33 phase order is unchanged; only the status markers on #23 and #24
were touched.

QA_BASELINE.md records measured results: 0 semantic differences against the
pre-migration registry with key order included, deterministic generation, the
five injected failures, bundle sizes with the #23 comparison as architecture-cost
context only, contrast minimums per theme, and the production regression sweep.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@UAJOP
UAJOP marked this pull request as ready for review August 23, 2026 21:30
@UAJOP
UAJOP merged commit f807860 into main Aug 23, 2026
2 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.

1 participant