refactor(storybook): hide page headers and preserve accessibility

StoryPageHeader no longer shows the page title, kicker, description or the SW group notice; it renders only a visually hidden H1 with the Story title, so the preview keeps a page heading for screen readers while the first section starts at the top gutter (unchanged position).

- OrgChart verify-p8 / p9 / p9-1 / p9-2-composition wait for the first visible StoryPage content block and read the page H1 instead of .sb-page-title; existing expectations are unchanged.
- SW overview browser test clicks the page gutter as its outside-click target.
- Drop the unused .sb-page-description from the SW Storybook hide rules.
- Update the Storybook page frame rule in the project guideline.
This commit is contained in:
shkim4 committed 2026-11-11 15:50:39 +09:00
1 parent f22428548a
commit 63b3ab010b
11 files changed
+59 -105

No files matched your search

+17 -50
View File
@@ -1,7 +1,7 @@
/* Storybook presentation only. Common page frame for representative Stories; all selectors stay inside .sb-page.
2026-11-11 compact document rhythm, shared by Web / Chart / OrgChart (SW and CENTER pages use the same frame only):
H1 page title 28/36/700 (Page Header title) → description 14/20/400 secondary, 8px below (Page Header description)
→ 24px → H2 section title 16/24/700 (body-md) over a thin divider → 12px → specimen; sections 24px apart (Dashboard gap);
2026-11-11: the H1 page title / description header is no longer shown (the H1 stays for screen readers only); pages start with the
H2 section title 16/24/700 (body-md) over a thin divider → 12px → specimen; sections 24px apart (Dashboard gap);
H3 specimen labels 14/20/500 secondary, no divider of their own (story-board.tsx). Top / bottom gutter 16–24px.
Side margins are unchanged (16px up to 480px, growing to 60px at 768px): the SW breakpoints 2040 / 1125 / 787 depend on them.
No design token is added; --sb-page-* only names existing component token values for this Storybook frame. */
@@ -27,32 +27,21 @@
margin-inline: auto;
}
/* No bottom padding: the header's margin collapses with the next block's own top margin (a section's 24px), so the first
content sits 24px below the title whether it is a section, a section grid or a plain preview. */
.sb-page .sb-page-header {
margin-bottom: var(--sb-page-section-gap);
font-size: var(--hm-label-font-size);
line-height: var(--hm-label-line-height);
overflow-wrap: anywhere;
}
.sb-page .sb-page-header .sb-page-kicker {
margin: 0 0 4px;
color: var(--hm-sem-fg-secondary);
font-size: var(--hm-button-font-size-sm);
font-weight: var(--hm-font-weight-bold);
}
.sb-page .sb-page-header .sb-page-title {
margin: 0;
font-size: var(--hm-page-header-title-font-size);
line-height: var(--hm-page-header-title-line-height);
font-weight: var(--hm-page-header-title-font-weight);
}
.sb-page .sb-page-header .sb-page-description {
margin: var(--hm-page-header-description-gap) 0 0;
color: var(--hm-page-header-description-color);
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);
/* 2026-11-11: no visible page header, so the first block starts right at the top gutter. StoryPageHeader only renders the
visually hidden page H1 (screen readers only, out of flow), so the first visible block after it gets the same reset. */
.sb-page > .sb-page-inner > :first-child,
.sb-page > .sb-page-inner > .sb-page-a11y-title:first-child + * { margin-top: 0; }
.sb-page .sb-page-a11y-title {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0 0 0 0);
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}
.sb-page .sb-page-section { margin-top: var(--sb-page-section-gap); scroll-margin-top: 16px; }
@@ -80,25 +69,3 @@
Columns and alignment stay with the Story (className): this unlayered rule would override Tailwind's layered grid-cols-*. */
.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; }
/* 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: 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-page-header-description-color);
overflow-wrap: anywhere;
}
.sb-page .sb-notice-icon {
flex: none;
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; }
+13 -31
View File
@@ -1,9 +1,9 @@
import { createContext, useContext, type ReactNode } from "react"
import { createContext, 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.
// Page → Header (title, description) → Sections (title, description, specimen). It never styles the component being reviewed.
// Inner and title follow Foundation / Color; the section header → content split follows Center Graphic / 교량.
// Page → Sections (title, description, specimen). It never styles the component being reviewed.
// Inner follows Foundation / Color; the section header → content split follows Center Graphic / 교량.
// A Story using it sets `parameters.layout: "fullscreen"` so the page owns its inner padding.
// Lets StateBoard render its specimen labels one level below the section heading.
@@ -13,36 +13,18 @@ export function StoryPage({ className, children }: { className?: string; childre
return <div className={className ? `sb-page ${className}` : "sb-page"}><div className="sb-page-inner">{children}</div></div>
}
// 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.
// Group notice context. Still provided by src/sw/sw-story-notice.tsx, but nothing renders it since the page header was
// removed from the Storybook frame (2026-11-11).
export const StoryNoticeContext = createContext<ReactNode>(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 (
<header className="sb-page-header">
{kicker ? <p className="sb-page-kicker">{kicker}</p> : null}
<h1 className="sb-page-title">{title}</h1>
{notice ? <StoryNotice>{notice}</StoryNotice> : null}
{description ? <p className="sb-page-description">{description}</p> : null}
</header>
)
}
// 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 (
<p className="sb-notice" data-story-notice="">
<svg className="sb-notice-icon" width="14" height="14" viewBox="0 0 29 29" fill="none" aria-hidden="true" focusable="false">
<path d="M15.7415 6L15.5383 17.3984H13.3743L13.1711 6H15.7415ZM14.4602 22.1484C14.0279 22.1484 13.6581 21.9974 13.3508 21.6953C13.0435 21.388 12.8925 21.0182 12.8977 20.5859C12.8925 20.1589 13.0435 19.7943 13.3508 19.4922C13.6581 19.1849 14.0279 19.0312 14.4602 19.0312C14.8821 19.0312 15.2467 19.1849 15.554 19.4922C15.8612 19.7943 16.0175 20.1589 16.0227 20.5859C16.0175 20.8724 15.942 21.1354 15.7961 21.375C15.6555 21.6094 15.468 21.7969 15.2336 21.9375C14.9993 22.0781 14.7415 22.1484 14.4602 22.1484Z" fill="currentColor" />
<circle cx="14.5" cy="14.5" r="13.5" stroke="currentColor" strokeWidth="2" />
</svg>
<span className="sb-notice-text">{children}</span>
</p>
)
// 2026-11-11 (user request): the Storybook page header (kicker, description and group notice) is no longer shown, so every
// page visually starts with its first section. Only the title stays, as a visually hidden H1 (no box, no space), so the
// preview document keeps one page heading above its H2 sections: the preview's own document title is the Story ID and the
// iframe title is generic, so this is the only place a screen reader can identify the page.
// Stories keep their calls and props, so the visible header can be restored here.
// The product Page Header component (@hm-design/ui PageHeader) is unrelated and unchanged.
export function StoryPageHeader({ title }: { kicker?: string; title: string; description?: ReactNode }) {
return <h1 className="sb-page-a11y-title">{title}</h1>
}
// Headings and specimens share StoryPage's single Inner. Wide specimens own their local scroll frame.