@iyulab/modern-app 0.22.0 → 0.23.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,47 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.23.0] - 2026-09-21
4
+
5
+ ### Fixed
6
+
7
+ - **Route content no longer paints through an open overlay.** The overlay frame sits at
8
+ `z-index: 1`, while route content routinely holds positioned descendants with a higher one —
9
+ a data grid's sticky header row and pinned columns sit at 2–4. Neither the content area nor
10
+ its wrapper established a stacking context, so those competed with the overlay frame in the
11
+ same context and won: the list showed through the panel meant to cover it. While the overlay
12
+ is open, the wrapper holding route content is now its own stacking context, so no `z-index`
13
+ in route content can outrank the panel. Raising the frame's own `z-index` was rejected as a
14
+ fix — it only moves the finish line, and a larger value in route content would win again.
15
+ The boundary applies only while the overlay is open, so nothing else about how route content
16
+ paints changes.
17
+
18
+ ### Added
19
+
20
+ - **`part="main-content"`** on the wrapper holding route content, so the `inert` the shell
21
+ applies while the overlay is open is visible from outside. It is applied to this wrapper and
22
+ not to `part="main"` — that one is the scroll container and stays interactive — which made it
23
+ easy to read `inert` off `part="main"`, see `false`, and conclude the shell was not applying
24
+ it at all.
25
+
26
+ ### Documentation
27
+
28
+ - **The overlay's ownership contract is now written down** — what the shell provides (placement,
29
+ paint order, `inert`, the close affordance, keeping the route mounted) and what stays with the
30
+ consumer (moving and restoring focus, Escape-to-close, any scrim, the panel's accessible
31
+ name). `docs/layout.md` had never documented the overlay slot at all, and its parts table was
32
+ missing `overlay` and `overlay-close`; both now match the skill pack's reference.
33
+
34
+ ## [0.22.1] - 2026-09-19
35
+
36
+ ### Fixed
37
+
38
+ - **A wide route content no longer pushes the desktop shell past the viewport.** In 0.22.0 the
39
+ scroll region's wrapper lacked a horizontal minimum-size override, so a body wider than the
40
+ available width (a data grid whose columns sum past the viewport) grew the wrapper beyond the
41
+ viewport: the right-hand columns were clipped and neither the content area nor the page scrolled
42
+ horizontally. The wide content now scrolls inside the content area again, as it did before 0.22.0.
43
+ This is the horizontal counterpart of the mobile height fix shipped in 0.22.0.
44
+
3
45
  ## [0.22.0] - 2026-09-18
4
46
 
5
47
  ### Added
@@ -179,7 +179,7 @@ var T = class extends i {
179
179
  <div class="main" part="main" scrollable tabindex="-1" @keydown=${this._handleMainKeydown}>
180
180
  <u-progress-bar part="progress"></u-progress-bar>
181
181
 
182
- <div class="main-content" ?inert=${this.hasOverlay}>
182
+ <div class="main-content" part="main-content" ?inert=${this.hasOverlay}>
183
183
  <slot></slot>
184
184
  </div>
185
185
  </div>
@@ -186,11 +186,16 @@ var t = e`
186
186
  .main-region {
187
187
  position: relative;
188
188
  flex: 1;
189
- /* :host([state="mobile"]) 은 flex-direction: column 이라 이 축(높이)이 주축이 된다 —
190
- min-height: 0 없이는 flex item 의 자동 최소 크기가 콘텐츠 기준(.main 의 min-content
191
- 높이)으로 잡혀, flex-shrink 가 있어도 뷰포트보다 작게 줄지 못한다. 그 결과 .main 의
192
- overflow: auto 가 한 번도 발동하지 않고 셸 전체가 콘텐츠 높이만큼 부풀어 오른다. */
189
+ /* flex item 의 자동 최소 크기는 콘텐츠 기준(.main 의 min-content)이다 — 이 래퍼는
190
+ overflow 가 visible 이라 그 하한이 그대로 살아, flex-shrink 가 있어도 셸 밖으로
191
+ 커진다. 그러면 .main 의 overflow: auto 가 한 번도 발동하지 않는다. 주축이 어느 쪽이든
192
+ 같은 원인이라 두 축을 함께 푼다:
193
+ · 높이 — :host([state="mobile"]) 은 flex-direction: column 이라 min-height 가 필요하다.
194
+ 없으면 셸 전체가 콘텐츠 높이만큼 부풀어 오른다.
195
+ · 너비 — 데스크톱(row)에서는 min-width 가 필요하다. 없으면 넓은 표 하나가 셸을
196
+ 뷰포트 밖으로 밀어 오른쪽 열이 잘리고 가로 스크롤도 생기지 않는다. */
193
197
  min-height: 0;
198
+ min-width: 0;
194
199
  }
195
200
 
196
201
  .main {
@@ -206,6 +211,36 @@ var t = e`
206
211
  height: 100%;
207
212
  }
