feat(ui): standardize LNB sizing and active depth effect

- Adopt the official LNB desktop spec as package defaults: 240px column,
  14px menu text, 48px 1-depth rows and 42px 2/3-depth rows, with new
  item/subitem padding-block and profile min-height tokens.
- Match --hm-lnb-current-depth to the Storybook Manager selected item:
  sampled soft inset falloff (left/top/bottom) and a fade over the last
  quarter, keeping the concave corners and content join.
- Drop the size overrides from the LNB Story CSS and update the LNB, GNB
  and Dashboard docs, AI guides and the theme verification baseline.
This commit is contained in:
shkim4 committed 2026-11-09 21:34:28 +09:00
1 parent 89f265ae2f
commit e42b67c53a
9 files changed
+95 -75

No files matched your search

+5 -36
View File
@@ -1,27 +1,16 @@
/* Storybook presentation only: Patterns/Layout/LNB Stories. Scoped by the decorator wrapper in lnb.stories.tsx, so the shared
@hm-design/ui LNB Pattern, its tokens and every other Story keep their values.
- min-width 212px: a floor, not a fixed width. The column follows --hm-lnb-width (240px from 1024px, see below) and may be wider.
- menu font 14px: --hm-lnb-item-font-size is redefined for this subtree; weight (700) and line height (28px) stay.
- row height: depth1 48px, depth2 and deeper 42px. Heights come from min-height + vertical padding (not a fixed height), so a
label that wraps grows the row instead of being clipped. Current and hover only change color/background, not the height. */
/* Storybook presentation only: Patterns/Layout/LNB Stories. Scoped by the decorator wrapper in lnb.stories.tsx.
Column width (240px), menu font (14px) and row heights (depth1 48px, depth2/3 42px) are now the @hm-design/ui package defaults
(packages/ui/src/theme/lnb.css); only the Story-specific presentation below stays here.
- min-width 212px: a floor, not a fixed width. The column follows --hm-lnb-width and may be wider.
- brand bottom padding 8px, profile text, and divider lines. */
.hm-lnb-story {
display: contents;
--hm-lnb-item-font-size: 14px;
/* Existing variable: min-height of every item and group trigger (used by nothing else). */
--hm-lnb-item-min-height: 48px;
/* Existing variable: bottom padding of [data-slot="lnb-brand"] (22px by default; top 0 and the 30px sides are unchanged). */
--hm-lnb-brand-padding-bottom: 8px;
}
.hm-lnb-story [data-slot="lnb"] {
min-width: 212px;
}
/* PC column width 240px through the existing --hm-lnb-width (the column, the Story grid track and the menu row offset all read it).
Only from 1024px: the package sets --hm-lnb-width to 0px below that and collapses the column, which must keep working. */
@media (min-width: 1024px) {
.hm-lnb-story {
--hm-lnb-width: 240px;
}
}
/* Profile text (PC column): name (strong) 14px, description (p) 12px with no top margin, both 700. The package's existing 1px text
stroke on strong/p stays as is; the color and the 1.5 line-height ratio are unchanged (21px + 18px lines in the 48px profile).
@@ -91,23 +80,3 @@
paint-order: normal;
}
/* depth1: nav items and top-level group triggers. The package hard-codes 14px vertical padding (28px line + 28px = 56px). */
.hm-lnb-story [data-slot="lnb-item"],
.hm-lnb-story [data-slot="lnb-group-trigger"] {
padding-top: 10px;
padding-bottom: 10px;
}
/* depth2: links directly inside a group and nested group triggers (28px line + 14px = 42px). */
.hm-lnb-story [data-slot="lnb-group-content"] > [data-slot="lnb-item"],
.hm-lnb-story [data-slot="lnb-group-content"] [data-slot="lnb-group-trigger"] {
min-height: 42px;
padding-top: 7px;
padding-bottom: 7px;
}
/* depth3: links in a nested group (24px line + 18px = 42px). --hm-lnb-third-min-height is also the Profile's height, so it is
overridden on the row's link instead of through the variable. */
.hm-lnb-story [data-slot="lnb-group-content"] [data-slot="lnb-third-row"] > [data-slot="lnb-item"] {
min-height: 42px;
padding-top: 9px;
padding-bottom: 9px;
}
+5 -4
View File
@@ -34,9 +34,10 @@ import { StateBoard } from "@/components/story-board"
import { StoryPage, StoryPageHeader, StorySection } from "@/components/story-page"
import "./lnb.stories.css"
// LNB is a composition pattern. Layout visual reference is the ui-viz LNB (the shared package default: 272px green column, 18/700 items,
// Beige current item joined to the content, Black 10% hover, Black 8% open group). This Story overrides only the display values (240px column,
// 212px min width, 14px items, 48/42px rows) in lnb.stories.css; the package defaults are unchanged. The menu data and the current location are local sample data for this Story
// LNB is a composition pattern. Layout visual reference is the ui-viz LNB (the shared package default: 240px green column, 14/700 items,
// 48px 1-depth and 42px 2/3-depth rows, Beige current item joined to the content, Black 10% hover, Black 8% open group). The sizes come from the package
// (`--hm-lnb-*` in packages/ui/src/theme/lnb.css); lnb.stories.css only keeps Story presentation (212px min width, brand bottom padding, profile text,
// divider lines). The menu data and the current location are local sample data for this Story
// (no router). The content next to the column is a positioned surface with `--hm-lnb-join` rounded left corners: the LNB Mode page
// owns it, here it only shows the connection. The column is desktop only (hidden below 1024px).
@@ -619,7 +620,7 @@ const mobileViewport = {
globals: { viewport: { value: "hmMobile", isRotated: false } },
}
// `hm-lnb-story` (display: contents, see lnb.stories.css) scopes the 212px min width and 14px menu font to this Story only.
// `hm-lnb-story` (display: contents, see lnb.stories.css) scopes the 212px min width, brand bottom padding, profile text and divider lines to this Story only.
const meta = { title: "Patterns/Layout/LNB", component: LNB, decorators: [(Story) => <div className="hm-lnb-story"><Story /></div>], parameters: { layout: "fullscreen", controls: { disable: true } } } satisfies Meta<typeof LNB>
export default meta
type Story = StoryObj<typeof meta>
+9 -5
View File
@@ -588,7 +588,7 @@
"--hm-label-line-height": "20px",
"--hm-label-font-weight": "500",
"--hm-label-text": "var(--hm-sem-fg-secondary)",
"--hm-lnb-width": "272px",
"--hm-lnb-width": "240px",
"--hm-lnb-join": "18px",
"--hm-lnb-background": "linear-gradient(90deg, var(--hm-green-default) 0%, var(--hm-green-light-1) 30%, var(--hm-green-light-2) 50%, var(--hm-green-light-3) 90%, var(--hm-green-light-2) 100%)",
"--hm-lnb-background-end": "var(--hm-green-light-2)",
@@ -608,10 +608,11 @@
"--hm-lnb-brand-eyebrow-spacing": "0.12em",
"--hm-lnb-brand-name-size": "17px",
"--hm-lnb-brand-text-gap": "5px",
"--hm-lnb-item-min-height": "56px",
"--hm-lnb-item-min-height": "48px",
"--hm-lnb-item-padding-block": "10px",
"--hm-lnb-item-padding-start": "18px",
"--hm-lnb-item-gap": "12px",
"--hm-lnb-item-font-size": "18px",
"--hm-lnb-item-font-size": "14px",
"--hm-lnb-item-line-height": "28px",
"--hm-lnb-item-font-weight": "700",
"--hm-lnb-item-radius": "4px 0 0 4px",
@@ -631,9 +632,11 @@
"--hm-lnb-group-current-text": "var(--hm-yellow-accent)",
"--hm-lnb-group-arrow-size": "18px",
"--hm-lnb-subitem-padding-start": "68px",
"--hm-lnb-subitem-min-height": "42px",
"--hm-lnb-subitem-padding-block": "7px",
"--hm-lnb-group-padding-bottom": "8px",
"--hm-lnb-third-min-height": "48px",
"--hm-lnb-third-padding-block": "12px",
"--hm-lnb-third-min-height": "42px",
"--hm-lnb-third-padding-block": "9px",
"--hm-lnb-third-padding-start": "88px",
"--hm-lnb-third-line-height": "24px",
"--hm-lnb-third-font-weight": "500",
@@ -646,6 +649,7 @@
"--hm-lnb-profile-padding-x": "30px",
"--hm-lnb-profile-padding-top": "22px",
"--hm-lnb-profile-gap": "12px",
"--hm-lnb-profile-min-height": "48px",
"--hm-lnb-profile-name-size": "16px",
"--hm-lnb-profile-description-size": "12px",
"--hm-loading-screen-background": "var(--hm-green-default)",