@naeil/ui 0.7.2 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/CHANGELOG.md +17 -1
  2. package/DESIGN.md +5 -1
  3. package/README.md +6 -6
  4. package/dist/{chunk-7JBLPGZ4.js → chunk-JDHGKM25.js} +1 -1
  5. package/dist/{chunk-QQQQZEPV.js → chunk-KPE4KKYM.js} +111 -14
  6. package/dist/{chunk-MGCCKOGU.js → chunk-XRO3NJAB.js} +102 -44
  7. package/dist/components/index.d.ts +2 -2
  8. package/dist/components/index.js +3 -3
  9. package/dist/components/naeil-footer.d.ts +2 -2
  10. package/dist/components/naeil-footer.js +3 -1
  11. package/dist/components/naeil-header.d.ts +8 -2
  12. package/dist/components/naeil-header.js +3 -3
  13. package/dist/components/ui/index.d.ts +2 -2
  14. package/dist/components/ui/index.js +1 -1
  15. package/dist/lib/ui-release.d.ts +1 -1
  16. package/dist/lib/ui-release.js +1 -1
  17. package/dist/{navigation-CgPeL9tF.d.ts → navigation-D8N1tMTu.d.ts} +4 -2
  18. package/dist/{site-footer-vXDa_6yr.d.ts → site-footer-s113FZgC.d.ts} +1 -1
  19. package/dist/source/components/naeil-footer.js +2 -0
  20. package/dist/source/components/naeil-header-account.js +54 -19
  21. package/dist/source/components/naeil-header.d.ts +8 -2
  22. package/dist/source/components/naeil-header.js +49 -23
  23. package/dist/source/components/ui/navigation.d.ts +8 -2
  24. package/dist/source/components/ui/navigation.js +112 -15
  25. package/dist/source/lib/ui-release.d.ts +1 -1
  26. package/dist/source/lib/ui-release.js +1 -1
  27. package/docs/components/README.md +1 -1
  28. package/docs/components/application-layout.md +28 -7
  29. package/docs/components/common.md +2 -2
  30. package/docs/components/naeil-header.md +39 -5
  31. package/docs/design/v2-migration.md +8 -4
  32. package/docs/package-release.md +25 -4
  33. package/package.json +1 -1
  34. package/src/styles/components.css +130 -0
@@ -1,6 +1,6 @@
1
1
  # Application frame and shared navigation/footer
2
2
 