208
213
 
214
+ /*
215
+ * 오버레이가 열린 동안 «본문 영역»을 스태킹 컨텍스트로 만든다.
216
+ *
217
+ * 오버레이 틀은 z-index: 1 이고, 라우트 본문은 그보다 큰 z 를 일상적으로 쓴다 —
218
+ * 데이터 그리드의 sticky 머리행·고정 열이 2~4 에 있다. .main 도 .main-region 도
219
+ * 스태킹 컨텍스트를 만들지 않으므로 그것들과 오버레이 틀이 «같은 컨텍스트에서 직접
220
+ * 겨루고», 본문이 이긴다 — 덮으라고 띄운 패널을 목록이 뚫고 나온다.
221
+ *
222
+ * ⚠값을 올리는 처방(z-index 를 50 으로)은 소비자의 z 어휘가 커지면 같은 경주를 다시
223
+ * 한다. 경계를 세워야 한다.
224
+ *
225
+ * ⚠경계를 «어디에» 세우는가가 갈린다 — 넷을 픽셀 대조로 실측하고 골랐다:
226
+ * · .overlay 자신을 컨텍스트로 → 자기 z 는 여전히 1 이라 바깥에서 3 에게 진다. ✗
227
+ * · .main-region 을 컨텍스트로 → 본문과 오버레이를 «같은» 새 컨텍스트에 함께
228
+ * 가두므로 그 안에서 순서가 그대로다. ✗
229
+ * · .main / .main-content 를 «상시» 컨텍스트로 → 오버레이는 이기지만 본문 안의
230
+ * position: fixed 팝오버가 사이드바(z 1000) 위로 못 올라간다(상시 동작 변경).
231
+ * .main 쪽은 셸 자신의 진행 막대(z 100)까지 오버레이 아래로 내린다. ✗
232
+ * · 이 규칙 — 오버레이가 열린 동안만 .main-content 를 컨텍스트로 → 세 축 모두 유지. ✓
233
+ *
234
+ * ⇒ 경계가 필요한 조건은 정확히 «오버레이가 열려 있다» 이고, 그때만 세운다. 닫혀 있으면
235
+ * 겨룰 상대가 없다(.overlay.empty 는 display: none 이다).
236
+ * ⚠ .main 이 아니라 .main-content 인 것도 의도다 — 가두는 대상은 «소비자의 라우트 본문»
237
+ * 이지 셸 자신의 chrome 이 아니다. 진행 막대는 .main-content 밖(.main 직속)이라 이
238
+ * 경계에 걸리지 않고, 오버레이가 열린 채로 일어나는 라우트 전환을 계속 보고한다.
239
+ */
240
+ :host(:state(overlay)) .main-content {
241
+ isolation: isolate;
242
+ }
243
+
209
244
  .main u-progress-bar {
210
245
  --progress-bar-height: 4px;
211
246
  --progress-bar-track-color: transparent;
@@ -12,7 +12,7 @@ export type { SidebarSectionConfig } from '../components/SidebarSection';
12
12
  export type { SidebarGroupConfig } from '../components/SidebarGroup';
13
13
  export type { SidebarButtonConfig } from '../components/SidebarButton';
14
14
  /** 사이드바 레이아웃 컴포넌트의 요소(part) 타입 */
15
- export type SidebarParts = 'host' | 'mobile-header' | 'sidebar' | 'sidebar-header' | 'sidebar-main' | 'sidebar-footer' | 'main' | 'progress' | 'overlay' | 'overlay-close';
15
+ export type SidebarParts = 'host' | 'mobile-header' | 'sidebar' | 'sidebar-header' | 'sidebar-main' | 'sidebar-footer' | 'main' | 'main-content' | 'progress' | 'overlay' | 'overlay-close';
16
16
  /** 사이드바 상태 타입 */
17
17
  export type SidebarState = 'default' | 'slim' | 'modal' | 'mobile' | 'mobile-open';
18
18
  /** 사이드바 안에 HTML 또는 엘리먼트를 직접 렌더링하는 설정 */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@iyulab/modern-app",
3
3
  "description": "web-framework by iyulab based on lit-element",
4
- "version": "0.22.0",
4
+ "version": "0.23.0",
5
5
  "keywords": [
6
6
  "iyulab",
7
7
  "web-framework",
@@ -256,7 +256,8 @@ Parts available for `styles` overrides on the root layout:
256
256
  | `sidebar-header` | Logo + title area |
257
257
  | `sidebar-main` | Scrollable main nav area |
258
258
  | `sidebar-footer` | Pinned footer area |
259
- | `main` | Main content area |
259
+ | `main` | Main content area (the scroll container) |
260
+ | `main-content` | Wrapper holding route content inside `main` — the shell puts `inert` here while the overlay is open |
260
261
  | `progress` | Top progress bar |
261
262
  | `overlay` | Route-independent overlay panel above `main` |
262
263
  | `overlay-close` | Overlay's close button |
@@ -334,6 +335,35 @@ To keep the underlying route mounted while a URL parameter drives the overlay op
334
335
  query-string change doesn't remount the whole screen), give the route a `key` that excludes that
335
336
  parameter — see [`docs/routing.md`](../../../docs/routing.md) `RouteConfig.key`.
336
337
 
338
+ ### What the shell owns, and what it does not
339
+
340
+ The overlay is a **layer**, not a modal dialog. Splitting that precisely is what keeps a consumer
341
+ from either re-implementing what the shell already does, or dropping what it does not.
342
+
343
+ | Concern | Owner | How |
344
+ |---|---|---|
345
+ | Placement, size and scrolling of the panel | shell | `part="overlay"` covers the main region and scrolls on its own |
346
+ | Painting above route content | shell | while the overlay is open, `part="main-content"` becomes its own stacking context, so a `z-index` in route content cannot paint through the panel |
347
+ | Making route content non-interactive | shell | `inert` on `part="main-content"`, which propagates through the slot into your route content |
348
+ | A close affordance | shell | `part="overlay-close"`, firing the non-cancelable `overlay-close` event |
349
+ | Keeping the route mounted underneath | shell | the overlay is independent of routing |
350
+ | **Moving focus into the panel when it opens** | **consumer** | focus the panel or its first control yourself |
351
+ | **Restoring focus when it closes** | **consumer** | remember the trigger and re-focus it |
352
+ | **Escape to close** | **consumer** | the shell binds no key; listen for it and empty the slot |
353
+ | **A backdrop / dimmed scrim** | **consumer** | the panel is opaque and full-bleed by design; add a scrim inside your panel if you want one |
354
+ | **Announcing the panel to assistive tech** | **consumer** | put `role`/`aria-label` (or `aria-modal`, if you have made it modal) on *your* panel — the shell does not know what it holds |
355
+
356
+ ⚠ **`inert` is applied to `part="main-content"`, not to `part="main"`.** `part="main"` is the scroll
357
+ container and stays interactive so it can keep scrolling; the wrapper inside it is what goes inert.
358
+ Reading `inert` off `part="main"` reports `false` and says nothing about whether the shell applied it.
359
+ There is also no reliable way to check it by enumeration — `querySelectorAll` still returns focusable
360
+ elements inside an inert subtree. Inertness shows up when something tries to *take* focus, not when
361
+ you count what is there.
362
+
363
+ ⚠ **Page-level scroll is not locked**, deliberately. In the shell model `part="main"` is the scroller
364
+ and the overlay covers it, so there is nothing behind it to scroll. If your app instead lets the
365
+ document itself scroll, locking that is yours.
366
+
337
367
  No `overlayBreakpoint`/responsive toggle exists here — unlike `u-master-detail-layout`, this
338
368
  overlay is always an overlay, never a side-by-side pane. Use `u-master-detail-layout` instead
339
369
  when you want the panel to sit *beside* content on wide screens.