From bbc7ee562e5365f3cb2dbb35eac493d60873ca4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=88=98=ED=98=84?= Date: Wed, 11 Nov 2026 11:47:26 +0900 Subject: [PATCH] fix(storybook): refine SW notice placement and icon size --- WORKING_CONTEXT.md | 7 ++++++- src/components/story-page.css | 26 +++++++++++--------------- src/components/story-page.tsx | 32 ++++++++++++++++---------------- src/sw/sw-story-notice.tsx | 11 +++++------ 4 files changed, 38 insertions(+), 38 deletions(-) diff --git a/WORKING_CONTEXT.md b/WORKING_CONTEXT.md index 6b16a35..ab6ba20 100644 --- a/WORKING_CONTEXT.md +++ b/WORKING_CONTEXT.md @@ -1,6 +1,11 @@ # Working Context -## 2026-11-11 — Storybook 페이지 위계·여백 공통화 (미커밋) +## 2026-11-11 — SW 공통 안내 문구를 H1 아래로 이동 (미커밋) + +- `withSWNotice`(src/sw/sw-story-notice.tsx)는 `StoryNoticeContext`로 문구만 전달하고, 공통 `StoryPageHeader`가 H1 바로 아래(설명 앞)에 `StoryNotice`를 렌더링. 별도 페이지 프레임(`.sb-page-notice`)은 제거. +- 안내 문구: H1 아래 8px, Page Header description 글꼴·보조색, 아이콘 14px(텍스트와 6px, 첫 줄 세로 중앙), 박스 없음. 첫 섹션까지는 header margin 24px. Context 제공자가 없는 Web/Chart/OrgChart 등은 렌더 결과 변화 없음. + +## 2026-11-11 — Storybook 페이지 위계·여백 공통화 (ff55a80) - 공통 프레임 `src/components/story-page.css`: 위·아래 여백 16–24px, H1 28/36/700 margin 0, 설명 Page Header description 토큰(8px·보조색), 제목→첫 내용·섹션 사이 24px(`--sb-page-section-gap`, header margin이 다음 블록 margin과 상쇄), H2 16/24/700(`--sb-page-section-title-*` = dialog body 토큰), 섹션 제목 구분선 8 + 12px. 좌우 여백·SW 기준 폭(2040/1125/787)은 그대로. - `StorySectionGrid`(story-page.tsx) 추가: 나란히 놓인 섹션의 row gap을 grid가 소유. Chart 7개·Toggle이 사용. `StateBoard` 라벨 구분선 제거, 행 간격 40 → 24px. diff --git a/src/components/story-page.css b/src/components/story-page.css index 8835ba7..0987f0b 100644 --- a/src/components/story-page.css +++ b/src/components/story-page.css @@ -81,28 +81,24 @@ .sb-page .sb-page-section-grid { display: grid; gap: var(--sb-page-section-gap) 24px; margin-top: var(--sb-page-section-gap); } .sb-page .sb-page-section-grid > .sb-page-section { margin-top: 0; } -/* Notice above a page title (StoryNotice). Reuses .sb-page / .sb-page-inner (width and side margin are not redefined). - The frame drops its bottom gutter and pulls the next page up so the notice sits 24px above the page title. */ -.sb-page.sb-page-notice { - padding-bottom: 0; - margin-bottom: calc(24px - var(--sb-page-gutter)); -} +/* Group notice under the page title (StoryNotice inside StoryPageHeader): 8px below the H1, same type and color as the + Page Header description (one level below the title), no box. The header's own bottom margin keeps the first section 24px below. */ .sb-page .sb-notice { display: flex; align-items: flex-start; - gap: 8px; - margin: 0; - font-size: var(--hm-label-font-size); - line-height: var(--hm-label-line-height); + gap: 6px; + margin: var(--hm-page-header-description-gap) 0 0; + font-size: var(--hm-page-header-description-font-size); + line-height: var(--hm-page-header-description-line-height); font-weight: var(--hm-input-font-weight); - color: var(--hm-sem-fg-primary); + color: var(--hm-page-header-description-color); overflow-wrap: anywhere; } .sb-page .sb-notice-icon { flex: none; - width: 20px; - height: 20px; - /* centers the icon on the first text line even when the line height differs from 20px */ - margin-block: calc((var(--hm-label-line-height) - 20px) / 2); + width: 14px; + height: 14px; + /* centers the 14px icon on the first text line (20px line height → 3px above and below) */ + margin-block: calc((var(--hm-page-header-description-line-height) - 14px) / 2); } .sb-page .sb-notice-text { min-width: 0; } diff --git a/src/components/story-page.tsx b/src/components/story-page.tsx index bf46ac4..9fbd422 100644 --- a/src/components/story-page.tsx +++ b/src/components/story-page.tsx @@ -1,4 +1,4 @@ -import { createContext, type ReactNode } from "react" +import { createContext, useContext, type ReactNode } from "react" import "./story-page.css" // Storybook presentation only (not part of @hm-design/ui): the common frame of a representative Story page. @@ -13,35 +13,35 @@ export function StoryPage({ className, children }: { className?: string; childre return
{children}
} +// Group notice shown directly under the page title (H1 → notice → description). A Story group opts in through a decorator +// that provides this context (e.g. src/sw/sw-story-notice.tsx); without a provider nothing is rendered. +export const StoryNoticeContext = createContext(null) + // Representative (sidebar) Stories omit `kicker`: the sidebar path already names the category. // Hidden verification Stories keep passing it so their page structure stays unchanged. export function StoryPageHeader({ kicker, title, description }: { kicker?: string; title: string; description?: ReactNode }) { + const notice = useContext(StoryNoticeContext) return (
{kicker ?

{kicker}

: null}