3
- This maintained guide covers **published and registry-verified @naeil/ui 0.7.1**, combining canonical footer/release identity and shared account presentation with the published 0.6.0 frame. Install exact `@naeil/ui@0.7.1` from npm; hosted Docs and service activation have separate receipts. See [release identities and status](../package-release.md#publication-and-deployment) and [consumer setup](../design/v2-migration.md) for CSS, Tailwind sources and fonts.
3
+ Exact **@naeil/ui 0.8.0 is published** (`next` tag; `latest` remains 0.7.2 at the 2026-10-29 verification). The **0.9.0 candidate** adds shared root reservation. Install exact `@naeil/ui@0.8.0` for the published Home APIs; hosted Docs and service activation have separate receipts. See [release identities and status](../package-release.md#publication-and-deployment) and [consumer setup](../design/v2-migration.md) for CSS, Tailwind sources and fonts.
4
4
 
5
5
  ## Choose the shared composition first
6
6
 
@@ -8,7 +8,8 @@ This maintained guide covers **published and registry-verified @naeil/ui 0.7.1**
8
8
  | --- | --- |
9
9
  | The actual naeil.dev brand, project catalog and controls | `NaeilHeader` from `@naeil/ui/naeil-header` |
10
10
  | Owned-service login/account/logout presentation | `NaeilHeaderAccountControl` from `@naeil/ui/naeil-header` in `authSlot` |
11
- | A product's own menu | `Navigation` from `@naeil/ui/ui` |
11
+ | Owned naeil service with product menu data (0.8.0) | Complete `NaeilHeader`, with `appNavigation`, catalog/blog hidden, optional `brandSuffix` and `statusSlot` |
12
+ | A different product menu without canonical naeil chrome | `Navigation` from `@naeil/ui/ui` |
12
13
  | Owned naeil service footer | `NaeilFooter` from `@naeil/ui/naeil-footer` |
13
14
  | Different product footer or explicit content override | `SiteFooter` from `@naeil/ui/ui` |
14
15
  | An existing Next/next-intl consumer | Keep its `Nav` / `Footer` adapters |
@@ -28,13 +29,30 @@ Choose one header per application frame. Supply menu data, links, route guards,
28
29
 
29
30
  Use `.ui-app-frame` on main. Shared header/footer already apply the frame internally; **do not wrap them in another padded frame**. Put purpose-specific sections inside main. At 1920px the default frame has 1520px available after its 40px gutters. Small screens shrink the frame and reduce gutters to at least 16px. A narrow form must not constrain or move navigation.
30
31
 
31
- Reserve a consistent scrollbar gutter in the consumer's global CSS when routes can alternate between short and tall content:
32
+ The **0.9.0 candidate** supplies `html { scrollbar-gutter: stable }` once in `components.css`, reached by `globals.css` or `theme.css + components.css`. Classic mode reserves one browser-sized gutter on short and long pages; overlay mode reserves none. The shared lock correction removes duplicate Radix body right-margin/gap compensation and retains its scroll prevention, focus and nested cleanup. Keep header/main/footer and their ordinary content horizontally stable. Compare matching root policies and measure before any fullPage capture. See the [adopted acceptance contract](https://github.com/naeil-dev/naeil-ui/blob/434b7d07235316097ca3a55265ee02c1422b0cfd/docs/superpowers/specs/2026-10-29-page-root-scrollbar-gutter.md).
33
+
34
+ For a different document policy, declare this on the root **before first paint**:
35
+
36
+ ```html
37
+ <html data-naeil-scrollbar-gutter="auto">
38
+ ```
39
+
40
+ This sets `scrollbar-gutter: auto` and disables the shared lock correction together. Apply custom root/scroller CSS only after opting out; changing only gutter CSS leaves an unsupported compensation combination. The default full-page profile has zero body margin/outer padding; no new body reset is supplied. Nonzero body edge spacing, custom scroll locks and react-remove-scroll-bar helper classes need consumer integration or opt-out. Theme-only and JS-only imports do not set root policy. Unsupported engines keep Radix's original compensation.
41
+
42
+ Import `globals.css`, or the split `theme.css` and `components.css`, without an enclosing named layer (`@import "@naeil/ui/components.css";`, not `@import "@naeil/ui/components.css" layer(ui);`). The package already declares its own layers. Wrapping the import in a named layer lets the dependency's unlayered `--removed-body-scroll-bar-size` win: it can remain 15px in classic mode even though the important margin correction still wins. Elements using that variable can then shift.
43
+
44
+ After opting out, a plain `@layer base { html { scrollbar-gutter: stable; } }` loses to the package's more specific root attribute selector in the same layer. Write the custom rule outside any layer, for example:
32
45
 
33
46
  ```css
34
- html { scrollbar-gutter: stable; }
47
+ /* The root has data-naeil-scrollbar-gutter="auto"; this policy owns its lock integration. */
48
+ html[data-naeil-scrollbar-gutter="auto"] { scrollbar-gutter: stable; }
35
49
  ```
36
50
 
37
- This keeps a centered frame from shifting when a classic scrollbar appears. The document scroll policy belongs to the application; the shared components do not set it implicitly. In a 1920px Chromium viewport with a 15px scrollbar, the frame starts at 152.5px with this policy in both short and tall states. Without it, the start changes from 160px to 152.5px. Overlay-scrollbar platforms may reserve no extra space.
51
+ Alternatively, use that attribute selector in `@layer base` after the package import, so equal specificity resolves to the later consumer rule. The shared lock correction stays disabled in either case; the consumer owns matching compensation.
52
+
53
+ In classic mode, a fixed modal scrim covers the page's available width, leaving the reserved gutter strip undimmed (15px in the measured macOS Chromium profile). This is expected with a reserved root; 0.9.0 leaves the scrim styles unchanged. Overlay mode has no reserved strip. A full-viewport scrim redesign is outside this release.
54
+
55
+ Frame and control tokens stay unchanged. At 1920px with a 15px classic gutter the centered 1600px frame starts at 152.5px on both short and long pages (overlay: 160px). Japanese signed-out canonical login content at width 320 has 257px inside the 305px classic sheet; its 261px toolbar wraps to 104px. Overlay remains 44px. Preserve all labels, 44px targets and keyboard access. Home Workspace/account and LoginGate/no-login-slot retain their 44px toolbars; this specific F3 profile difference is documented, not a new Home chrome exception.
38
56
 
39
57
  ## Canonical naeil application starter
40
58
 
@@ -78,7 +96,9 @@ export function ApplicationLayout({ children, locale, Link, currentHref,
78
96
 
79
97
  `NaeilHeader` is fixed; reserve its 65px height and account for it in sticky areas and focus/anchor scroll margins. Default destinations are official naeil.dev URLs. Preserve routing/unsaved-change guards using the [header contract](naeil-header.md) and [footer interception contract](naeil-footer.md); a Link adapter must handle absolute official and GitHub destinations as well as local routes. Only pass `onLocaleChange` when the application supports its complete language set.
80
98
 
81
- For a different product menu, use the [generic Navigation composition](navigation.md) with the same main/footer structure. Generic Navigation is in document flow by default and needs no fixed-header clearance. The [standalone React example](https://github.com/naeil-dev/naeil-ui/tree/main/examples/react) exercises the installed header, narrow form and SiteFooter together.
99
+ For owned naeil product navigation, keep the whole header and pass menu data through `appNavigation`; see the [Home profile and host conditions](naeil-header.md#host-typography-and-mobile-handoff-080). `mobileOpen`/`onMobileOpenChange` can coordinate the sheet with a consumer confirmation, while busy state and request locking remain above the header. For a different product menu without canonical chrome, use the [generic Navigation composition](navigation.md) with the same main/footer structure. Generic Navigation is in document flow by default and needs no fixed-header clearance. The [standalone React example](https://github.com/naeil-dev/naeil-ui/tree/main/examples/react) exercises the installed header, narrow form and SiteFooter together.
100
+
101
+ An empty desktop menu preserves B1's limitation: resizing alone leaves the sheet open; Escape/close/overlay/route change dismiss it. Controlled-mobile account callbacks wait for both closing layers and their focus handoff; desktop and uncontrolled-mobile account callbacks retain B1's selection-time behavior. A consumer confirmation owns cancellation focus, including a visible desktop account fallback after resize.
82
102
 
83
103
  ## Deliberate overrides
84
104
 
@@ -106,6 +126,7 @@ Put this in the **consumer application's** `AGENTS.md` (or equivalent repository
106
126
  - Use @naeil/ui/naeil-header for the canonical naeil header; use Navigation from @naeil/ui/ui for a different product menu. Use NaeilFooter from @naeil/ui/naeil-footer for owned service footers; SiteFooter remains the generic primitive. Keep existing Next Nav/Footer compatibility adapters when applicable.
107
127
  - Put NaeilHeaderAccountControl from @naeil/ui/naeil-header in authSlot for owned apps. Supply real session state, the busy flag and guarded logout callback. Keep pending state and the request lock above the header so desktop/mobile copies share them; do not copy account/avatar/menu markup.
108
128
  - Local wrappers supply menu data, routing/guards, locale, auth and slots. Do not copy the menu, mobile sheet, controls or footer implementation. Extend the shared component for reusable missing behavior.
129
+ - The 0.9.0 candidate CSS reserves a stable root gutter through globals or theme+components; opt out on html with data-naeil-scrollbar-gutter="auto" before first paint to disable both root reservation and lock correction. Compare classic/overlay and short/long body content through menus/dialogs; measure before fullPage screenshots.
109
130
  - Use ui-app-frame on main; navigation/footer own their internal frames. Keep reading/settings/short-form sections inside that frame. Apply frame overrides consistently across routes.
110
131
  - Check the installed package version and CSS before creating a replacement for an apparently missing component.
111
132
  - Verify actual shared navigation/footer rendering and stable anchors across routes, themes and supported sizes, including signed-out, signed-in, menu, busy, cancellation/failure and retry states. An import or anonymous-only check is not adoption evidence. Record intentional route exceptions.
@@ -119,7 +140,7 @@ Owned-service checks also verify canonical header/footer destinations, rendered
119
140
 
120
141
  A blanket ban on `<header>`, `<nav>` or `<footer>` would also reject valid section landmarks and adapters. Review the root composition and its rendered output instead. This repository checks packed React/Next examples; external applications still need their own upgrades, adapter migrations and CI. Publication and deployment are separate work.
121
142
 
122
- See [canonical footer content and save interception](naeil-footer.md). Version 0.7.1 includes package/contract identity from the unpublished 0.7.0 candidate; registry publication, hosted Docs and per-service operational rollout have separate receipts.
143
+ See [canonical footer content and save interception](naeil-footer.md). Version 0.7.2 includes package/contract identity; registry publication, hosted Docs and per-service operational rollout have separate receipts.
123
144
 
124
145
  ## Shared naeil.dev login (services on `*.naeil.dev`)
125
146
 
@@ -1,12 +1,12 @@
1
1
  # Shared usage contract
2
2
 
3
- These guides target the **@naeil/ui 0.6.0**; release identities and publication evidence are recorded in the [release record](https://github.com/naeil-dev/naeil-ui/releases/tag/v0.6.0). The [hosted Docs](https://naeil-dev.github.io/naeil-ui/) were last verified for [0.5.0 on 2026-10-15](https://github.com/naeil-dev/naeil-ui/releases/tag/v0.5.0), while `pnpm storybook` shows this checkout. Start with the [consumer setup and migration](../design/v2-migration.md). Primitives and generic Navigation/SiteFooter are exported from `@naeil/ui/ui`; the actual branded whole header uses the opt-in [NaeilHeader](naeil-header.md) entry `@naeil/ui/naeil-header`. Both are React-only. The legacy root needs the optional Next/next-intl peers. Examples import real wrappers; product state, validation and network requests belong to consumers.
3
+ These maintained guides cover published exact **@naeil/ui 0.8.0** and the unpublished **0.9.0 frame candidate**; see [release identities and status](../package-release.md#publication-and-deployment). The [hosted Docs](https://naeil-dev.github.io/naeil-ui/) were last verified for [0.5.0 on 2026-10-15](https://github.com/naeil-dev/naeil-ui/releases/tag/v0.5.0), while `pnpm storybook` shows this checkout. Start with the [consumer setup and migration](../design/v2-migration.md). Primitives and generic Navigation/SiteFooter are exported from `@naeil/ui/ui`; the actual branded whole header uses the opt-in [NaeilHeader](naeil-header.md) entry `@naeil/ui/naeil-header`. Both are React-only. The legacy root needs the optional Next/next-intl peers. Examples import real wrappers; product state, validation and network requests belong to consumers.
4
4
 
5
5
  ## Styling, themes and overrides
6
6
 
7
7
  Import `@naeil/ui/globals.css` once and register `@source` for the installed package's `dist` in your Tailwind 4 build. Alternatively load both theme.css and components.css alongside your existing Tailwind setup. Deliver Pretendard and, for Japanese, Noto Sans JP locally through your app's font pipeline; the package does not fetch fonts. Keep font OFL notices with redistributed binaries. The independent React example demonstrates consumer-owned delivery.
8
8
 
9
- Use `.light`/`.dark` on the document root to override OS theme. Root `data-ui-density="compact"` changes spacing, not text size: default controls are 40px, compact 36px, small screens/coarse pointers at least 44px. Portals use document-level styles/density, so a locally themed subtree alone does not theme portaled content. Header, main and footer share a 1600px maximum outer frame and `clamp(16px, 2.4vw, 40px)` gutters. Use `ui-app-frame` on main; shared header/footer components apply that frame internally. Reading/settings sections remain capped at 640px, short forms at 480px, and lists at 1600px within the available frame space. Product root token overrides and explicit component classes remain supported. The [application layout guide](application-layout.md) explains composition, adoption and consistent overrides.
9
+ Use `.light`/`.dark` on the document root to override OS theme. Root `data-ui-density="compact"` changes spacing, not text size: default controls are 40px, compact 36px, small screens/coarse pointers at least 44px. Portals use document-level styles/density, so a locally themed subtree alone does not theme portaled content. Header, main and footer share a 1600px maximum outer frame and `clamp(16px, 2.4vw, 40px)` gutters. Use `ui-app-frame` on main; shared header/footer components apply that frame internally. Reading/settings sections remain capped at 640px, short forms at 480px, and lists at 1600px within the available frame space. Product root token overrides and explicit component classes remain supported. The [application layout guide](application-layout.md) explains composition, adoption and consistent overrides, including the 0.9.0 default stable root gutter, the coupled html opt-out and unchanged classic/overlay toolbar tokens. Theme-only/JS-only imports do not set document policy.
10
10
 
11
11
  `className` is merged by `cn` (clsx + tailwind-merge). Consumer classes and primitive props remain available. Recheck specificity, text/control/focus contrast, mobile targets and portaled content after overriding. Override semantic color companions together; do not replace only an action hue or remove focus outlines. Reduced-motion rules remove shared transitions/animations. Custom consumer animation still needs its own reduced-motion treatment.
12
12
 
@@ -1,6 +1,6 @@
1
1
  # NaeilHeader — branded whole-header composition
2
2
 
3
- This maintained guide covers **published and registry-verified @naeil/ui 0.7.1**, adding shared account presentation to the unchanged 0.6.0 frame. Install exact `@naeil/ui@0.7.1` from npm; hosted Docs and service activation have separate receipts. See [release identities and publication status](../package-release.md#publication-and-deployment). Import this opt-in React-only entry when an application inherits the actual naeil.dev header. Generic `Navigation` from `/ui` remains available for different product menus.
3
+ Exact **@naeil/ui 0.8.0 is published** (registry `next`; `latest=0.7.2` verified 2026-10-29). These Home APIs are available in that exact version. The **0.9.0 candidate** adds shared root reservation and remains unpublished. Hosted Docs and service activation have separate receipts. See [release identities](../package-release.md#publication-and-deployment).
4
4
 
5
5
  ```tsx
6
6
  import { NaeilHeader, NaeilHeaderAccountControl } from '@naeil/ui/naeil-header';
@@ -16,7 +16,7 @@ import '@naeil/ui/globals.css';
16
16
 
17
17
  Load shared styles once. Apps that already own their Tailwind/base styles may import both `@naeil/ui/theme.css` and `@naeil/ui/components.css` instead of globals. Tailwind 4 consumers must register the installed `dist` with `@source`; see [setup](../design/v2-migration.md#시작하기).
18
18
 
19
- The module owns the brand, five-project catalog and header-only translations, project artwork references/status, theme/optional locale controls, toolbar order, fixed positioning, branded frame and Radix desktop/mobile behavior. Do not copy menu JSX or styles into a consumer. The website's `NavWrapper` imports this same module and supplies locale/router/auth state; estimate consumes the built package entry. Legacy `Nav` retains its Next/next-intl compatibility API.
19
+ The module owns the brand, single-project naeil-ui catalog and header-only translations, project artwork references/status, theme/optional locale controls, toolbar order, fixed positioning, branded frame and Radix desktop/mobile behavior. Do not copy menu JSX or styles into a consumer. The website's `NavWrapper` imports this same module and supplies locale/router/auth state; estimate consumes the built package entry. Legacy `Nav` retains its Next/next-intl compatibility API.
20
20
 
21
21
  ## Consumer contract
22
22
 
@@ -33,6 +33,10 @@ Only provide `onLocaleChange` when the application's interface supports all thre
33
33
  | `authSlot` | Canonical `NaeilHeaderAccountControl`, with consumer-owned auth state/actions. Legacy arbitrary markup remains supported. |
34
34
  | `brandHref`, `brandSlot`, `brandLinkProps`, `brandLinkComponent` | Explicit brand destination/content/anchor overrides; default accessible brand name is `.naeil`. |
35
35
  | `projects`, `projectsExternal`, `showProjects`, `showBlog` | Legacy catalog/visibility/native-link overrides; defaults use the complete canonical catalog. |
36
+ | `appNavigation` (0.8.0) | `NavigationItem[]` passed unchanged after catalog/blog and before flat `subNav`. Owned product menus use this with `showProjects={false} showBlog={false}`. |
37
+ | `brandSuffix` (0.8.0) | Product qualifier inside the brand link; the package owns its 14px type, 4px gap and optical alignment. Ignored when `brandSlot` replaces the complete content. Set `brandLinkProps["aria-label"]` for a product name. |
38
+ | `statusSlot` (0.8.0) | Exactly once, outside and 8px after the brand link at every width. The consumer owns the content and accessible status. No wrapper is added when omitted. |
39
+ | `mobileOpen`, `onMobileOpenChange` (0.8.0) | Controlled/uncontrolled sheet state, also on generic Navigation. Omit `mobileOpen` for internal state; a callback alone observes it. Accepted transitions notify once. In controlled mode callbacks request a change against the committed prop, so another close request is reported if the parent rejects or delays the previous one; an external prop update emits no request. Route changes, ordinary links, close/overlay/Escape dismiss. Desktop crossing dismisses when the desktop navigation gains width; an empty menu retains B1's open sheet until Escape/close/overlay/route change. Modified/middle clicks preserve it; nested menus consume the first Escape. |
36
40
  | `subNav`, `currentProjectKey`, `currentProjectHref` | Legacy extra links and current-project destination. |
37
41
  | `translations`, `labels` | Legacy header/project lookup overrides and navigation/menu/close accessible labels. |
38
42
  | `toolbarSlot` | Replaces the complete default theme/locale/auth toolbar. |
@@ -47,7 +51,9 @@ Standard header attributes and the remaining inherited [Navigation props](naviga
47
51
  - `{ status: 'signed-out', loginHref }` renders the shared login link through that adapter.
48
52
  - `{ status: 'signed-in', onLogout, busy?, displayName?, email? }` renders the same account icon in every app; optional identity appears only inside its menu. There is no shared identity or avatar fetch.
49
53
  - `onLogout` starts the app's guarded action; the app handles async errors, cancellation, session endpoints and redirects. `busy` disables the menu action while keeping the trigger available, with `aria-busy`, for inspection and focus restoration. Cancel/failure clears busy and permits retry.
50
- - Callback invocation closes the menu and returns focus to the trigger. Mobile navigation may remain open during a failed action. The account menu uses the existing Radix overlay/inert implementation.
54
+ - Desktop and uncontrolled-mobile account controls retain B1: `onLogout` runs during selection, then Radix closes the account menu and restores its trigger; the mobile sheet may remain open during a failed action. A controlled mobile sheet instead defers the callback until its account menu and sheet finish closing, focuses a surviving visible header control, and invokes the callback once. Reopening the sheet or account menu, a new sheet dismissal request, or unmounting the header cancels the queued action. The account menu uses the existing Radix overlay/inert implementation.
55
+
56
+ While a controlled parent keeps `mobileOpen=true`, its rejection and delayed acceptance are indistinguishable. A later `false` prop without an intervening cancellation accepts the queued selection; assigning `true` when it is already `true` creates no observable reopen transition.
51
57
 
52
58
  Keep errors visible in the consumer's recovery UI or toast. The package never calls an authentication endpoint. Compare signed-in and signed-out render states separately; anonymous-only acceptance cannot establish authenticated parity. `AccountStates` is a synthetic Storybook example for pending/cancel/error/retry.
53
59
 
@@ -59,10 +65,38 @@ The fixed header is 65px: the current 64px navigation token plus a 1px border. C
59
65
 
60
66
  ## Artwork and package boundary
61
67
 
62
- The next release narrows the default catalog to naeil-ui and its `whale.png` image; 0.7.1 retains the five-project catalog described here. The five decorative images reference `https://naeil.dev/images/{coral,jellyfish,whale,fish,turtle}.png`. Rendering those images requires network access to the official origin. Their bytes are not redistributed in npm; [third-party notices](../../THIRD_PARTY_NOTICES.md) retain unresolved artwork provenance and redistribution limits. Brand marks are not relicensed by the repository MIT license. Captions and links remain usable when images fail.
68
+ The 0.7.2 default catalog contains naeil-ui and references only `https://naeil.dev/images/whale.png`. Rendering that decorative image requires network access to the official origin. Its bytes are not redistributed in npm; [third-party notices](../../THIRD_PARTY_NOTICES.md) retain unresolved artwork provenance and redistribution limits. Brand marks are not relicensed by the repository MIT license. Captions and links remain usable when images fail.
63
69
 
64
70
  This entry requires no Next.js, next-intl, site session/auth backend, full application message catalog, hero or site public folder. Generic `/ui` imports do not import its catalog. Storybook provides **Brand / NaeilHeader / Docs, Usage, JapaneseApplication, AccountStates, ReferenceFull, ReferenceCompact**; the Japanese example omits unsupported locale switching and supplies application state to the shared account control. Local examples do not establish hosted deployment.
65
71
 
66
72
  Verify actual packed consumption, Storybook behavior and rendered comparisons at matching viewport/language/theme. Source imports alone do not prove visual parity. Native Safari, devices, assistive technology, full OAuth and unresolved accessibility observations retain the [documented support limits](../design/public-ui-support.md).
67
73
 
68
- Pair owned-service headers with [NaeilFooter](naeil-footer.md). Version 0.7.1 emits package/contract identifiers for actual rendered validation. The immutable `v0.7.1` package source is `32108fd8e1cb1db6dae24a09f3ca72ac9a91f618`; maintained pending-state guidance above and hosted Docs are reviewed separately from that frozen snapshot.
74
+ Pair owned-service headers with [NaeilFooter](naeil-footer.md). The 0.7.2 source is `40a3928e24cd2f0141be3e605eed0a75260938fd`. Both 0.7.2 and 0.8.0 emit package version and chrome contract `1`; archive identities, maintained guidance and hosted Docs remain separate evidence.
75
+
76
+
77
+ ## Host typography and mobile handoff (0.8.0)
78
+
79
+ Import either `globals.css` or `theme.css` plus `components.css`, and register the installed `dist` with Tailwind `@source`. Header/footer roots and the actual navigation popover, sheet, account menu and locale menu Content elements carry `data-naeil-chrome` and `lang=locale`; the locale Select Content also carries `data-naeil-locale-menu`. Generic Navigation/Select/DropdownMenu do not acquire these markers. Do not identify chrome by option text or by the presence of a Japanese body option.
80
+
81
+ Marker-and-language-scoped final typography uses a package-local font stack and explicit sizes, weights, line heights, letter spacing and smoothing. It supports a host `@theme` / `@theme inline` with different text tokens/font stacks and unlayered element/role rules at Home's specificity, including `(0,3,0)` `:root[attr] [role="menuitem"]` and Home's `[role="dialog"] p` rules. Host rules can load before or after either supported package CSS entry; owned text declarations outrank them. Rules with `!important` or IDs aimed at chrome are outside this guarantee. Ordinary body Select/menu/dialog typography remains host-owned; custom slot content remains consumer-owned. Font files are still consumer-supplied; Japanese chrome and portals use Noto Sans JP even when the document language differs.
82
+
83
+ A controlled mobile sheet defers its shared account action until the account menu and sheet finish closing and restore a surviving header control. The consumer still owns dirty state, confirmation, logout, busy state and the request lock; `onLogout` must tolerate this asynchronous handoff. When a confirmation saves its opener, its restoration policy must also handle a viewport change hiding that opener. Verify normal and reduced motion, cancellation after a desktop transition, pending requests, reopen and retry in the actual application; package verification does not replace actual product acceptance.
84
+
85
+ The empty-menu desktop exception is intentional B1 compatibility: a zero-width desktop navigation cannot signal the crossing, so the sheet stays open. Escape, its close button, the overlay or a `currentHref` change still close it. Home owns the confirmation's desktop cancel fallback to the visible row account trigger; the package supplies a visible, focusable control and does not implement the confirmation's restoration policy.
86
+
87
+ ```tsx
88
+ <NaeilHeader locale={locale} currentHref={pathname} Link={GuardedLink}
89
+ showProjects={false} showBlog={false} appNavigation={navigationItems}
90
+ brandHref="/" brandSuffix="Home" brandLinkProps={{ 'aria-label': 'Naeil Home' }}
91
+ statusSlot={readinessStatus} mobileOpen={mobileOpen} onMobileOpenChange={setMobileOpen}
92
+ onLocaleChange={changeLanguage}
93
+ authSlot={<NaeilHeaderAccountControl locale={locale} account={account} />} />
94
+ ```
95
+
96
+ Keep the default theme/locale/separator/account toolbar for this profile; `toolbarSlot` is a compatibility override, not the Home migration path. Omitting all new props preserves the 0.7.2 canonical profile, apart from markers, language correction, release identity and Radix-generated IDs. SVG/Logo IDs and consumer-supplied IDs are not normalized away. See the `docs/superpowers/specs/2026-10-28-canonical-home-chrome.md` acceptance contract for gates and the four allowed Home differences.
97
+
98
+ ## Page-root frame policy (0.9.0 candidate)
99
+
100
+ Both supported CSS paths obtain the stable root rule and scoped Radix duplicate-compensation correction from `components.css`. Theme-only and JS-only imports do not set document scrolling. Declare `<html data-naeil-scrollbar-gutter="auto">` before first paint to disable both pieces for a custom policy. See [application layout](application-layout.md) for custom-body/scroller/helper-class limits, fallback and the [adopted contract](https://github.com/naeil-dev/naeil-ui/blob/434b7d07235316097ca3a55265ee02c1422b0cfd/docs/superpowers/specs/2026-10-29-page-root-scrollbar-gutter.md) for required probes and capture ordering.
101
+
102
+ Keep frame/toolbar tokens unchanged. The Japanese signed-out canonical login toolbar at 320px is 104px tall under a 15px classic reservation and 44px with overlay. The owner's F3 decision accepts this wrap on naeil.dev itself. Home Workspace uses the shared account control/full footer; its signed-out LoginGate omits the auth slot/separator and footer (F1/F2). Neither Home profile is required to wrap. D1–D4 and the exact F3 auth-composition boundary remain; compare matching roots and auth-slot compositions before applying tolerances.
@@ -2,21 +2,21 @@
2
2
 
3
3
  이 안내는 `@naeil/ui`의 기본 모양과 재사용 규칙을 설명한다. 패키지 소비와 소비 사이트 배포는 별도 작업이다.
4
4
 
5
- **0.7.1 npm 게시·registry 검증 완료 — 2026-10-23.** 현재 게시된 기준 버전은 **0.7.1**이다. 이 유지보수 안내는 canonical `NaeilFooter`, `NaeilHeaderAccountControl`, `/release` 식별자와 기존 1600px 프레임을 함께 다룬다. 0.7.0은 미게시 후보이며, 처음 게시된 0.7.x 버전은 0.7.1이다. 독립된 새 React/Next registry 설치에서 동결 파일 139개와 타입·프로덕션 빌드·브라우저 검증이 통과했다. hosted Docs와 상시 배포 활성화는 별도 실행 기록으로 확인한다. UI v2는 디자인 세대 이름이며 npm 2.0을 뜻하지 않는다. 동결 패키지 소스·archive, 별도 검토한 Docs 소스와 실제 게시·배포 결과는 [릴리스 정책](../package-release.md#publication-and-deployment)에서 구분한다.
5
+ **정확한 0.8.0 npm 게시·archive 일치 검증 완료 — 2026-10-29.** registry 태그는 `latest=0.7.2`, `next=0.8.0`이며 0.8.0 archive는 동결된 139파일과 일치한다. Home API는 0.8.0에 포함된다. 현재 소스의 **0.9.0은 미게시 후보**이며 root 예약 기본값을 추가한다. hosted Docs와 소비 서비스 배포는 별도 기록이다. [릴리스 정책](../package-release.md#publication-and-deployment)을 따른다.
6
6
 
7
7
  ## 시작하기
8
8
 
9
9
  React 19/ReactDOM 19와 Tailwind 4 앱에서 `/ui` 또는 선택적 `/naeil-header`·`/naeil-footer`를 사용한다. API 키와 Next.js는 필요 없다. 검증한 registry 버전을 고정해 설치한다.
10
10
 
11
11
  ```sh
12
- pnpm add --save-exact @naeil/ui@0.7.1
12
+ pnpm add --save-exact @naeil/ui@0.8.0
13
13
  ```
14
14
 
15
- registry의 0.7.1 archive는 동결 릴리스 archive와 바이트가 일치한다. archive에 포함된 안내는 준비 시점의 기록이므로 현재 설치에는 이 유지보수 안내를 따른다. 로컬 개발에서는 저장소에서 `pnpm build:pkg`와 `npm pack --ignore-scripts --pack-destination /tmp`를 실행하고 `/tmp/naeil-ui-0.7.1.tgz`를 설치한다. 현재 소스의 개발용 pack은 동결 릴리스 archive와 다르므로 게시 파일을 대체하지 않는다. [독립 React 예제](https://github.com/naeil-dev/naeil-ui/tree/main/examples/react)와 [검증/릴리스 정책](../package-release.md)을 참고한다.
15
+ registry의 0.8.0 archive는 동결 릴리스 archive와 바이트가 일치한다. archive에 포함된 안내는 준비 시점의 기록이므로 현재 설치에는 이 유지보수 안내를 따른다. 로컬 개발에서는 저장소에서 `pnpm build:pkg`와 `npm pack --ignore-scripts --pack-destination /tmp`를 실행하고 현재 미게시 후보인 `/tmp/naeil-ui-0.9.0.tgz`를 설치한다. 현재 소스의 개발용 pack은 동결 릴리스 archive와 다르므로 게시 파일을 대체하지 않는다. [독립 React 예제](https://github.com/naeil-dev/naeil-ui/tree/main/examples/react)와 [검증/릴리스 정책](../package-release.md)을 참고한다.
16
16
 
17
17
  공통 UI 소비에는 Supabase, 21st 또는 다른 API 키가 필요하지 않다. React/ReactDOM 19는 소비 앱의 peer이며, Next 15/16과 next-intl 4는 호환 경로에 필요한 선택적 peer다. next-themes 0.4와 tailwind-merge 3은 공통 런타임 의존성으로 설치된다. 사이트의 Supabase·MDX·Three 의존성과 코드는 공통 소비 경계에서 제외했다.
18
18
 
19
- 버전을 생략한 설치는 registry의 latest 태그를 따른다. 2026-10-23에 latest는 0.7.1로 확인했다. 검증한 버전을 유지하려면 0.7.1을 명시한다. 업그레이드 전에 [변경 기록](../../CHANGELOG.md)을 확인한다.
19
+ 버전을 생략한 설치는 registry의 latest 태그를 따른다. 2026-10-29에 latest는 0.7.2, next는 0.8.0으로 확인했다. Home API를 사용하려면 검증한 0.8.0을 명시한다. 업그레이드 전에 [변경 기록](../../CHANGELOG.md)을 확인한다.
20
20
 
21
21
  Tailwind 4를 사용하는 앱의 전역 CSS에서 공통 스타일을 한 번 불러온다.
22
22
 
@@ -29,6 +29,10 @@ Tailwind 4를 사용하는 앱의 전역 CSS에서 공통 스타일을 한 번
29
29
 
30
30
  이미 Tailwind와 기본 스타일을 구성한 앱은 `@naeil/ui/theme.css`와 `@naeil/ui/components.css`를 함께 불러올 수 있다. 컴포넌트 CSS에는 크기·포커스·밀도·포털·reduced-motion 규칙이 있으므로 theme.css만으로 전체 v2 모양이 완성되지는 않는다.
31
31
 
32
+ 0.9.0 후보부터 두 CSS 경로의 `components.css`가 `html { scrollbar-gutter: stable }`과 Radix 이중 보상 제거를 제공한다. classic은 한쪽 공간을 예약하고 overlay는 0px이다. `<html data-naeil-scrollbar-gutter="auto">`를 첫 paint 전에 선언하면 root 예약과 lock 보정을 함께 끈다. theme-only/JS-only는 문서 정책을 바꾸지 않는다. 본문 바깥 여백/custom scroller/helper class는 별도 소비자 통합이나 opt-out이 필요하다. [채택된 계약](https://github.com/naeil-dev/naeil-ui/blob/434b7d07235316097ca3a55265ee02c1422b0cfd/docs/superpowers/specs/2026-10-29-page-root-scrollbar-gutter.md)과 [layout guide](../components/application-layout.md)의 비교·fallback·일본어 320px F3 wrap 규칙을 따른다.
33
+
34
+ CSS import를 `layer(ui)` 같은 named layer로 감싸지 않는다. 그러면 dependency의 unlayered `--removed-body-scroll-bar-size`가 이겨 classic에서 15px로 남을 수 있다. opt-out 후 `@layer base { html { scrollbar-gutter: stable; } }`는 패키지 attribute selector보다 약하므로, layer 밖에 `html[data-naeil-scrollbar-gutter="auto"] { scrollbar-gutter: stable; }`를 쓰거나 패키지 import 뒤의 같은 base layer에서 이 attribute selector를 쓴다. 이때 lock 보상은 소비자가 책임진다. Classic modal scrim은 예약된 gutter 띠를 덮지 않으며, 0.9.0에서 scrim 스타일을 바꾸지 않는다. 자세한 예시와 적용 범위는 [layout guide](../components/application-layout.md#width-contract)를 따른다.
35
+
32
36
  ```tsx
33
37
  import { Button, Input, Select, SelectTrigger, SelectValue,
34
38
  SelectContent, SelectItem, Switch, Checkbox } from '@naeil/ui/ui';
@@ -1,6 +1,6 @@
1
1
  # Package verification and release policy
2
2
 
3
- **0.7.1 published and registry-verified — 2026-10-23.** Current registry baseline: **0.7.1**. This first published 0.7.x release includes canonical footer/release identity from the unpublished 0.7.0 candidate, plus shared account presentation. The owner authorized publication and the selected three-service continuous rollout; hosted Docs and standing activation require separate receipts. The completed [0.6.0 release](https://github.com/naeil-dev/naeil-ui/releases/tag/v0.6.0) remains evidence for its published application frame. Verified 0.5.0, 0.4.0 and 0.3.0 evidence remains dated history. UI v2 names a design generation, not an npm major. Future breaking changes require an explicit migration and version decision. Version-scoped installation is in the [README](../README.md#use-the-ui).
3
+ **Published exact baseline: 0.8.0** (source/tag `3f708e27d05212af7f7a5be769f13f871e8d10c7`, archive SHA-256 `6feb5a8cd50265ace584a0855432683b4127119377e08235282eaebd27081084`, 139 files). Registry download matched that frozen archive on 2026-10-29; tags were `latest=0.7.2`, `next=0.8.0`. Its Home menu/brand/status/controlled-sheet APIs and package-owned chrome typography are published. **0.9.0 is an unpublished candidate** for the adopted page-root frame rule. Contract version remains `1` only if all invariants pass. Historical releases/archives remain immutable; hosted Docs and service activation require separate receipts.
4
4
 
5
5
  ## Architecture
6
6
 
@@ -8,7 +8,7 @@ One repository contains the shared package and the separate Next.js brand/exampl
8
8
 
9
9
  Owned services use `@naeil/ui/naeil-footer` for the canonical localized destinations in full/compact variants; generic SiteFooter and legacy Footer retain their APIs. Footer navigation adapters preserve application save/flush decisions and modified-click behavior. `@naeil/ui/release` is server-safe metadata; it contains no application artifact, credential or environment. Applications compile their own allowlisted artifact/source identity. See [the footer contract](components/naeil-footer.md). Current coordinator policy, selected services, activation and recovery evidence are maintained in the [rollout records](https://github.com/naeil-dev/naeil-ui/tree/main/docs/rollout); historical disabled-policy statements describe their dated checkpoints. Publishing a package, creating an update PR or merging it never establishes complete consumer rollout. Home Console is outside the selected shared-UI rollout.
10
10
 
11
- Website tools remain development dependencies so maintainers can develop both products. npm tarball consumers install only component runtime dependencies and their host React/ReactDOM. Build entries, package exports and packed checks constrain the public boundary. There is no bundled website action, Supabase helper, Three/hero/cursor module, blog engine or full application message catalog. The opt-in header includes only its three header dictionaries and canonical project catalog. Its five decorative images reference official `https://naeil.dev/images/*.png` URLs and require network access; no image bytes are bundled, and unresolved artwork redistribution provenance remains in [third-party notices](../THIRD_PARTY_NOTICES.md).
11
+ Website tools remain development dependencies so maintainers can develop both products. npm tarball consumers install only component runtime dependencies and their host React/ReactDOM. Build entries, package exports and packed checks constrain the public boundary. There is no bundled website action, Supabase helper, Three/hero/cursor module, blog engine or full application message catalog. The opt-in header includes only its three header dictionaries and canonical project catalog. Its 0.7.2 default catalog contains naeil-ui and references the official `https://naeil.dev/images/whale.png` URL and require network access; no image bytes are bundled, and unresolved artwork redistribution provenance remains in [third-party notices](../THIRD_PARTY_NOTICES.md).
12
12
 
13
13
  ## Checks
14
14
 
@@ -104,7 +104,7 @@ The immutable npm archive's bundled guides are the release-preparation snapshot.
104
104
 
105
105
  ### Release procedure
106
106
 
107
- Maintained Storybook resolves relative Markdown guide links against `v0.7.1`, whose frozen package source is `32108fd8e1cb1db6dae24a09f3ca72ac9a91f618`. That tag is independent of the reviewed Docs deployment SHA. Hosted verification checks that the pinned links resolve; the unpublished `v0.7.0` tag is not a valid target. Historical foundation/source-authority links remain at their original revisions. Freeze subsequent documentation changes separately from immutable release archives.
107
+ Maintained Storybook resolves relative Markdown guide links against the published `v0.7.2` source `40a3928e24cd2f0141be3e605eed0a75260938fd`. The published 0.8.0 archive and 0.9.0 candidate keep that published guide-link target until a separately reviewed Docs release. That tag is independent of the reviewed Docs deployment SHA. Hosted verification checks that the pinned links resolve; the unpublished `v0.7.0` tag is not a valid target. Historical foundation/source-authority links remain at their original revisions. Freeze subsequent documentation changes separately from immutable release archives.
108
108
 
109
109
  The authorized 0.3.0 release completed main merge, npm 0.3.0, a GitHub Release and hosted Docs as recorded above. The release maintainer performs external authentication and publication; documentation preparation does not itself execute those operations. The automatic verification workflow does not publish or deploy. Record native/manual observations with their actual scope and carry unrun checks as limitations; do not substitute automated WebKit/axe checks or publication authorization for human certification.
110
110
 
@@ -120,6 +120,19 @@ npm publication, GitHub Releases, main merge and hosted Docs are distinct operat
120
120
 
121
121
  `vercel.json` disables Git deployments only for `feat/public-ui-readiness`. Other branches retain Vercel's default behavior, so merging or pushing another branch requires a separate deployment decision. The automatic GitHub Actions checks are verification-only; enabling repository workflows is not evidence of a hosted site or approved release.
122
122
 
123
+
124
+ ### Manual exact-archive preparation for 0.9.0 (following 0.8.0)
125
+
126
+ The standing `scripts/rollout/prepare-release.mjs` accepts consecutive stable 0.7.x releases only. Do not change rollout policy, `release.json` or the chrome contract to admit 0.9.0. Use the manual release procedure above: complete G1–G7 from the [adopted root-frame contract](https://github.com/naeil-dev/naeil-ui/blob/434b7d07235316097ca3a55265ee02c1422b0cfd/docs/superpowers/specs/2026-10-29-page-root-scrollbar-gutter.md), including the retained 0.8.0 P1–P5 regressions, freeze the exact source SHA/archive, then run boundary and clean React/Next checks with `NAEIL_UI_EXPECTED_ARCHIVE_SHA256`. The paired browser gate accepts `NAEIL_UI_CHROME_PACKAGE=/absolute/extracted/package` to exercise that exact archive, alongside SHA-512-pinned 0.7.2 and SHA-256-pinned frozen 0.8.0. Reference-only root/lock CSS adjustments are separate hashed fixture files; immutable archives stay untouched. Both classic and overlay jobs require actual probes. Viewport captures have geometry guards; all acceptance measurement precedes any terminal fullPage capture. It retains numeric differences and screenshots for human review; new renders never replace expectations automatically.
127
+
128
+ Before npm publication, the separately approved source push must make the linked adopted contract at `434b7d07235316097ca3a55265ee02c1422b0cfd` public. Immediately after that push, verify an anonymous HTTP 200 for the exact guide URL and confirm its status says **rev 1, adopted 2026-10-29**. Local link/boundary checks do not establish public reachability. Source push may trigger Vercel deployment and retains its own approval boundary.
129
+
130
+ Published 0.8.0 adds `appNavigation`, `brandSuffix`, `statusSlot` and controlled/observer sheet props; see [NaeilHeader](components/naeil-header.md) for supported host CSS and consumer-supplied fonts. Default catalog/blog/legacy overrides must remain equivalent to B1. Home Workspace uses the default account toolbar and full footer; LoginGate omits auth/separator and footer (F1/F2). A source rollback must revert adapter, CSS and manifest/lock together; pin-only rollback cannot support the new API.
131
+
132
+ The shipped guide records the B1 empty-menu desktop limitation, controlled-mobile asynchronous account/focus handoff versus desktop/uncontrolled selection-time behavior, and Home's confirmation-owned desktop cancel fallback. Compatibility comparisons normalize only the contract markers/language/release identity and Radix-generated IDs; SVG/Logo and consumer IDs stay exact. Source or guide corrections require a new archive and exact-archive gates; earlier candidate digests are historical evidence.
133
+
134
+ Package publication, tag, GitHub Release, main merge and Home deployment each require separate explicit approval. naeil.dev imports this repository's source, so a main merge upgrades its default header to the 0.8.0 API/style plus the 0.9.0 root/lock rule (including Japanese 320px classic F3 wrap) and is held until the owner approves that site impact and timing. Estimate/Jev stay on 0.7.2, and STOP remains. A prepared archive or local gate result grants no publication or rollout authority.
135
+
123
136
  ## Storybook preview artifact
124
137
 
125
138
  `pnpm build:storybook` builds 17 actual Docs pages: 15 UI families (the 13 primitives plus Navigation and SiteFooter) and Brand / Logo and NaeilHeader. Interactive examples include the complete branded header, JapaneseApplication, canonical footer ReferenceFull/ReferenceCompact and AccountStates. Canonical footer/account examples live within existing stories, without a separate NaeilFooter Docs family. It copies generated documentation notices and approved canonical brand SVGs, bundles locally installed Pretendard/Noto Sans JP and disables Vite publicDir. The artifact check verifies real Docs/story entries, exact MIT/shadcn/font notices, brand references and absence of website public images/SVGs/HTML. It does not claim those excluded site assets have cleared rights.
@@ -148,8 +161,16 @@ Select **Publish reviewed UI docs (manual only)** in Actions (or use the command
148
161
  gh workflow run publish-docs-manual.yml --ref main -f reviewed_sha=FULL_REVIEWED_DOCS_COMMIT_SHA -f confirmation=PUBLISH_REVIEWED_DOCS
149
162
  ```
150
163
 
151
- After the 0.7.1 Docs deployment, verify the manager and iframe under `/naeil-ui/`, all 15 UI and 2 Brand Docs pages, their Usage examples, NaeilHeader JapaneseApplication, canonical footer full/compact destinations, and signed-out/signed-in/menu/busy account states. Check pinned `v0.7.1` guide links, canonical artwork and official image references, local fonts and exact LICENSE/THIRD_PARTY_NOTICES/font notices over HTTP. Retain the deployment URL, Docs source SHA, workflow run and observed results separately from the package source/tag. Hosted Storybook uses simulated data and requires no Supabase project, design API key or external font server. Only the reviewed Storybook directory is uploaded; website public assets stay excluded.
164
+ After the reviewed Docs deployment, verify the manager and iframe under `/naeil-ui/`, all 15 UI and 2 Brand Docs pages, their Usage examples, NaeilHeader JapaneseApplication, canonical footer full/compact destinations, and signed-out/signed-in/menu/busy account states. Check the maintained `v0.7.2` guide links (or the separately approved release target), canonical artwork and official image references, local fonts and exact LICENSE/THIRD_PARTY_NOTICES/font notices over HTTP. Retain the deployment URL, Docs source SHA, workflow run and observed results separately from the package source/tag. Hosted Storybook uses simulated data and requires no Supabase project, design API key or external font server. Only the reviewed Storybook directory is uploaded; website public assets stay excluded.
152
165
 
153
166
  Normal builds are network-independent after dependency installation. Vite collects notices for actual preview modules; checked-in manager notices conservatively cover named embedded packages and versions from Storybook 10.4.6's original source lock. `check-storybook` fails if a named embedded module lacks a full notice. Four vendored tiny modules have no embedded version in that lock/comments; their original npm notice source is explicitly marked, without an exact-version claim. The exact use-composed-ref@1.4.0 tarball/repository provides MIT metadata but no standalone notice; its metadata plus full declared terms are retained without inventing a year/holder. The client-only@0.0.1 React marker retains its exact metadata and the original React MIT notice. Other missing notices fail the build; no generic MIT fallback passes arbitrary packages.
154
167
 
155
168
  For a Storybook dependency upgrade, first build the new preview to obtain fresh manager scripts, then run `python3 scripts/refresh-storybook-notices.py` (read-only npm/source retrieval plus a local notice-file update), review its source/notice changes, and rebuild. The refresh is opt-in and needs network; the build never retrieves legal/reference material silently. Keep all generated `licenses/` JSON/text files with the preview. The standalone React consumer likewise emits exact installed code/font notices with its production output.
169
+
170
+ ### 0.9.0 frame preparation gates
171
+
172
+ The owner adopted rev 1 on 2026-10-29 for local implementation and verification, including the root opt-out and F3 wrap. Both CSS graphs, theme/JS-only controls, opt-out and false-support branch, short/long route frames, body/content/fixed anchors under nested Radix locks, capture guards, frozen 0.8.0 comparisons and clean installed React/Next production CSS must pass before freezing. The false-support fixture tests a CSS branch, not a real unsupported engine. `build-chrome-fixtures.ts` alone is not clean-consumer proof; `check-consumers.ts` independently installs/builds/exercises both paths in each framework.
173
+
174
+ Run the normal checks above serially, plus the two deterministic Chromium mode jobs using B0's process-local wrappers, `NAEIL_UI_SCROLLBAR_MODE=classic|overlay` and `NAEIL_UI_SCROLLBAR_WRAPPERS=/absolute/wrapper-directory`. Their 15/0px overflow probes and stable-root frame probes must pass; the diagnostic `--enable-automation` flag exposes actual argv through CDP without changing the mode probe. Preserve exact source/archive SHA-256/SRI/inventory and per-gate commands/exit codes outside the archive. Repeat archive-dependent checks using `NAEIL_UI_CHROME_PACKAGE=/absolute/extracted/package` and `NAEIL_UI_EXPECTED_ARCHIVE_SHA256`.
175
+
176
+ This candidate is not published. npm/dist-tag, version tag, GitHub Release, push/main merge (= naeil.dev production), hosted Docs and Home bump/deploy remain separate approvals. Home keeps its local root rule until an approved exact-version upgrade removes it; rollback restores manifest/lock and local root rule together. Estimate/Jev remain on 0.7.2 and STOP remains in force.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naeil/ui",
3
- "version": "0.7.2",
3
+ "version": "0.9.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "sideEffects": [
@@ -1,3 +1,20 @@
1
+ @layer base {
2
+ html:not([data-naeil-scrollbar-gutter="auto"]) {
3
+ scrollbar-gutter: stable;
4
+ }
5
+ html[data-naeil-scrollbar-gutter="auto"] {
6
+ scrollbar-gutter: auto;
7
+ }
8
+ }
9
+
10
+ /* The root already reserves the gap. Keep Radix's lock, without a second gap. */
11
+ @supports (scrollbar-gutter: stable) {
12
+ html:not([data-naeil-scrollbar-gutter="auto"]) > body[data-scroll-locked] {
13
+ margin-right: 0 !important;
14
+ --removed-body-scroll-bar-size: 0px;
15
+ }
16
+ }
17
+
1
18
  @layer components {
2
19
  .ui-navigation {
3
20
  position: relative;
@@ -536,6 +553,119 @@
536
553
  }
537
554
  }
538
555
 
556
+
557
+ /* Canonical chrome owns final typography. Unlayered, marker-scoped rules also
558
+ beat supported (0,3,0) host rules even when loaded later. Body portals stay local. */
559
+ html:root [data-naeil-chrome][lang] {
560
+ /* Keep the base :lang(ja) fallback local while slot font utilities still win. */
561
+ --font-sans: 'Pretendard Variable', Pretendard, 'Noto Sans JP', -apple-system, system-ui, sans-serif;
562
+ --naeil-chrome-font: "Pretendard Variable", Pretendard, "Noto Sans JP", -apple-system, system-ui, sans-serif;
563
+ font-family: var(--naeil-chrome-font);
564
+ font-size: 16px;
565
+ font-weight: 400;
566
+ line-height: 1.5625;
567
+ letter-spacing: normal;
568
+ -webkit-font-smoothing: antialiased;
569
+ -moz-osx-font-smoothing: grayscale;
570
+ }
571
+ html:root [data-naeil-chrome][lang] .ui-navigation-link:not([data-app-toolbar] *):not([data-app-brand]),
572
+ html:root [data-naeil-chrome][lang] .naeil-header-login,
573
+ html:root [data-naeil-chrome][lang] .naeil-header-account-trigger,
574
+ html:root [data-naeil-chrome][lang] .ui-navigation-title,
575
+ html:root [data-naeil-chrome][lang][data-app-toolbar] > [data-slot="button"]:first-child,
576
+ html:root [data-naeil-chrome][lang][data-app-toolbar] > [data-slot="select-trigger"]:nth-child(2) {
577
+ font-family: var(--naeil-chrome-font);
578
+ font-size: 16px;
579
+ font-weight: 400;
580
+ line-height: 1.5625;
581
+ letter-spacing: normal;
582
+ }
583
+ html:root [data-naeil-chrome][lang] :is(.ui-navigation-title, .naeil-header-login) {
584
+ font-weight: 500;
585
+ }
586
+ html:root [data-naeil-chrome][lang] .ui-navigation-entry[aria-current="page"] .ui-navigation-title {
587
+ font-weight: 600;
588
+ }
589
+ html:root [data-naeil-chrome][lang] :is(.ui-navigation-description, .ui-navigation-group-label, .ui-site-footer-description, .ui-site-footer-label, .ui-site-footer-link, .ui-site-footer-meta) {
590
+ font-family: var(--naeil-chrome-font);
591
+ font-size: 14px;
592
+ font-weight: 400;
593
+ line-height: 1.5625;
594
+ letter-spacing: normal;
595
+ }
596
+ html:root [data-naeil-chrome][lang] :is(.ui-navigation-group-label, .ui-site-footer-label) {
597
+ font-weight: 600;
598
+ }
599
+ html:root [data-naeil-chrome][lang] .ui-navigation-sheet-heading > h2 {
600
+ font-family: var(--naeil-chrome-font);
601
+ font-size: 20px;
602
+ font-weight: 600;
603
+ line-height: 28px;
604
+ letter-spacing: normal;
605
+ }
606
+ html:root [data-naeil-chrome][lang] :is([data-slot="select-item"], [data-slot="dropdown-menu-item"]) {
607
+ font-family: var(--naeil-chrome-font);
608
+ font-size: 16px;
609
+ font-weight: 400;
610
+ line-height: 24px;
611
+ letter-spacing: normal;
612
+ }
613
+ html:root [data-naeil-chrome][lang] [data-slot="dropdown-menu-label"] {
614
+ font-family: var(--naeil-chrome-font);
615
+ font-size: 14px;
616
+ font-weight: 400;
617
+ line-height: 20px;
618
+ letter-spacing: normal;
619
+ }
620
+ html:root [data-naeil-chrome][lang] [data-slot="dropdown-menu-label"] > .text-base {
621
+ font-family: var(--naeil-chrome-font);
622
+ font-size: 16px;
623
+ font-weight: 500;
624
+ line-height: 24px;
625
+ letter-spacing: normal;
626
+ }
627
+ html:root [data-naeil-chrome][lang] [data-slot="dropdown-menu-label"] > .text-sm {
628
+ font-family: var(--naeil-chrome-font);
629
+ font-size: 14px;
630
+ font-weight: 400;
631
+ line-height: 20px;
632
+ letter-spacing: normal;
633
+ }
634
+ html:root [data-naeil-chrome][lang][data-app-toolbar] > [data-slot="button"]:first-child {
635
+ font-weight: 500;
636
+ line-height: 24px;
637
+ }
638
+ html:root [data-naeil-chrome][lang]:lang(ja),
639
+ html:root [data-naeil-locale-menu][lang] :lang(ja) {
640
+ --naeil-chrome-font: "Noto Sans JP", "Pretendard Variable", Pretendard, "Noto Sans JP", -apple-system, system-ui, sans-serif;
641
+ font-family: var(--naeil-chrome-font);
642
+ }
643
+
644
+ .naeil-header-brand-row {
645
+ display: flex;
646
+ align-items: center;
647
+ gap: 8px;
648
+ width: max-content;
649
+ }
650
+ html:root [data-naeil-chrome][lang] .naeil-header-brand-suffix {
651
+ font-family: var(--naeil-chrome-font);
652
+ margin-inline-start: 4px;
653
+ font-size: 14px;
654
+ font-weight: 400;
655
+ line-height: 21.875px;
656
+ letter-spacing: normal;
657
+ white-space: nowrap;
658
+ transform: translateY(1px);
659
+ }
660
+ @supports (text-box-trim: trim-both) {
661
+ html:root [data-naeil-chrome][lang] .naeil-header-brand-suffix {
662
+ font-family: var(--naeil-chrome-font);
663
+ text-box-trim: trim-both;
664
+ text-box-edge: cap alphabetic;
665
+ transform: none;
666
+ }
667
+ }
668
+
539
669
  @keyframes ui-reveal {
540
670
  from {
541
671
  opacity: 0;