From a0b14dfca13ca4425376fec89025caf5168e0738 Mon Sep 17 00:00:00 2001 From: Mina Choi Date: Wed, 30 Sep 2026 17:13:24 +0900 Subject: [PATCH] =?UTF-8?q?[fix]=20solution/site,shared:=20=EC=B2=AB=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=EC=9D=84=20=EB=AA=A9=EC=97=85=20=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=EB=A1=9C=20=E2=80=94=20=EC=BA=90=EC=B9=98=ED=94=84?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=A6=88=20=ED=95=98=EB=82=98=20=EA=B3=A0?= =?UTF-8?q?=EC=A0=95,=20=EC=95=84=EB=9E=98=20=EC=A4=84=20=EC=88=9C?= =?UTF-8?q?=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /s/stay 목업은 캐치프레이즈(tagline) 한 줄이 고정이고 그 아래 줄에서 문구가 돈다 (2026-09-11 "메인 모토는 고정, 그 아래 변동되는 모토"). 템플릿은 큰 줄에 공통 문구 10개를 돌리고 있었고, 앞 커밋의 소문구는 "명소까지 거리" 로 목업과 달랐다. - sections/HeroCatchphrase.tsx: HeroCatchphrase = tagline 고정(없으면 공통 문구 중 사이트별 하나), HeroRotator = narrative.catchphrases 를 일반→계절→일반→월→일반→날씨 순서로 6초마다, 줄 높이는 가장 긴 문구 - 9종 Hero: 소문구 자리를 HeroRotator 로. kit/heroSub.ts 삭제 - shared: catchphrases 항목에 season·month·weather - 테스트: 고정·폴백·비숙박·빈 순환·순서·날씨 분류 tsc·eslint 통과 · vitest 127 passed · 9종 모바일 캡처, 라운드·부티크 6초 간격 순환·높이 고정 확인 Co-Authored-By: Claude Opus 5.5 --- docs/TEMPLATE_DESIGN.md | 5 +- solution/shared/src/types/site-payload.ts | 13 +- solution/site/src/layouts/bigtype/Hero.tsx | 6 +- solution/site/src/layouts/boutique/Hero.tsx | 6 +- solution/site/src/layouts/cinema/Hero.tsx | 8 +- solution/site/src/layouts/coral/Hero.tsx | 6 +- solution/site/src/layouts/graphic/Hero.tsx | 29 +--- solution/site/src/layouts/kit/heroSub.ts | 10 -- solution/site/src/layouts/minimal/Hero.tsx | 6 +- solution/site/src/layouts/paper/Hero.tsx | 6 +- solution/site/src/layouts/pine/Hero.tsx | 6 +- solution/site/src/layouts/round/Hero.tsx | 6 +- .../site/src/sections/HeroCatchphrase.tsx | 144 ++++++++++++++---- .../site/src/sections/hero-catchphrase.css | 14 +- .../src/sections/hero-catchphrase.test.tsx | 61 ++++++-- 15 files changed, 212 insertions(+), 114 deletions(-) delete mode 100644 solution/site/src/layouts/kit/heroSub.ts diff --git a/docs/TEMPLATE_DESIGN.md b/docs/TEMPLATE_DESIGN.md index 6125d3d..37fa678 100644 --- a/docs/TEMPLATE_DESIGN.md +++ b/docs/TEMPLATE_DESIGN.md @@ -85,7 +85,10 @@ ## 6. 값 보여주기 -- **첫 화면은 캐치프레이즈 → 그 아래 소문구.** 소문구는 사장님 문구, 없으면 가장 가까운 명소까지 거리(`kit/heroSub.ts`) +- **첫 화면은 캐치프레이즈 한 줄(고정) → 그 아래 순환 문구.** `/s/stay` 목업과 같다(`sections/HeroCatchphrase.tsx`) + - 캐치프레이즈 = `narrative.tagline`. 없으면 공통 문구 10개 중 사이트마다 정해진 하나 + - 순환 = `narrative.catchphrases`, 일반 → 계절 → 일반 → 월 → 일반 → 날씨 순서, 6초. 없으면 줄을 두지 않는다 + - 두 값은 COPY 잡이 소개문과 함께 만든다(fact `tagline` · `catchphrases`, `services/copy_steps.py`) - 첫 화면에 태그·칩(‘주차 가능’, 지역 말풍선)을 두지 않는다 - 부티크 첫 화면은 모바일도 문(아치) 모양이다. 전체화면 사진으로 되돌리지 않는다 diff --git a/solution/shared/src/types/site-payload.ts b/solution/shared/src/types/site-payload.ts index eebf9c8..144df9d 100644 --- a/solution/shared/src/types/site-payload.ts +++ b/solution/shared/src/types/site-payload.ts @@ -322,10 +322,19 @@ export interface RouteEntry { } export interface Narrative { - /** 히어로 둘째 줄에서 순환할 문구. */ + /** 히어로 둘째 줄에서 순환할 문구. 첫째 줄(캐치프레이즈)은 `tagline` 하나로 고정이다. */ catchphrases?: { version: 1; - items: {text: string; kind?: 'general' | 'season' | 'month' | 'weather'}[]; + items: { + text: string; + kind?: 'general' | 'season' | 'month' | 'weather'; + /** kind=season 일 때 봄·여름·가을·겨울. */ + season?: string; + /** kind=month 일 때 1~12. */ + month?: number; + /** kind=weather 일 때 맑음·흐림·비·눈. */ + weather?: string; + }[]; }; heroHeadline?: string; diff --git a/solution/site/src/layouts/bigtype/Hero.tsx b/solution/site/src/layouts/bigtype/Hero.tsx index 2fb7df6..45b64b1 100644 --- a/solution/site/src/layouts/bigtype/Hero.tsx +++ b/solution/site/src/layouts/bigtype/Hero.tsx @@ -1,8 +1,7 @@ import {PlaceCategory} from '@o2o/shared'; -import {HeroCatchphrase} from '@site/sections/HeroCatchphrase'; +import {HeroCatchphrase, HeroRotator} from '@site/sections/HeroCatchphrase'; import {useSite} from '@site/lib/site-context'; import {bookingActionLabel, bookingLinks, isSectionEnabled, stayBookingView} from '@site/lib/derive'; -import {heroSub} from '../kit/heroSub'; import {BOOK_EVENT} from '../paper/Rooms'; import {Weather} from './Weather'; @@ -13,7 +12,6 @@ export function Hero() { .filter((image) => image.alt?.trim()) .sort((a, b) => Number(b.isPrimary) - Number(a.isPrimary)); const fixed = narrative.tagline ?? narrative.heroSubline; - const sub = heroSub(payload); const lodging = place.category === PlaceCategory.LODGING; const stay = lodging ? stayBookingView(payload) : null; const link = bookingLinks(payload)[0]; @@ -31,7 +29,7 @@ export function Hero() { 오늘의 문장

