From 633207b0ab52b1e9b665ad2fbf3a51ea7a2d0fcb Mon Sep 17 00:00:00 2001 From: ClarusIubar Date: Sat, 13 Jun 2026 11:13:38 +0900 Subject: [PATCH] refactor: clean app shell map offsets --- ...TSK-012-06-app-shell-css-offset-cleanup.md | 56 ++++ src/hooks/{ => map}/useTourismMapState.ts | 2 +- src/hooks/useMapDomainState.ts | 2 +- src/index.css | 44 ++- src/styles/refinements.css | 277 ------------------ test/unit/second-uiux-audit-baseline.test.ts | 12 +- 6 files changed, 100 insertions(+), 293 deletions(-) create mode 100644 reports/completion/TSK-012-06-app-shell-css-offset-cleanup.md rename src/hooks/{ => map}/useTourismMapState.ts (95%) diff --git a/reports/completion/TSK-012-06-app-shell-css-offset-cleanup.md b/reports/completion/TSK-012-06-app-shell-css-offset-cleanup.md new file mode 100644 index 0000000..ad1382c --- /dev/null +++ b/reports/completion/TSK-012-06-app-shell-css-offset-cleanup.md @@ -0,0 +1,56 @@ +# TSK-012-06 App Shell CSS Offset Cleanup Completion + +## Scope + +- Scope-ID: `TSK-012-06` +- Issue: `#410` +- Parent Issue: `#404` +- Branch: `app-shell-css-offset-cleanup` +- Status: local validation complete, PR pending + +## Responsibility Map + +- `src/index.css`: app-shell map flow canonical rules and active layout ownership. +- `src/styles/refinements.css`: legacy visual refinements only; no longer owns map filter/surface/back-button layout overrides. +- `src/hooks/map/useTourismMapState.ts`: map-owner hook locality for KTO tourism UI state. +- `test/unit/second-uiux-audit-baseline.test.ts`: source guard for the removed legacy override selectors. + +## Dependency Direction + +- App shell owns header/subnav/body slots. +- Map stage owns map content flow inside the app-shell body. +- `refinements.css` may style residual visual details but must not override app-shell map flow with `!important`. +- Map domain hooks remain owner-local instead of increasing root `src/hooks` sprawl. + +## Test Seam + +- Source-quality unit test verifies that removed utility/back-button selectors do not return. +- E2E app-shell and critical UI flows verify map filters, drawer, bottom navigation, and KTO toggle positioning. + +## Scope Map + +- Included: CSS offset cleanup, legacy app back/utility selector removal, map-surface canonical flow, hook locality correction triggered by #409. +- Excluded: UI copy changes, API/DB/OAuth changes, new KTO provider behavior, broad visual redesign. + +## Architecture Risk + +- `src/styles/refinements.css` still contains unrelated historical `!important` refinements outside this child scope. +- This PR removes only the app-shell map/back-button/utility overlap covered by #410 and leaves unrelated visual debt for separate issues. + +## Validation + +- `npm.cmd run check:numeric-literals`: passed +- `npm.cmd run lint`: passed +- `npm.cmd run typecheck`: passed +- `npm.cmd run test:unit`: passed +- `npm.cmd run test:integration`: passed +- `npm.cmd run test:regression`: passed +- `npm.cmd run test:e2e`: passed +- `npm.cmd run build`: passed +- `git diff --check`: passed, CRLF warnings only + +## Remote Evidence + +- PR: pending +- Merge SHA: pending +- CI URL: pending diff --git a/src/hooks/useTourismMapState.ts b/src/hooks/map/useTourismMapState.ts similarity index 95% rename from src/hooks/useTourismMapState.ts rename to src/hooks/map/useTourismMapState.ts index 8876d5c..d726313 100644 --- a/src/hooks/useTourismMapState.ts +++ b/src/hooks/map/useTourismMapState.ts @@ -1,5 +1,5 @@ /* - * File: useTourismMapState.ts + * File: hooks/map/useTourismMapState.ts * Purpose: Store map-local KTO tourism overlay UI state. * Primary Responsibility: Own the tourism visibility toggle, selected tourism item id, and sheet expansion state. * Design Intent: Keep KTO map overlay state local to map domain instead of encoding it in route URLs before the flow is proven stable. diff --git a/src/hooks/useMapDomainState.ts b/src/hooks/useMapDomainState.ts index a02b3a3..d163172 100644 --- a/src/hooks/useMapDomainState.ts +++ b/src/hooks/useMapDomainState.ts @@ -1,6 +1,6 @@ import { useMapCategoryState } from './useMapCategoryState'; import { useRoutePreviewState } from './useRoutePreviewState'; -import { useTourismMapState } from './useTourismMapState'; +import { useTourismMapState } from './map/useTourismMapState'; export function useMapDomainState() { return { diff --git a/src/index.css b/src/index.css index 6b9d0dc..27b252a 100644 --- a/src/index.css +++ b/src/index.css @@ -161,17 +161,6 @@ textarea { pointer-events: none; } -.phone-shell__utility-slot { - position: absolute; - top: var(--utility-slot-top); - right: var(--utility-slot-right); - z-index: var(--layer-utility); - display: inline-flex; - align-items: flex-start; - gap: var(--utility-gap); - pointer-events: auto; -} - .phone-shell__body { position: absolute; inset: var(--app-header-height) 0 0 0; @@ -2576,6 +2565,39 @@ textarea { width: 100%; height: 1px; } + +/* TSK-012 app-shell map flow canonical rules */ +.phone-shell--map .map-stage { + position: relative; + display: flex; + flex-direction: column; + gap: 8px; + width: 100%; + height: 100%; + min-height: 0; + padding: 12px 12px calc(78px + env(safe-area-inset-bottom)); +} + +.phone-shell--map .map-stage .map-surface-frame { + position: relative; + inset: auto; + flex: 1 1 auto; + min-height: 0; + height: auto; + margin: 0 0 6px; + border-radius: 24px; +} + +.app-shell__sub-nav-slot .map-stage-subnav, +.app-shell__sub-nav-slot .map-filter-strip, +.app-shell__sub-nav-slot .map-filter-strip .chip-row { + min-width: 0; +} + +.app-shell__sub-nav-slot .map-filter-strip { + position: relative; + inset: auto; +} @font-face { font-family: 'Pretendard'; src: url('./assets/fonts/Pretendard-Regular.woff2') format('woff2'); diff --git a/src/styles/refinements.css b/src/styles/refinements.css index 7629643..8248660 100644 --- a/src/styles/refinements.css +++ b/src/styles/refinements.css @@ -1,217 +1,4 @@ -/* 2026-03-18 prototype reset */ -.phone-shell--map { - display: block !important; -} - -.map-stage { - position: relative !important; - display: flex !important; - flex-direction: column !important; - gap: 10px !important; - height: 100% !important; - padding: 14px 14px var(--bottom-nav-offset) !important; - background: linear-gradient(180deg, rgba(255, 248, 251, 0.92), rgba(255, 253, 248, 0.94)) !important; -} - -.map-stage__gradient { - display: none !important; -} - -.map-stage__header, -.map-filter-strip, -.floating-notice, -.floating-status { - position: relative !important; - inset: auto !important; - left: auto !important; - right: auto !important; - top: auto !important; - bottom: auto !important; - z-index: 2 !important; -} - -.map-stage__header { - display: flex !important; - align-items: flex-start !important; - justify-content: space-between !important; - gap: 10px !important; -} - -.map-stage__brand { - max-width: 150px !important; - padding: 8px 10px !important; - border-radius: 20px !important; - background: rgba(255, 252, 249, 0.96) !important; - box-shadow: 0 8px 18px rgba(255, 143, 183, 0.08) !important; -} - -.map-stage__summary { - display: flex !important; - align-items: center !important; - justify-content: flex-end !important; - gap: 6px !important; - flex-wrap: wrap !important; -} - -.map-stage__headline { - margin: 0 !important; - font-size: 10px !important; - line-height: 1.42 !important; -} - -.map-filter-strip { - display: flex !important; - align-items: center !important; - gap: 8px !important; - overflow-x: auto !important; - overflow-y: hidden !important; - padding: 0 !important; - background: transparent !important; - border: 0 !important; - box-shadow: none !important; - scrollbar-width: none !important; -} - -.map-filter-strip::-webkit-scrollbar, -.map-filter-strip .chip-row::-webkit-scrollbar { - display: none !important; -} - -.map-filter-strip .chip-row { - display: flex !important; - flex-wrap: nowrap !important; - gap: 8px !important; - overflow-x: auto !important; - overflow-y: hidden !important; - width: 100% !important; - padding: 0 !important; -} - -.map-filter-chip { - flex: 0 0 auto !important; - padding: 9px 12px !important; - background: rgba(255, 255, 255, 0.96) !important; -} - -.floating-notice, -.floating-status { - padding: 8px 10px !important; - margin-top: -2px !important; - border-radius: 18px !important; - font-size: 12px !important; -} - -.map-surface-frame { - position: relative !important; - inset: auto !important; - flex: 1 1 auto !important; - min-height: 0 !important; - height: auto !important; - margin-top: 2px !important; - border-radius: 28px !important; - overflow: hidden !important; - background: rgba(255, 255, 255, 0.88) !important; - box-shadow: 0 12px 24px rgba(255, 143, 183, 0.1) !important; -} - -.map-floating-controls { - top: 10px !important; - right: 10px !important; -} - -.map-locate-button { - padding: 8px 12px !important; - font-size: 12px !important; -} - -.map-location-pill { - left: 12px !important; - right: 12px !important; - bottom: 12px !important; - padding: 9px 11px !important; - font-size: 10px !important; -} - -.place-drawer { - left: 14px !important; - right: 14px !important; - bottom: var(--bottom-nav-offset) !important; - margin: 0 !important; - border-radius: 28px 28px 24px 24px !important; - background: rgba(255, 252, 249, 0.99) !important; -} - -.place-drawer--peek { - bottom: calc(var(--bottom-nav-offset) + var(--map-sheet-tab-gap)) !important; - height: 35% !important; -} - -.place-drawer--half { - bottom: 0 !important; - height: var(--map-sheet-half-height) !important; -} - -.place-drawer--partial { - bottom: calc(var(--bottom-nav-offset) + var(--map-sheet-tab-gap)) !important; - height: 35% !important; -} - -.place-drawer--full { - bottom: 0 !important; - height: var(--map-sheet-full-height) !important; -} - -.bottom-nav { - gap: 8px !important; - padding: - var(--bottom-nav-padding-y) - 12px - calc(var(--bottom-nav-safe-padding-y) + env(safe-area-inset-bottom)) !important; -} - -.bottom-nav__item { - min-height: 48px !important; -} - -input.visually-hidden[type='file'] { - display: none !important; -} - -@media (max-width: 430px) { - .map-stage { - padding: 12px 12px calc(82px + env(safe-area-inset-bottom)) !important; - } - - .map-stage__brand { - max-width: 156px !important; - padding: 9px 10px !important; - } - - .map-stage__headline { - font-size: 10px !important; - } - - .map-surface-frame { - height: auto !important; - } - - .place-drawer--partial { - bottom: calc(var(--bottom-nav-offset) + var(--map-sheet-tab-gap)) !important; - height: 34% !important; - } - - .place-drawer--full { - bottom: 0 !important; - height: 58% !important; - } -} - - /* 2026-03-18 drawer over map fix */ -.map-surface-frame { - margin-bottom: 68px !important; -} - .place-drawer { z-index: 120 !important; } @@ -483,12 +270,6 @@ input.visually-hidden[type='file'] { } } -.phone-shell--map .phone-shell__utility-slot .feedback-button, -.phone-shell--map .phone-shell__utility-slot .notification-bell { - background: rgba(255, 252, 249, 0.98) !important; - border-color: rgba(255, 197, 217, 0.62) !important; - box-shadow: 0 12px 24px rgba(255, 143, 183, 0.12) !important; -} /* 2026-03-19 my page header action fix */ .panel-header__actions { flex: 0 0 auto !important; @@ -885,32 +666,6 @@ input.visually-hidden[type='file'] { box-shadow: 0 0 0 4px rgba(255, 206, 222, 0.32) !important; } - - -/* 2026-03-19 explicit app back button */ -.app-back-button { - position: absolute !important; - right: 18px !important; - bottom: var(--bottom-nav-offset) !important; - z-index: 160 !important; - display: inline-flex !important; - align-items: center !important; - justify-content: center !important; - width: 46px !important; - height: 46px !important; - padding: 0 !important; - border-radius: 999px !important; - border: 1px solid rgba(255, 127, 168, 0.22) !important; - background: rgba(255, 249, 252, 0.96) !important; - box-shadow: 0 10px 20px rgba(74, 44, 63, 0.08) !important; - color: var(--ink) !important; -} - -.app-back-button span:first-child { - font-size: 18px !important; - line-height: 1 !important; -} - /* 2026-03-19 feed card hierarchy alignment */ .review-card__top--feed { align-items: flex-start !important; @@ -1360,11 +1115,6 @@ body { font-size: var(--type-sm) !important; } -.app-back-button { - bottom: calc(var(--bottom-nav-offset) + 24px) !important; - right: 14px !important; -} - .map-route-preview-card { bottom: calc(var(--bottom-nav-offset) + 28px) !important; } @@ -1575,23 +1325,6 @@ body { background: linear-gradient(180deg, #fff7fa, #ffeef4) !important; border-color: rgba(255, 93, 146, 0.34) !important; } - -/* 2026-03-21 draggable back button */ -.app-back-button { - position: fixed !important; - right: auto !important; - bottom: auto !important; - touch-action: none !important; - user-select: none !important; - cursor: grab !important; -} - -.app-back-button.is-dragging { - cursor: grabbing !important; - box-shadow: 0 16px 28px rgba(74, 44, 63, 0.14) !important; -} - - .review-card__top--stamp-log { justify-content: space-between !important; gap: 16px !important; @@ -1627,10 +1360,6 @@ body { margin-top: 0 !important; } -.map-stage .map-surface-frame { - margin-bottom: 8px !important; -} - .map-drawer-teaser { padding: 10px 14px 12px !important; } @@ -1641,12 +1370,6 @@ body { padding: 12px 12px calc(78px + env(safe-area-inset-bottom)) !important; } - .map-stage .map-surface-frame { - margin-bottom: 6px !important; - min-height: 340px !important; - border-radius: 24px !important; - } - .map-drawer-teaser { gap: 8px !important; padding: 10px 12px 10px !important; diff --git a/test/unit/second-uiux-audit-baseline.test.ts b/test/unit/second-uiux-audit-baseline.test.ts index 4f4d967..cb9192c 100644 --- a/test/unit/second-uiux-audit-baseline.test.ts +++ b/test/unit/second-uiux-audit-baseline.test.ts @@ -51,11 +51,17 @@ describe('TSK-012-01 second UI/UX audit baseline', () => { expect(sourceExists('src/tourismTypes.ts')).toBe(true); }); - it('records the remaining map overlay and surface CSS cleanup debt', () => { - const css = `${readSource('src/index.css')}\n${readSource('src/styles/refinements.css')}`; + it('records that app-shell map offset cleanup no longer depends on legacy override selectors', () => { + const indexCss = readSource('src/index.css'); + const refinementsCss = readSource('src/styles/refinements.css'); + const css = `${indexCss}\n${refinementsCss}`; expect(css.match(/\.map-filter-strip/g)?.length ?? 0).toBeGreaterThan(1); expect(css.match(/\.map-surface-frame/g)?.length ?? 0).toBeGreaterThan(1); - expect(css).toContain('.phone-shell--map .phone-shell__utility-slot'); + expect(css).not.toContain('.phone-shell--map .phone-shell__utility-slot'); + expect(css).not.toContain('.app-back-button'); + expect(refinementsCss).not.toContain('.map-filter-strip'); + expect(refinementsCss).not.toContain('.map-surface-frame'); + expect(indexCss).toContain('TSK-012 app-shell map flow canonical rules'); }); });