with one click
corp-web-japan-static-page-convention-refactor
// Refactor corp-web-japan static marketing pages so page.tsx becomes the primary readable implementation surface, while keeping only small reusable or interactive helpers extracted.
// Refactor corp-web-japan static marketing pages so page.tsx becomes the primary readable implementation surface, while keeping only small reusable or interactive helpers extracted.
[HINT] Download the complete skill directory including SKILL.md and all related files
| name | corp-web-japan-static-page-convention-refactor |
| description | Refactor corp-web-japan static marketing pages so page.tsx becomes the primary readable implementation surface, while keeping only small reusable or interactive helpers extracted. |
| version | 1.0.0 |
| author | Hermes Agent |
| license | MIT |
| metadata | {"hermes":{"tags":["corp-web-japan","nextjs","static-pages","code-location-conventions","refactor"],"related_skills":["corp-web-japan-origin-main-worktree-safety"]}} |
Use this when cleaning up static marketing pages in corp-web-japan to match docs/code-location-conventions.md section 1.
Make the route file itself the primary readable implementation surface:
src/app/<route>/page.tsx should show the main copy, section order, and page-specific JSXsrc/app/<route>/page.tsx should contain the copy text and the explicit calls/composition that use section componentssrc/components/sections/** should define the components used by page.tsx and hold the style/UI/UX implementation details such as classes, JavaScript, and styling behaviorsrc/components/sections/**src/content/**Do not limit the analysis to the explicit examples named in docs/code-location-conventions.md.
Those examples are illustrative, not an exhaustive registry of every current static page.
For a compliance audit on latest origin/main:
src/app/**/page.tsx/t/** when they are mostly authored local pagesPractical implication learned from repo follow-up:
/t/about-us, /t/certifications, and /t/services/** may already satisfy the route-local authoring goal even if they are newer than the examples in the docspage.tsxorigin/main; those labels can lag behind after a sequence of section-scoped PRs mergespage.tsx, current src/components/sections/** files, and recent git history for that route before recommending more route-local-authoring worksrc/app/solutions/ai-crew/page.tsx was still described in docs as a wrong/pre-refactor example even after multiple merged PRs had already moved most section copy and composition into the route; the correct conclusion was that the docs had become stale, not that the page still needed the same refactor classTypical targets:
Do NOT apply this pattern to:
src/lib/** + shared sectionsorigin/main in a fresh worktree.README.mddocs/code-location-conventions.mdpage.tsxsrc/app/solutions/ai-dashi/page.tsx.page.tsx only within that approved scope.For this repo, one narrowly scoped PR with a single fully completed section is better than a broad PR with many half-finished sections.
When the user wants a section-scoped refactor:
If the branch already accumulated broader changes than the user actually wants:
Practical lesson from AI Dashi follow-up work:
Additional stale-PR lesson from PR 210 follow-up:
origin/main and identify which neighboring sections are now already route-localized on mainpage.tsx structure, insert only the target section's route-local JSX back into the correct place, remove the old section data from shared content files, and keep newly merged neighboring sections untouchedresults) but also re-externalized already-migrated neighboring sections back into src/content/** or a shared *Sections wrapper, throw away that stale branch shapeorigin/main, preserve the latest-main route-local sections exactly as they are, add only the target section's route-local JSX to page.tsx, create a UI-only section component file for that target section if needed, remove only that target section's rendering from the shared wrapper, and delete only that target section's content blob from the shared content moduleplatform section (実務での安全なAI活用を支える...) appears before use-cases. Localizing use-cases first while platform still lived inside the shared shell caused the preview page to render process -> use-cases -> platform -> results instead of process -> platform -> use-cases -> results.page.tsx render order explicitly. Do not assume preserving the copied JSX preserves the page order.platform -> use-cases, moving use-cases out to page.tsx before platform is localized or before the shell is split will often produce use-cases -> platform on preview, even if each section works in isolationAsk: “If I open only page.tsx, can I understand the page quickly?”
If no, pull more structure back into the route.
Good to keep extracted:
RevealOnScrollsrc/components/sections/** when they let page.tsx read as authored composition, for example SectionCard, SectionTitle, SectionBody, PromoCard, or PromoActionCardHeading and CardBodyImportant semantic-slot rule learned from AI Crew design-elements follow-up:
heading="...", title="...", or similar string/ReactNode props, that section is still hiding key authored copy behind a component API<Card><CardHeading>マーケティング見出し</CardHeading><CardBody><p>説明文</p></CardBody></Card>page.tsx while leaving styling, spacing, icon chrome, and layout implementation in the extracted section component fileAICrewDesignElementCard label="業務定義" heading="任せる業務と期待する成果を明確にする">...</AICrewDesignElementCard> with AICrewDesignElementHeading + AICrewDesignElementBody children made the route read more like authored JSX and matched the user's preferred method bettertitle={<>...</>} or similar prop-passing for the main section heading, that title is still being hidden behind a component API<Section><SectionTitle>見出し <strong>強調句</strong></SectionTitle><SectionGrid>...</SectionGrid></Section>AICrewDesignElementsSection title={...} with AICrewDesignElementsTitle + AICrewDesignElementsGrid made the route easier to review because the main section heading and its emphasized phrase were visible directly in page.tsx instead of being passed through a propBad to keep extracted:
<SomethingSections /> wrapper that hides most of the routesrc/content/<page>.ts object whose main job is to store the page’s marketing copy and section compositionconst hero = { ... }, const roles = { ... }, and const contact = { ... } while the route still reads as data blob first, JSX secondpage.tsx as a large local helper like function AICrewSections() when it still hides most of the authored page structurefunction SupportSection() or function ReleaseFlowSection() when it still owns that section's real headings, prose-heavy arrays, CTA text, and JSX structure togetherfunction SomeSection() { const items = [...] ... } while the route body now only shows <SomeSection />page.tsxImportant anti-regression rule from AI Dashi follow-up work:
moving const supportItems = [...] or const releaseFlow = [...] out of file scope is not enough if the same prose-heavy data and section markup are merely re-hidden inside a local helper such as function AIDashiSupportSection()
that reduces top-level clutter, but it does not yet make the route body the primary readable authoring surface
if the default export body becomes less readable because the section collapsed to a single helper call, treat the refactor as still incomplete
when localizing a pre-existing card/list section into route-local JSX, preserve the exact existing icon components unless the user explicitly asks for a visual change
do not replace established Lucide icons with ad hoc inline <svg> markup just because the section is being rewritten into direct JSX
practical regression found in AI Dashi wall-cards follow-up: a refactor-only PR accidentally changed Users and Settings icons to custom SVGs even though the task was only authoring relocation; the correct fix was to restore the original icon components so the refactor remained visually identical
when a route-localized section is still a large raw JSX/class blob in page.tsx, treat the refactor as incomplete even if the copy has already been removed from src/content/**
the next step is usually to promote that section file from a thin container into several semantic section components, while keeping the actual user-facing sentences and CTA labels authored in page.tsx
practical pattern confirmed in AI Crew lost-section follow-up: move RevealOnScroll, background-image rendering, card geometry, and CTA button implementation into src/components/sections/<section>.tsx, then let page.tsx read as <LostProblemCard>, <LostProblemTitle>, <LostProblemBody>, <LostWhitepaperCard>, and <LostWhitepaperAction href={...}>...
When the user wants a cleaner route-level static page but also wants reusable section/UI extraction first, prefer this order:
Primitive-extraction PR first
origin/mainsrc/components/sections/**src/app/.../page.tsx yet in this PRRoute-localization PR second
origin/main after the primitive PR mergessrc/app/<route>/page.tsx<SectionPrimitive><h2>マーケティング文句</h2><p>説明文</p>This staged approach is especially useful when the current page has both:
In fast-moving repos, do not keep executing a long static-page refactor plan against a worktree if origin/main advanced materially while you were preparing or partially editing a previous attempt.
If a prerequisite PR merged and advanced origin/main:
Do not assume an earlier fresh worktree is still fresh enough after upstream movement.
If the page is mostly static and currently imports:
page-specific sections componentpage-specific content modulethen refactor to:
page.tsxfunction <Page>Sections() inside page.tsx for the page body if that improves readabilityThis keeps page.tsx readable without forcing everything into one giant JSX return.
When extracting top-page-specific UX-semantic components, prefer names that describe the user-facing choice being presented rather than lower-level implementation structure.
Practical example from top-page solution cards:
SolutionChoice*TopPageSolutionPath*Good examples:
SolutionChoiceCardSolutionChoiceHeaderSolutionChoiceBadgeSolutionChoiceTitleSolutionChoiceSubtitleSolutionChoiceDescriptionSolutionChoiceActionWhy:
TopPage in every exported symbol adds noiseChoice reads more clearly than Path for a landing-page UI where the user is choosing between alternativespage.tsx read more like content structure and less like implementation plumbingImportant nuance learned from PR follow-up:
src/content/<page>.ts with a giant top-level const pageContent = { ... } inside page.tsx and consider the job done.markup + content registry, treat that as valid feedback and keep collapsing the distance between copy and JSX.Practical follow-up from src/app/solutions/ai-dashi/page.tsx refactor work:
function <Page><Section>NameSection()data blob first, JSX second, not changing page behavior<SupportSection /> and <FlowSection /> while the section's headings, prose-heavy arrays, CTA copy, and JSX structure are all re-hidden inside those helpersIf only one subsection needs useState / client behavior:
page.tsx as a server componentThis worked well for the top page roadmap tab section.
Practical service-preview pattern learned from /t/services/acp follow-up:
"use client"page.tsxsrc/components/sections/<page>-service-page.tsxsrc/components/sections/<page>-feature-browser.tsxpage.tsx"use client""use client"/t/services/acp, QueryPie ACPができること belonged in the route, while the category tab/prev-next feature browser was split into its own client component; leaving both concerns together in one "use client" page-section module made the route less readable and widened the client boundary unnecessarilyImportant App Router build pitfall learned from follow-up refactors:
createContext, useContext, useState, useEffect, or other client hooks, mark that extracted file with "use client"createContext in an unmarked component imported by page.tsxYou're importing a module that depends on createContext into a React Server Component moduleSolutionChoiceCard used createContext/useContext, so the component file itself needed "use client" after being imported from src/app/page.tsxImportant client-boundary scope rule learned from /t/services/acp follow-up review:
"use client" file if that file also contains many static layout primitivespage.tsxsections/*.tsx file marked "use client" only because one feature browser/tabbed widget inside that file uses useState, while the same file also exports hero wrappers, section shells, and other static primitivesone opaque interactive section call instead of a page with visible authored section ownershipQueryPie ACPができること) is rendered inside the client widget file rather than in page.tsx, treat the route-local refactor as only partialpage.tsx only renders something like <FeatureBrowser categories={...} /> for a major marketing section, inspect whether the section heading and surrounding authored narrative should move back into the route while the tab/browser logic remains extractedisExternalHref style helpersLink usage over click-wrapper + router.push when the card can simply be a link.page.tsx, delete or stop using the old page-specific source files as part of the same change. Otherwise the refactor is only a copy, not a move.src/components/sections/<page>-sections.tsxsrc/content/<page>.tsnot-found.tsx, floating guides, or tests that still import old CTA constants.When the repository already contains structure tests that read exact source files such as:
src/content/top-page.tssrc/content/home.tssrc/components/sections/top-page-sections.tsxsrc/components/sections/home-page-sections.tsxprefer this order:
Why:
A reusable pattern is to extend test helpers with functions like:
export function sourceExists(relativePath) { ... }
export function readFirstExistingSource(relativePaths) { ... }
Then assertions can prefer old canonical paths first and fall back to new route-local paths:
const topPageDataSource = readFirstExistingSource([
"src/content/top-page.ts",
"src/app/page.tsx",
]);
Use this for tests that should verify equivalence of:
Important nuance:
/t/*/t/*When migrating a legacy corp-web-contents company/info page into a local preview route such as /t/certifications in corp-web-japan:
src/app/t/<slug>/page.tsxpage.tsxSiteHeader and SiteFooter in the route, and keep the main readable page structure in that file/t route such as /certifications already exists as a redirect surface, do not replace or widen that scope unless the user explicitly asks; adding the local /t/... preview page does not itself authorize changing the public redirect behavior../corp-web-contents/public/..., do not assume the preview route prefix /t must also appear in the final public asset path. If the user asks for a cleaner or shorter asset root, use a direct path such as public/<slug>/... and update the page image references accordingly. Example learned from certifications work: the page route stayed /t/certifications, while the accepted asset root became public/certifications/* with image URLs like /certifications/<file>.font-size, line-height, font-weight, and letter-spacingborder-radius, title size, detail size, and grid geometryborder-radius, font-size, font-weight, line-height, padding, and border colorgetComputedStyle() and getBoundingClientRect() for the exact elements, then translate those values into the current repo's Tailwind classes or arbitrary values. This is especially effective when the goal is “match the rendered result” rather than “approximate the structure”.Important source-of-truth rule learned from /t/certifications work:
../corp-web-contents primarily as the content/composition source../corp-web-app as the style/widget implementation source when the legacy MDX page uses shared foundation/widget components such as StaticHeader, CenterSection, ButtonLink, or Certificationscorp-web-app widget CSS/modules and design-token variables../corp-web-app/src/components/widget/certifications/certifications.component.tsx and certifications.module.css provided the real grid, radius, and card spacing behavior for the certifications page, while corp-web-contents/pages/company/certifications/ja/content.mdx only described the content structure and which widget was usedImportant browser-verification rule learned from the same work:
querypie.com rendering, open both the live URL and the preview URL in the browser and inspect the actual rendered result before deciding layout structure/t/news or /t/about-us/ja/company/certifications, the live page did not have a left company-info sidebar inside the main content area even though a sidebar seemed plausible from other local preview patterns; the correct follow-up was to remove the preview sidebar and match the live single-column 1200px content span insteadUse browser computed-style measurement for high-fidelity follow-up polish:
font-size, font-weight, line-height, letter-spacing, padding, border, border-radius, width, and left/right alignment against the header spanThis pattern is especially useful for one-off company/info migrations where the user wants:
../corp-web-contentsquerypie.com live page, not just the content inventoryWhen the user asks for the local /t/... route to match the live querypie.com/ja/company/... page visually, do not assume the intended structure from neighboring local preview routes such as /t/news.
Instead:
Practical lessons from /t/certifications follow-up work:
getBoundingClientRect, getComputedStyle) to verify:
font-size, line-height, and font-weightborder-radiusmax-width while still looking too narrow because inner px-* padding shifts the real content start/end inward.16.875px, 26.25px, 5.625px, or 9.375px when the live site uses them, rather than rounding everything to the nearest Tailwind default tokenA practical follow-up learned from /t/certifications work:
max-w-[1200px] to the header span is not sufficient by itselfpx-6 lg:px-10, the effective content edges become narrower than the header span even though the outer section width still measures 1200pxleft=40 right=1240 width=1200, but the visible title/grid content started at x=80 and ended at x=1200 because of the extra inner paddingWhen the user asks for the content area to align with the header edges:
h1, card grid, or primary section content)max-widthFor typical corp-web-japan desktop layouts, a useful sanity check is:
left=40, right=1240, width=1200Minimum verification for this refactor class:
npm run typecheck
If the user explicitly wants more verification or the change affects broader rendering risk, then also run:
npm run test:ci
npm run build
But for this user, prefer the lightest meaningful verification first.
page.tsx is the primary readable implementation surfacenpm run typecheck passes