{title}

+ {notice ? {notice} : null} {description ?

{description}

: null}
) } -// Notice row (icon + text) shown above a Story page title. Reusable by any Story group through a decorator -// (e.g. src/sw/sw-story-notice.tsx); nothing renders it unless a group opts in. It owns a page frame whose Inner is the -// same as StoryPage's, and cancels its own bottom gutter so the page title below stays where it was. -// Icon: Group 1171290812.svg (circle + "!"), colors via currentColor. +// Notice row (icon + text) under the page title, one level below it (Page Header description type and color). +// No box, background or border. Icon: Group 1171290812.svg (circle + "!"), colors via currentColor. export function StoryNotice({ children }: { children: ReactNode }) { return ( -
-
-

- - {children} -

-
-
+

+ + {children} +

) } diff --git a/src/sw/sw-story-notice.tsx b/src/sw/sw-story-notice.tsx index aa6e622..796268c 100644 --- a/src/sw/sw-story-notice.tsx +++ b/src/sw/sw-story-notice.tsx @@ -1,8 +1,8 @@ import type { Decorator } from "@storybook/react-vite" -import { StoryNotice } from "@/components/story-page" +import { StoryNoticeContext } from "@/components/story-page" -// Storybook presentation only. Common notice shown above the page title of every SW Story (SW/* groups only): -// each SW stories file opts in with `decorators: [withSWNotice]`, so other menus never render it. +// Storybook presentation only. Common notice of every SW Story page (SW/* groups only), shown directly under the page +// title by StoryPageHeader: each SW stories file opts in with `decorators: [withSWNotice]`, so other menus never render it. // Hidden verification / internal Stories are skipped; `parameters.swNotice: false` opts a Story out // (use it where the page already carries the same notice, so it is never shown twice). export const SW_NOTICE_TEXT = @@ -14,9 +14,8 @@ export const withSWNotice: Decorator = (Story, context) => { const hidden = (context.tags ?? []).some(tag => HIDDEN_TAGS.includes(tag)) if (hidden || context.parameters.swNotice === false) return return ( - <> - {SW_NOTICE_TEXT} + - + ) }