{(lodging || fixed) &&

{lodging ? : fixed}

} - {lodging && sub &&

{sub}

} +
{stay ? (
{isSectionEnabled(payload, 'weather') && ( diff --git a/solution/site/src/layouts/pine/Hero.tsx b/solution/site/src/layouts/pine/Hero.tsx index 6c749f5..900616f 100644 --- a/solution/site/src/layouts/pine/Hero.tsx +++ b/solution/site/src/layouts/pine/Hero.tsx @@ -1,8 +1,7 @@ import {PlaceCategory} from '@o2o/shared'; -import {HeroCatchphrase} from '@site/sections/HeroCatchphrase'; +import {HeroCatchphrase, HeroRotator} from '@site/sections/HeroCatchphrase'; import {useSite} from '@site/lib/site-context'; import {bookingActionLabel, bookingLinks, isSectionEnabled, stayBookingView} from '@site/lib/derive'; -import {heroSub} from '../kit/heroSub'; import {Car} from '../paper/Car'; import {BOOK_EVENT} from '../paper/Rooms'; import {Weather} from './Weather'; @@ -19,7 +18,6 @@ export function Hero() { const fixed = narrative.tagline ?? narrative.heroSubline; const lodging = place.category === PlaceCategory.LODGING; const region = place.addressLocality ?? place.addressRegion; - const sub = heroSub(payload); const booking = bookingLinks(payload)[0]; const stay = lodging ? stayBookingView(payload) : null; @@ -57,7 +55,7 @@ export function Hero() { ) : ( fixed &&

{fixed}

)} - {lodging && sub &&

{sub}

} +
{stay ? (