@iyulab/modern-app 0.3.3 → 0.3.5

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,99 +1,113 @@
1
1
  # Changelog
2
2
 
3
- ## 0.3.3 (2026-04-01)
3
+ ## [0.3.5] - 2026-04-07
4
4
 
5
- ### Fixes
6
- - **Sidebar 컴포넌트 customElement 등록명 수정**: `sidebar-*``u-sidebar-*` 접두사 추가
7
- - `SidebarButton`, `SidebarGroup`, `SidebarLink`, `SidebarSection` 4개 컴포넌트
8
- - `SidebarLayout` 템플릿에서 `u-sidebar-*`로 참조하지만 등록은 `sidebar-*`로 되어 있어 렌더링 안 되던 버그 수정
5
+ ### Changed
6
+ - Updated dependencies: `@iyulab/components` ^1.0.1 ^1.0.3, `@iyulab/router` ^0.7.4 ^0.9.1
9
7
 
10
- ## 0.3.2 (2026-04-01)
8
+ ## [0.3.4] - 2026-04-01
11
9
 
12
- ### Breaking Changes
13
- - **`@iyulab/components` major upgrade**: `^0.4.0` `^1.0.0`
14
- - `Notifier` `Toast` API migration
15
- - `AlertType` `AlertStatus`, `ScreenPosition` `ToastPosition` type rename
16
- - `IconRegistry` import path: `utilities/IconRegistry.js` → `utilities/icons.js`
17
- - Component import style: `.component.js` → side-effect import (`UIcon.js`, `UButton.js`, etc.)
10
+ ### Added
11
+ - Progress bar fade-in/out with CSS transition tied to `route-begin` and `route-done` events
12
+ - Progress bar error state (red) on `route-error` with auto-dismiss
13
+ - `docs/` topic guides and `skills/modern-app/` agent skill package
18
14
 
19
- ### Refactor
20
- - **Component registration migration**: Removed `static dependencies` pattern `@customElement` decorator + side-effect import
21
- - **Simplified Toast API**: Removed `private notify()` method from `App` class, each notification method now calls `Toast.notice()`, etc. directly
15
+ ### Changed
16
+ - Rewrote `README.md` with accurate API references and links to docs and skills
22
17
 
23
- ### Fixes
24
- - Changed `updated()` → `willUpdate()` in `SidebarLayout` to resolve update-in-update anti-pattern
25
- - Switched `SidebarLayout` icon library from inline `sidebar-app` to `bootstrap`, removed inline SVG registration code
18
+ ## [0.3.3] - 2026-04-01
26
19
 
27
- ### Dependencies
28
- - `@iyulab/components` ^0.4.0 ^1.0.0
29
- - `i18next` ^25.8.13 → ^25.10.10
30
- - `vite` ^7.3.1 → ^8.0.3
31
- - `@types/node` ^25.3.2 → ^25.5.0
20
+ ### Fixed
21
+ - Fixed missing `u-` prefix in `customElement` registration for `SidebarButton`, `SidebarGroup`, `SidebarLink`, and `SidebarSection` — components were referenced as `u-sidebar-*` in templates but registered as `sidebar-*`, causing them not to render
32
22
 
33
- ## 0.3.1 (2026-03-05)
23
+ ## [0.3.2] - 2026-04-01
34
24
 
35
- ### Breaking Changes
36
- - **Slot-based Outlet structure**: Replaced `<u-outlet>` inside `SidebarLayout` with `<slot>`, and moved `<u-outlet>` to Light DOM in `App`. This allows external CSS to apply correctly during React routing
25
+ ### Changed
26
+ - **Breaking:** Upgraded `@iyulab/components` from `^0.4.0` to `^1.0.0`:
27
+ - `Notifier` → `Toast` API migration
28
+ - `AlertType` → `AlertStatus`, `ScreenPosition` → `ToastPosition` type renames
29
+ - `IconRegistry` import path: `utilities/IconRegistry.js` → `utilities/icons.js`
30
+ - Component import style: `.component.js` → side-effect import (e.g. `UIcon.js`, `UButton.js`)
31
+ - Removed `static dependencies` pattern — switched to `@customElement` decorator with side-effect imports
32
+ - Simplified Toast API: each notification method now calls `Toast.notice()` etc. directly; removed private `notify()` from `App` class
33
+ - Updated dependencies: `i18next` ^25.8.13 → ^25.10.10, `vite` ^7.3.1 → ^8.0.3, `@types/node` ^25.3.2 → ^25.5.0
37
34
 
38
- ### Changes
39
- - **BaseElement → UElement migration**: Changed base class of all components from `BaseElement` to `UElement`
40
- - **SidebarLinkConfig**: Changed `type` property from optional to required
41
- - **Fixed import paths**: Corrected casing for `Theme.js`, `Notifier.js`, etc.
35
+ ### Fixed
36
+ - Changed `updated()` `willUpdate()` in `SidebarLayout` to resolve update-in-update anti-pattern
37
+ - Switched `SidebarLayout` icon library from inline `sidebar-app` to `bootstrap`; removed inline SVG registration
42
38
 
43
- ### Fixes
39
+ ## [0.3.1] - 2026-03-05
40
+
41
+ ### Changed
42
+ - **Breaking:** Replaced `<u-outlet>` inside `SidebarLayout` with `<slot>`; moved `<u-outlet>` to Light DOM in `App` to allow external CSS during React routing
43
+ - Migrated base class of all components from `BaseElement` to `UElement`
44
+ - `SidebarLinkConfig.type` changed from optional to required
45
+ - Corrected import path casing for `Theme.js`, `Notifier.js`, etc.
46
+ - Updated dependencies: `@iyulab/components` ^0.2.0 → ^0.4.0, `@iyulab/router` ^0.6.2 → ^0.7.2, `i18next` ^25.8.0 → ^25.8.13, `@types/node` ^25.0.9 → ^25.3.2
47
+
48
+ ### Fixed
44
49
  - Removed duplicate `color` property in `SidebarButton.styles.ts`
45
50
  - Cleaned up unused callback parameters in `repeat` directive
46
51
 
47
- ### Dependencies
48
- - `@iyulab/components` ^0.2.0 → ^0.4.0
49
- - `@iyulab/router` ^0.6.2 → ^0.7.2
50
- - `i18next` ^25.8.0 ^25.8.13
51
- - `@types/node` ^25.0.9 ^25.3.2
52
-
53
- ## 0.3.0 (2026-01-21)
54
-
55
- ### Breaking Changes
56
- - **Configuration Updates**:
57
- - Renamed `localization` to `i18n`
58
- - Renamed `layout.menu` to `layout.main`
59
- - Changed `logo` type to string (icon name)
60
- - Added `title` property for app title
61
- - Added `iconBasepath` option
62
- - **Mobile layout support**: New mobile states (`mobile`, `mobile-open`) with dedicated mobile header
63
-
64
- ### Removals
65
- - Removed `SidebarLogo` component: Logo is now rendered using `u-icon` component
66
- - Removed `observables.ts`: Replaced MobX observables with native `ScreenObserver` class
67
- - Removed `AppTypes.ts`: Type definitions moved to their respective modules
68
-
69
- ### Improvements
70
- - Improved responsive behavior with screen size observer
71
- - Added route context tracking and active link highlighting
72
- - Improved color scheme and typography
73
- - Enhanced transition animations
74
- - Cleaner layout spacing and gaps
75
- - Improved event handling for route changes and screen resizing
76
- - Enhanced pattern matching for active link detection
77
-
78
- ## 0.2.5 (2026-01-06)
52
+ ## [0.3.0] - 2026-01-21
53
+
54
+ ### Added
55
+ - Mobile layout support with new states (`mobile`, `mobile-open`) and dedicated mobile header
56
+ - Route context tracking and active link highlighting
57
+ - `ScreenObserver` class replacing MobX observables for responsive behavior
58
+ - `title` and `iconBasepath` properties in app configuration
59
+
60
+ ### Changed
61
+ - **Breaking:** `localization` config option renamed to `i18n`
62
+ - **Breaking:** `layout.menu` renamed to `layout.main`
63
+ - **Breaking:** `logo` type changed to string (icon name)
64
+ - Type definitions moved from `AppTypes.ts` to their respective modules
65
+ - Improved color scheme, typography, transition animations, layout spacing, and event handling
66
+
67
+ ### Removed
68
+ - Removed `SidebarLogo` component — logo now rendered via `u-icon`
69
+ - Removed `observables.ts` (replaced by `ScreenObserver`)
70
+ - Removed `AppTypes.ts`
71
+
72
+ ## [0.2.5] - 2026-01-06
73
+
74
+ ### Changed
79
75
  - Updated `@iyulab/components` to v0.1.11
80
76
 
81
- ## 0.2.4 (2025-12-19)
82
- - fix import path, modified correct case-sensitive paths
77
+ ## [0.2.4] - 2025-12-19
78
+
79
+ ### Fixed
80
+ - Fixed case-sensitive import paths
81
+
82
+ ## [0.2.3] - 2025-12-19
83
+
84
+ ### Added
85
+ - `root` option in `AppConfig` to set the application root element
86
+
87
+ ### Changed
88
+ - Updated `@iyulab/components` to v0.1.10
89
+
90
+ ## [0.2.2] - 2025-11-17
91
+
92
+ ### Added
93
+ - `fallback` config option in `AppConfig`
94
+
95
+ ### Changed
96
+ - Improved `route-progress` handling in layout component
97
+
98
+ ## [0.2.1] - 2025-11-13
99
+
100
+ ### Changed
101
+ - `progress` property changed from a value to a function on the `app` instance
102
+ - `locales` option renamed to `localization` in `AppConfig`
83
103
 
84
- ## 0.2.3 (2025-12-19)
85
- - added `root` option in `AppConfig` type to set application root element
86
- - update `@iyulab/components` to v0.1.10
104
+ ### Fixed
105
+ - Fixed component and sidebar style issues
87
106
 
88
- ## 0.2.2 (2025-11-17)
89
- - added `fallback` config in `AppConfig` type
90
- - improved route-progress handling in layout component
107
+ ### Removed
108
+ - Removed `theme` setter property from `app` instance
91
109
 
92
- ## 0.2.1 (2025-11-13)
93
- - Fixed few component and sidebar styles
94
- - removed `theme` setter property in `app` instance
95
- - changed `progress` property to function in `app` instance
96
- - changed name `locales` option to `localization` option in `AppConfig` type
110
+ ## [0.2.0] - 2025-11-12
97
111
 
98
- ## 0.2.0 (2025-11-12)
99
- - Initial library version release
112
+ ### Added
113
+ - Initial release
package/README.md CHANGED
@@ -1,8 +1,6 @@
1
- # Modern App
1
+ # @iyulab/modern-app
2
2
 
3
- A modern web application framework by iyulab, built on React and Lit Element.
4
-
5
- For complete examples and documentation, visit our demo site: [https://modern-app.iyulab.com](https://modern-app.iyulab.com)
3
+ A client-side SPA framework built on [Lit Element](https://lit.dev/) by iyulab. It bundles routing, a responsive sidebar layout, theme management, toast notifications, and i18n into a single `app` singleton.
6
4
 
7
5
  ## Installation
8
6
 
@@ -10,167 +8,119 @@ For complete examples and documentation, visit our demo site: [https://modern-ap
10
8
  npm install @iyulab/modern-app
11
9
  ```
12
10
 
13
- ## Architecture
14
-
15
- `@iyulab/modern-app` is a **client-side SPA framework** built on Lit Element. It is designed for:
16
-
17
- ✅ **Suitable for:**
18
- - Single Page Applications (SPA)
19
- - Admin dashboards and internal tools
20
- - Progressive Web Apps (PWA)
21
- - Projects where SEO is not a primary concern
22
-
23
- ❌ **Not suitable for:**
24
- - Server-Side Rendering (SSR) frameworks (Next.js, Nuxt, SvelteKit, etc.)
25
- - Static Site Generation (SSG)
26
- - SEO-critical public-facing pages
27
- - Projects requiring initial HTML content for search engines
11
+ ## When to use it
28
12
 
29
- This is by design Lit components render on the client side. If you need SSR capabilities, consider using Lit's experimental SSR support separately or choose a framework designed for SSR from the start.
13
+ | Good fit | Not a good fit |
14
+ |-------------|-----------------|
15
+ | Single Page Applications (SPA) | SSR frameworks (Next.js, Nuxt, SvelteKit) |
16
+ | Admin dashboards and internal tools | Static Site Generation (SSG) |
17
+ | Progressive Web Apps (PWA) | SEO-critical public-facing pages |
30
18
 
31
19
  ## Quick Start
32
20
 
33
21
  ```typescript
34
22
  import { app } from '@iyulab/modern-app';
23
+ import { html } from 'lit';
35
24
 
36
25
  await app.load({
37
26
  basepath: '/',
38
27
  layout: {
39
28
  type: 'sidebar',
40
- // ...layout configuration
29
+ logo: '/assets/logo.svg',
30
+ title: 'My App',
31
+ main: [
32
+ { type: 'link', icon: 'home', label: 'Home', href: '/' },
33
+ { type: 'link', icon: 'users', label: 'Users', href: '/users' },
34
+ ],
41
35
  },
42
36
  routes: [
43
- { index: true, render: () => html`<home-page></home-page>` },
44
- { path: 'about', render: () => html`<about-page></about-page>` },
37
+ { index: true, render: () => html`<home-page></home-page>` },
38
+ { path: 'users', render: () => html`<users-page></users-page>` },
39
+ { path: 'users/:id', render: (ctx) => html`<user-detail .userId=${ctx.params.id}></user-detail>` },
45
40
  ],
41
+ fallback: {
42
+ render: (ctx) => html`<error-page .error=${ctx.error}></error-page>`,
43
+ },
44
+ theme: { default: 'system' },
46
45
  });
47
46
  ```
48
47
 
49
- ## API Reference
48
+ ## Skills Usage
50
49
 
51
- ### Navigation
50
+ AI agent skills for this package are located in `skills/modern-app/`. Install them with `npx skills`:
52
51
 
53
- ```typescript
54
- // Navigate to a path
55
- app.navigate('/path');
56
-
57
- // Access router instance
58
- app.router?.go('/path');
59
- app.router?.basepath; // Get base path
60
- app.router?.routes; // Get registered routes
61
- app.router?.context; // Get current route context
52
+ **From GitHub:**
53
+ ```bash
54
+ npx skills add iyulab/node-modern-app
62
55
  ```
63
56
 
64
- ### Theme Management
65
-
66
- ```typescript
67
- // Get current theme
68
- app.theme.get(); // Returns: 'system' | 'light' | 'dark' | undefined
69
-
70
- // Set theme
71
- app.theme.set('dark'); // 'system' | 'light' | 'dark'
72
-
73
- // Check initialization status
74
- app.theme.isInitialized;
57
+ **From local `node_modules`:**
58
+ ```bash
59
+ npx skills add ./node_modules/@iyulab/modern-app
75
60
  ```
76
61
 
77
- ### Notifications
78
-
79
- ```typescript
80
- // Display notifications (returns Promise<void>)
81
- await app.notice('Notice message');
82
- await app.info('Info message');
83
- await app.success('Success message');
84
- await app.warning('Warning message');
85
- await app.error('Error message');
86
-
87
- // With options
88
- await app.success('Saved!', {
89
- title: 'Success',
90
- duration: 5000, // milliseconds (default: 3000)
91
- position: 'top-right' // 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'
92
- });
93
- ```
62
+ ## Core API
94
63
 
95
- ### Localization
64
+ ### Navigation
96
65
 
97
66
  ```typescript
98
- // Access i18next instance
99
- app.localizer; // i18next instance
100
-
101
- // Usage with lit-i18n
102
- import { translate } from 'lit-i18n';
103
- html`<p>${translate('namespace::key')}</p>`;
67
+ app.navigate('/users/42'); // push a route
68
+ app.router?.go('/users/42'); // via router instance
69
+ app.router?.context; // current RouteContext
104
70
  ```
105
71
 
106
- ## Configuration
107
-
108
- ### AppConfig
72
+ ### Theme
109
73
 
110
74
  ```typescript
111
- interface AppConfig {
112
- root?: Element; // Root element (default: document.body)
113
- basepath?: string; // Base path for routing (default: '/')
114
- routes: RouteConfig[]; // Route definitions
115
- fallback?: FallbackConfig; // Error fallback route
116
- theme?: ThemeInitOptions; // Theme configuration
117
- localization?: i18next.InitOptions; // i18next options
118
- layout: LayoutConfig; // Layout configuration
119
- }
75
+ app.theme.get(); // 'system' | 'light' | 'dark' | undefined
76
+ app.theme.set('dark');
77
+ app.theme.isInitialized; // boolean
120
78
  ```
121
79
 
122
- ### Theme Options
80
+ ### Notifications
123
81
 
124
82
  ```typescript
125
- interface ThemeInitOptions {
126
- default?: 'system' | 'light' | 'dark'; // Default theme
127
- debug?: boolean; // Enable debug logging
128
- store?: false | { // Persist theme preference
129
- type: 'cookie' | 'localStorage' | 'sessionStorage';
130
- prefix?: string;
131
- };
132
- useBuiltIn?: boolean; // Use built-in styles (default: true)
133
- }
83
+ await app.success('Saved!', { title: 'Done', duration: 4000, position: 'top-right' });
84
+ await app.error('Something went wrong');
85
+ await app.info('Info message');
86
+ await app.warning('Double-check this');
87
+ await app.notice('Neutral notice');
134
88
  ```
135
89
 
136
- ### Layout Configuration (Sidebar)
90
+ ### Localization (i18next)
137
91
 
138
92
  ```typescript
139
- interface SidebarLayoutConfig {
140
- type: 'sidebar';
141
- breakpoints?: [number, number]; // [small, medium] (default: [768, 1024])
142
- logo?: {
143
- type: 'icon' | 'image';
144
- icon?: string;
145
- src?: string;
146
- label?: string;
147
- onClick?: () => void;
148
- };
149
- menu?: MenuItem[]; // Navigation menu items
150
- footer?: FooterItem[]; // Footer buttons/items
151
- }
152
- ```
93
+ // Pass i18next plugins and InitOptions
94
+ await app.load({
95
+ // ...
96
+ i18n: {
97
+ plugins: [i18nextHttpBackend],
98
+ lng: 'en',
99
+ backend: { loadPath: '/locales/{{lng}}/{{ns}}.json' },
100
+ },
101
+ });
153
102
 
154
- ### Route Configuration
103
+ // Access i18next
104
+ app.i18n.t('common::greeting');
105
+ app.i18n.changeLanguage('ko');
155
106
 
156
- ```typescript
157
- interface RouteConfig {
158
- index?: boolean; // Index route
159
- path?: string; // Route path (supports :param patterns)
160
- title?: string; // Document title
161
- force?: boolean; // Force re-render
162
- render: (context: RouteContext) => RenderResult | Promise<RenderResult>;
163
- }
164
-
165
- interface RouteContext {
166
- href: string; // Full URL
167
- pathname: string; // Path portion
168
- basepath: string; // Base path
169
- params: Record<string, string>; // URL parameters
170
- progress: (value: number) => void; // Progress callback (0-100)
171
- }
107
+ // Reactive translations in Lit templates
108
+ import { translate } from 'lit-i18n';
109
+ html`<p>${translate('common::greeting')}</p>`;
172
110
  ```
173
111
 
112
+ ## Documentation
113
+
114
+ | Guide | Description |
115
+ |-------|-------------|
116
+ | [getting-started.md](./docs/getting-started.md) | Bootstrap, architecture, entry point setup |
117
+ | [routing.md](./docs/routing.md) | Route config, URL params, async routes, progress |
118
+ | [layout.md](./docs/layout.md) | Sidebar layout, all menu item types, responsive behaviour |
119
+ | [theme.md](./docs/theme.md) | Theme init, runtime switching, CSS tokens |
120
+ | [notifications.md](./docs/notifications.md) | Toast methods and options |
121
+ | [i18n.md](./docs/i18n.md) | i18next setup, plugins, lit-i18n usage |
122
+ | [configuration.md](./docs/configuration.md) | Full TypeScript interface reference |
123
+
174
124
  ## License
175
125
 
176
126
  MIT
@@ -1,4 +1,4 @@
1
- //#region \0@oxc-project+runtime@0.122.0/helpers/decorate.js
1
+ //#region \0@oxc-project+runtime@0.124.0/helpers/decorate.js
2
2
  function e(e, t, n, r) {
3
3
  var i = arguments.length, a = i < 3 ? t : r === null ? r = Object.getOwnPropertyDescriptor(t, n) : r, o;
4
4
  if (typeof Reflect == "object" && typeof Reflect.decorate == "function") a = Reflect.decorate(e, t, n, r);
@@ -1,4 +1,4 @@
1
- //#region \0@oxc-project+runtime@0.122.0/helpers/decorateMetadata.js
1
+ //#region \0@oxc-project+runtime@0.124.0/helpers/decorateMetadata.js
2
2
  function e(e, t) {
3
3
  if (typeof Reflect == "object" && typeof Reflect.metadata == "function") return Reflect.metadata(e, t);
4
4
  }
@@ -1,5 +1,5 @@
1
- import { __decorateMetadata as e } from "../_virtual/_@oxc-project_runtime@0.122.0/helpers/decorateMetadata.js";
2
- import { __decorate as t } from "../_virtual/_@oxc-project_runtime@0.122.0/helpers/decorate.js";
1
+ import { __decorateMetadata as e } from "../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorateMetadata.js";
2
+ import { __decorate as t } from "../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorate.js";
3
3
  import { StyledElement as n } from "../internals/StyledElement.js";
4
4
  import { styles as r } from "./SidebarButton.styles.js";
5
5
  import { html as i } from "lit";
@@ -1,5 +1,5 @@
1
- import { __decorateMetadata as e } from "../_virtual/_@oxc-project_runtime@0.122.0/helpers/decorateMetadata.js";
2
- import { __decorate as t } from "../_virtual/_@oxc-project_runtime@0.122.0/helpers/decorate.js";
1
+ import { __decorateMetadata as e } from "../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorateMetadata.js";
2
+ import { __decorate as t } from "../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorate.js";
3
3
  import { StyledElement as n } from "../internals/StyledElement.js";
4
4
  import { SidebarLink as r } from "./SidebarLink.js";
5
5
  import { styles as i } from "./SidebarGroup.styles.js";
@@ -1,5 +1,5 @@
1
- import { __decorateMetadata as e } from "../_virtual/_@oxc-project_runtime@0.122.0/helpers/decorateMetadata.js";
2
- import { __decorate as t } from "../_virtual/_@oxc-project_runtime@0.122.0/helpers/decorate.js";
1
+ import { __decorateMetadata as e } from "../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorateMetadata.js";
2
+ import { __decorate as t } from "../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorate.js";
3
3
  import { StyledElement as n } from "../internals/StyledElement.js";
4
4
  import { styles as r } from "./SidebarLink.styles.js";
5
5
  import { html as i } from "lit";
@@ -1,5 +1,5 @@
1
- import { __decorateMetadata as e } from "../_virtual/_@oxc-project_runtime@0.122.0/helpers/decorateMetadata.js";
2
- import { __decorate as t } from "../_virtual/_@oxc-project_runtime@0.122.0/helpers/decorate.js";
1
+ import { __decorateMetadata as e } from "../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorateMetadata.js";
2
+ import { __decorate as t } from "../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorate.js";
3
3
  import { StyledElement as n } from "../internals/StyledElement.js";
4
4
  import { styles as r } from "./SidebarSection.styles.js";
5
5
  import { html as i } from "lit";
@@ -1,5 +1,5 @@
1
- import { __decorateMetadata as e } from "../_virtual/_@oxc-project_runtime@0.122.0/helpers/decorateMetadata.js";
2
- import { __decorate as t } from "../_virtual/_@oxc-project_runtime@0.122.0/helpers/decorate.js";
1
+ import { __decorateMetadata as e } from "../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorateMetadata.js";
2
+ import { __decorate as t } from "../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorate.js";
3
3
  import { property as n } from "lit/decorators.js";
4
4
  import { UElement as r } from "@iyulab/components/dist/components/UElement.js";
5
5
  //#region src/internals/StyledElement.ts
@@ -49,6 +49,8 @@ export declare class SidebarLayout extends StyledElement<SidebarParts> {
49
49
  private handleRouteProgress;
50
50
  /** 라우트 변경 완료 핸들러 */
51
51
  private handleRouteDone;
52
+ /** 라우트 에러 핸들러 */
53
+ private handleRouteError;
52
54
  /** 화면 크기 변경에 따른 사이드바 상태 업데이트 */
53
55
  private handleScreenResize;
54
56
  }
@@ -1,6 +1,6 @@
1
1
  import { app as e } from "../App.js";
2
- import { __decorateMetadata as t } from "../_virtual/_@oxc-project_runtime@0.122.0/helpers/decorateMetadata.js";
3
- import { __decorate as n } from "../_virtual/_@oxc-project_runtime@0.122.0/helpers/decorate.js";
2
+ import { __decorateMetadata as t } from "../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorateMetadata.js";
3
+ import { __decorate as n } from "../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorate.js";
4
4
  import { StyledElement as r } from "../internals/StyledElement.js";
5
5
  import "../components/SidebarSection.js";
6
6
  import "../components/SidebarLink.js";
@@ -25,11 +25,17 @@ var m, h = class extends r {
25
25
  }, this.handleBackdropClick = () => {
26
26
  this.state = "slim";
27
27
  }, this.handleRouteBegin = (e) => {
28
- this.progressBarEl.value = 0, this.state === "modal" && (this.state = "slim"), this.state === "mobile-open" && (this.state = "mobile"), this.context = e.context;
28
+ this.progressBarEl.setAttribute("visible", ""), this.progressBarEl.value = 0, this.state === "modal" && (this.state = "slim"), this.state === "mobile-open" && (this.state = "mobile"), this.context = e.context;
29
29
  }, this.handleRouteProgress = (e) => {
30
30
  this.progressBarEl.value = e.progress;
31
31
  }, this.handleRouteDone = (e) => {
32
- this.progressBarEl.value = 100;
32
+ this.progressBarEl.value = 100, setTimeout(() => {
33
+ this.progressBarEl.removeAttribute("visible");
34
+ }, 300);
35
+ }, this.handleRouteError = (e) => {
36
+ this.progressBarEl.setAttribute("error", ""), this.progressBarEl.value = 100, setTimeout(() => {
37
+ this.progressBarEl.removeAttribute("visible"), this.progressBarEl.removeAttribute("error");
38
+ }, 300);
33
39
  }, this.handleScreenResize = (e) => {
34
40
  let t = e.detail.size;
35
41
  t === "large" ? this.state = "default" : t === "medium" ? this.state = "slim" : t === "small" ? this.state = "mobile" : console.warn("Unknown screen size:", t);
@@ -39,10 +45,10 @@ var m, h = class extends r {
39
45
  this.styles = [super.styles, i];
40
46
  }
41
47
  connectedCallback() {
42
- super.connectedCallback(), window.addEventListener("route-begin", this.handleRouteBegin), window.addEventListener("route-done", this.handleRouteDone), window.addEventListener("route-progress", this.handleRouteProgress), window.addEventListener("screen-resize", this.handleScreenResize);
48
+ super.connectedCallback(), window.addEventListener("route-begin", this.handleRouteBegin), window.addEventListener("route-done", this.handleRouteDone), window.addEventListener("route-progress", this.handleRouteProgress), window.addEventListener("route-error", this.handleRouteError), window.addEventListener("screen-resize", this.handleScreenResize);
43
49
  }
44
50
  disconnectedCallback() {
45
- window.removeEventListener("route-begin", this.handleRouteBegin), window.removeEventListener("route-done", this.handleRouteDone), window.removeEventListener("route-progress", this.handleRouteProgress), window.removeEventListener("screen-resize", this.handleScreenResize), super.disconnectedCallback();
51
+ window.removeEventListener("route-begin", this.handleRouteBegin), window.removeEventListener("route-done", this.handleRouteDone), window.removeEventListener("route-progress", this.handleRouteProgress), window.removeEventListener("route-error", this.handleRouteError), window.removeEventListener("screen-resize", this.handleScreenResize), super.disconnectedCallback();
46
52
  }
47
53
  willUpdate(e) {
48
54
  super.willUpdate(e), e.has("config") && (this.styles = this.config?.styles);
@@ -151,14 +151,25 @@ var t = e`
151
151
  }
152
152
 
153
153
  .main u-progress-bar {
154
+ --progress-bar-height: 4px;
155
+ --progres-bar-track-color: transparent;
156
+
154
157
  position: absolute;
155
158
  z-index: 100;
156
159
  top: 0;
157
160
  left: 0;
158
161
  right: 0;
159
- height: 4px;
160
- border-radius: 0;
161
- background-color: transparent;
162
+ opacity: 0;
163
+ transform: translateY(-4px);
164
+ transition: opacity 0.3s ease, transform 0.3s ease;
165
+ pointer-events: none;
166
+ }
167
+ .main u-progress-bar[visible] {
168
+ opacity: 1;
169
+ transform: translateY(0);
170
+ }
171
+ .main u-progress-bar[error] {
172
+ --progress-bar-color: var(--u-red-500);
162
173
  }
163
174
 
164
175
  /* Backdrop for modal mode */
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.3.3",
4
+ "version": "0.3.5",
5
5
  "keywords": [
6
6
  "iyulab",
7
7
  "web-framework",
@@ -15,6 +15,7 @@
15
15
  },
16
16
  "files": [
17
17
  "dist",
18
+ "skills",
18
19
  "package.json",
19
20
  "README.md",
20
21
  "CHANGELOG.md",
@@ -37,15 +38,15 @@
37
38
  "build": "vite build"
38
39
  },
39
40
  "dependencies": {
40
- "@iyulab/components": "^1.0.0",
41
- "@iyulab/router": "^0.7.4",
42
- "i18next": "^25.10.10",
41
+ "@iyulab/components": "^1.0.3",
42
+ "@iyulab/router": "^0.9.1",
43
+ "i18next": "^26.0.6",
43
44
  "lit": "^3.3.2"
44
45
  },
45
46
  "devDependencies": {
46
- "@types/node": "^25.5.0",
47
+ "@types/node": "^25.6.0",
47
48
  "typescript": "^5.9.3",
48
- "vite": "^8.0.3",
49
+ "vite": "^8.0.8",
49
50
  "vite-plugin-dts": "^4.5.4"
50
51
  }
51
52
  }
@@ -0,0 +1,219 @@
1
+ ---
2
+ name: modern-app
3
+ description: Client-side SPA framework built on Lit Element. Bootstraps an application with sidebar layout, client-side routing, theme management, toast notifications, and i18n (i18next). Use when working with the @iyulab/modern-app package — setting up a new app, configuring routes, adding navigation menu items, managing theme, showing toasts, or wiring up localization.
4
+ license: MIT
5
+ metadata:
6
+ author: iyulab
7
+ version: "0.3.4"
8
+ compatibility: Designed for Lit Element / TypeScript projects. Requires @iyulab/modern-app.
9
+ ---
10
+
11
+ # @iyulab/modern-app
12
+
13
+ A client-side SPA framework built on Lit Element. Provides a single `app` singleton that wires together routing, layout, theme, notifications, and i18n.
14
+
15
+ > **Suitable for:** SPAs, admin dashboards, internal tools, PWAs.
16
+ > **Not suitable for:** SSR (Next.js, Nuxt, SvelteKit), SSG, SEO-critical pages.
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install @iyulab/modern-app
22
+ ```
23
+
24
+ ---
25
+
26
+ ## Bootstrap
27
+
28
+ Call `app.load()` once at the entry point. All options are in [`AppConfig`](./references/api.md#appconfig).
29
+
30
+ ```typescript
31
+ import { app } from '@iyulab/modern-app';
32
+ import { html } from 'lit';
33
+
34
+ await app.load({
35
+ basepath: '/',
36
+ layout: {
37
+ type: 'sidebar',
38
+ logo: '/assets/logo.svg',
39
+ title: 'My App',
40
+ main: [
41
+ { type: 'link', icon: 'home', label: 'Home', href: '/' },
42
+ { type: 'link', icon: 'users', label: 'Users', href: '/users' },
43
+ ],
44
+ },
45
+ routes: [
46
+ { index: true, render: () => html`<home-page></home-page>` },
47
+ { path: 'users', render: () => html`<users-page></users-page>` },
48
+ { path: 'users/:id', render: (ctx) => html`<user-detail .userId=${ctx.params.id}></user-detail>` },
49
+ ],
50
+ fallback: {
51
+ render: (ctx) => html`<error-page .error=${ctx.error}></error-page>`,
52
+ },
53
+ });
54
+ ```
55
+
56
+ To tear down the app:
57
+
58
+ ```typescript
59
+ app.unload();
60
+ ```
61
+
62
+ ---
63
+
64
+ ## Navigation
65
+
66
+ ```typescript
67
+ app.navigate('/users/42'); // push route
68
+ app.router?.go('/users/42'); // same via router instance
69
+ app.router?.basepath; // base path string
70
+ app.router?.context; // current RouteContext
71
+ app.router?.routes; // registered routes
72
+ ```
73
+
74
+ ---
75
+
76
+ ## Theme
77
+
78
+ ```typescript
79
+ app.theme.get(); // 'system' | 'light' | 'dark' | undefined
80
+ app.theme.set('dark'); // 'system' | 'light' | 'dark'
81
+ app.theme.isInitialized; // boolean
82
+ ```
83
+
84
+ Theme `store` options persist the preference across sessions:
85
+
86
+ ```typescript
87
+ theme: {
88
+ default: 'system',
89
+ store: { type: 'localStorage', prefix: 'myapp' },
90
+ useBuiltIn: true,
91
+ }
92
+ ```
93
+
94
+ ---
95
+
96
+ ## Notifications (Toast)
97
+
98
+ All methods return `Promise<void>`.
99
+
100
+ ```typescript
101
+ await app.notice('A general notice');
102
+ await app.info('Loaded successfully');
103
+ await app.success('Saved!', { title: 'Done', duration: 4000, position: 'top-right' });
104
+ await app.warning('Check your input');
105
+ await app.error('Something went wrong');
106
+ ```
107
+
108
+ `position` values: `'top-right'` | `'top-left'` | `'bottom-right'` | `'bottom-left'`
109
+ `duration` default: `3000` ms
110
+
111
+ ---
112
+
113
+ ## Localization (i18next)
114
+
115
+ Pass standard i18next `InitOptions` plus an optional `plugins` array.
116
+
117
+ ```typescript
118
+ import i18nextHttpBackend from 'i18next-http-backend';
119
+
120
+ await app.load({
121
+ // ...
122
+ i18n: {
123
+ plugins: [i18nextHttpBackend],
124
+ lng: 'en',
125
+ fallbackLng: 'en',
126
+ backend: { loadPath: '/locales/{{lng}}/{{ns}}.json' },
127
+ },
128
+ });
129
+
130
+ // Access i18next instance
131
+ app.i18n.t('namespace::key');
132
+ ```
133
+
134
+ Use in Lit templates with `lit-i18n`:
135
+
136
+ ```typescript
137
+ import { translate } from 'lit-i18n';
138
+ html`<p>${translate('namespace::greeting')}</p>`;
139
+ ```
140
+
141
+ ---
142
+
143
+ ## Sidebar Layout
144
+
145
+ Full configuration reference: [references/layout.md](./references/layout.md)
146
+
147
+ ### Menu item types
148
+
149
+ | type | Description |
150
+ |------|-------------|
151
+ | `'link'` | Single navigation link with optional icon |
152
+ | `'group'` | Collapsible group of links |
153
+ | `'section'` | Labelled section grouping links and groups |
154
+ | `'button'` | Action button (non-navigation) |
155
+ | `'html'` | Custom Lit template rendered inline |
156
+
157
+ ```typescript
158
+ layout: {
159
+ type: 'sidebar',
160
+ logo: '/logo.svg',
161
+ title: 'App Name',
162
+ main: [
163
+ {
164
+ type: 'section',
165
+ title: 'Management',
166
+ items: [
167
+ { type: 'link', icon: 'users', label: 'Users', href: '/users' },
168
+ {
169
+ type: 'group', icon: 'settings', label: 'Settings',
170
+ items: [
171
+ { type: 'link', label: 'Profile', href: '/settings/profile' },
172
+ { type: 'link', label: 'Security', href: '/settings/security' },
173
+ ],
174
+ },
175
+ ],
176
+ },
177
+ ],
178
+ footer: [
179
+ { type: 'button', icon: 'logout', label: 'Logout', onClick: () => signOut() },
180
+ ],
181
+ }
182
+ ```
183
+
184
+ ### Responsive breakpoints
185
+
186
+ ```typescript
187
+ layout: {
188
+ type: 'sidebar',
189
+ breakpoints: [768, 1024], // [tablet-min-px, desktop-min-px]
190
+ // ...
191
+ }
192
+ ```
193
+
194
+ Sidebar states: `'default'` | `'slim'` | `'modal'` | `'mobile'` | `'mobile-open'`
195
+
196
+ ---
197
+
198
+ ## Routes with progress
199
+
200
+ ```typescript
201
+ {
202
+ path: 'dashboard',
203
+ title: 'Dashboard',
204
+ render: async (ctx) => {
205
+ ctx.progress(30);
206
+ const data = await fetchData();
207
+ ctx.progress(100);
208
+ return html`<dashboard-page .data=${data}></dashboard-page>`;
209
+ },
210
+ }
211
+ ```
212
+
213
+ `RouteContext` fields: `href`, `pathname`, `basepath`, `params`, `progress`
214
+
215
+ ---
216
+
217
+ ## Full `AppConfig` reference
218
+
219
+ See [references/api.md](./references/api.md) for all TypeScript interfaces.
@@ -0,0 +1,179 @@
1
+ # API Reference — @iyulab/modern-app
2
+
3
+ ## `AppConfig`
4
+
5
+ ```typescript
6
+ interface AppConfig {
7
+ /** Root element to render into. Default: document.body */
8
+ root?: Element;
9
+
10
+ /** Base path for all routes. Default: '/' */
11
+ basepath?: string;
12
+
13
+ /** Base URL for icon assets. Default: '/assets/icons/' */
14
+ iconBasepath?: string;
15
+
16
+ /** Route definitions. */
17
+ routes?: RouteConfig[];
18
+
19
+ /** Fallback rendered on 404 or unhandled errors. */
20
+ fallback?: FallbackRouteConfig;
21
+
22
+ /** Layout configuration. Currently only 'sidebar' is supported. */
23
+ layout: LayoutConfig;
24
+
25
+ /** Theme initialization options. */
26
+ theme?: ThemeInitOptions;
27
+
28
+ /** i18next options plus optional plugins array. Omit to skip i18n. */
29
+ i18n?: I18nInitOptions;
30
+ }
31
+ ```
32
+
33
+ ---
34
+
35
+ ## `LayoutConfig`
36
+
37
+ ```typescript
38
+ type LayoutConfig = SidebarLayoutConfig & {
39
+ /** Responsive breakpoints [tablet-min, desktop-min] in px. Default: [768, 1024] */
40
+ breakpoints?: [number, number];
41
+ };
42
+ ```
43
+
44
+ ---
45
+
46
+ ## `ThemeInitOptions`
47
+
48
+ ```typescript
49
+ interface ThemeInitOptions {
50
+ /** Initial theme. Default: 'system' */
51
+ default?: 'system' | 'light' | 'dark';
52
+
53
+ /** Log theme decisions to console. */
54
+ debug?: boolean;
55
+
56
+ /**
57
+ * Persist the user's preference.
58
+ * Set to `false` to disable persistence.
59
+ * Default: localStorage with no prefix.
60
+ */
61
+ store?: false | {
62
+ type: 'cookie' | 'localStorage' | 'sessionStorage';
63
+ prefix?: string;
64
+ };
65
+
66
+ /** Apply built-in CSS custom properties. Default: true */
67
+ useBuiltIn?: boolean;
68
+ }
69
+ ```
70
+
71
+ ---
72
+
73
+ ## `RouteConfig`
74
+
75
+ ```typescript
76
+ interface RouteConfig {
77
+ /** Matches the root path (equivalent to `path: ''`). */
78
+ index?: boolean;
79
+
80
+ /** Path string. Supports `:param` segments. */
81
+ path?: string;
82
+
83
+ /** Sets `document.title` when the route activates. */
84
+ title?: string;
85
+
86
+ /** Force a re-render even if the path did not change. */
87
+ force?: boolean;
88
+
89
+ /** Render function. May be async. */
90
+ render: (context: RouteContext) => RenderResult | Promise<RenderResult>;
91
+ }
92
+ ```
93
+
94
+ ---
95
+
96
+ ## `RouteContext`
97
+
98
+ ```typescript
99
+ interface RouteContext {
100
+ /** Full URL string. */
101
+ href: string;
102
+
103
+ /** Pathname portion of the URL. */
104
+ pathname: string;
105
+
106
+ /** Configured basepath. */
107
+ basepath: string;
108
+
109
+ /** Named URL parameters extracted from the path pattern. */
110
+ params: Record<string, string>;
111
+
112
+ /**
113
+ * Report loading progress (0–100).
114
+ * Drives the progress bar shown in the layout header.
115
+ */
116
+ progress: (value: number) => void;
117
+ }
118
+ ```
119
+
120
+ ---
121
+
122
+ ## `FallbackRouteConfig`
123
+
124
+ ```typescript
125
+ interface FallbackRouteConfig {
126
+ render: (context: RouteContext) => RenderResult | Promise<RenderResult>;
127
+ }
128
+ ```
129
+
130
+ The fallback `RouteContext` will include an `error` property when triggered by
131
+ a routing error.
132
+
133
+ ---
134
+
135
+ ## `I18nInitOptions`
136
+
137
+ ```typescript
138
+ type I18nInitOptions = i18next.InitOptions & {
139
+ /** i18next plugins to register via i18next.use() before init. */
140
+ plugins?: (Module | NewableModule<Module> | Newable<Module>)[];
141
+ };
142
+ ```
143
+
144
+ ---
145
+
146
+ ## `NotificationOptions`
147
+
148
+ ```typescript
149
+ interface NotificationOptions {
150
+ title?: string;
151
+ duration?: number; // milliseconds, default 3000
152
+ position?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left';
153
+ }
154
+ ```
155
+
156
+ ---
157
+
158
+ ## `app` singleton methods
159
+
160
+ | Method | Signature | Description |
161
+ |--------|-----------|-------------|
162
+ | `load` | `(config: AppConfig) => Promise<void>` | Initialize and mount the application |
163
+ | `unload` | `() => void` | Tear down layout, router, and observers |
164
+ | `navigate` | `(path: string) => void` | Push a new route |
165
+ | `notice` | `(msg, opts?) => Promise<void>` | Show a neutral toast |
166
+ | `info` | `(msg, opts?) => Promise<void>` | Show an info toast |
167
+ | `success` | `(msg, opts?) => Promise<void>` | Show a success toast |
168
+ | `warning` | `(msg, opts?) => Promise<void>` | Show a warning toast |
169
+ | `error` | `(msg, opts?) => Promise<void>` | Show an error toast |
170
+
171
+ ### `app` singleton properties
172
+
173
+ | Property | Type | Description |
174
+ |----------|------|-------------|
175
+ | `config` | `AppConfig \| undefined` | Current config passed to `load()` |
176
+ | `router` | `Router \| undefined` | Underlying `@iyulab/router` instance |
177
+ | `screen` | `ScreenSize \| undefined` | Current responsive screen size |
178
+ | `theme` | `Theme` (static) | Theme utility (`get`, `set`, `isInitialized`) |
179
+ | `i18n` | `i18next` | Raw i18next instance |
@@ -0,0 +1,204 @@
1
+ # Sidebar Layout Reference — @iyulab/modern-app
2
+
3
+ ## `SidebarLayoutConfig`
4
+
5
+ ```typescript
6
+ interface SidebarLayoutConfig {
7
+ type: 'sidebar';
8
+
9
+ /** URL or path to the logo image. */
10
+ logo?: string;
11
+
12
+ /** Application title displayed beside the logo. */
13
+ title?: string;
14
+
15
+ /** Main (top) navigation items. */
16
+ main?: SidebarItem[];
17
+
18
+ /** Footer (bottom-pinned) items. */
19
+ footer?: SidebarItem[];
20
+
21
+ /** Per-part style overrides (CSS custom properties / inline styles). */
22
+ styles?: StyleMap<SidebarParts>;
23
+ }
24
+ ```
25
+
26
+ ---
27
+
28
+ ## `SidebarItem` union
29
+
30
+ `SidebarItem` is the union of all six item types below.
31
+
32
+ ### `SidebarLinkConfig` — `type: 'link'`
33
+
34
+ A single navigation link. Highlights automatically when the current URL matches `href` (or `pattern`).
35
+
36
+ ```typescript
37
+ interface SidebarLinkConfig {
38
+ type: 'link';
39
+ label: string | DirectiveResult;
40
+ href: string;
41
+ icon?: string;
42
+ /** Override the URL matching pattern. Accepts a string or URLPattern. */
43
+ pattern?: string | URLPattern;
44
+ styles?: StyleMap<'host' | 'base' | 'icon' | 'label'>;
45
+ }
46
+ ```
47
+
48
+ Example:
49
+
50
+ ```typescript
51
+ { type: 'link', icon: 'dashboard', label: 'Dashboard', href: '/' }
52
+ ```
53
+
54
+ ---
55
+
56
+ ### `SidebarGroupConfig` — `type: 'group'`
57
+
58
+ Collapsible group that contains links.
59
+
60
+ ```typescript
61
+ interface SidebarGroupConfig {
62
+ type: 'group';
63
+ icon: string;
64
+ label: string | DirectiveResult;
65
+ items: SidebarLinkConfig[];
66
+ /** Start collapsed. Default: true */
67
+ collapsed?: boolean;
68
+ styles?: StyleMap<'host' | 'header' | 'icon' | 'label' | 'caret' | 'items'>;
69
+ }
70
+ ```
71
+
72
+ Example:
73
+
74
+ ```typescript
75
+ {
76
+ type: 'group',
77
+ icon: 'settings',
78
+ label: 'Settings',
79
+ collapsed: false,
80
+ items: [
81
+ { type: 'link', label: 'Profile', href: '/settings/profile' },
82
+ { type: 'link', label: 'Security', href: '/settings/security' },
83
+ ],
84
+ }
85
+ ```
86
+
87
+ ---
88
+
89
+ ### `SidebarSectionConfig` — `type: 'section'`
90
+
91
+ Labelled section that groups links and groups.
92
+
93
+ ```typescript
94
+ interface SidebarSectionConfig {
95
+ type: 'section';
96
+ title: string | DirectiveResult;
97
+ subTitle?: string | DirectiveResult;
98
+ items: (SidebarGroupConfig | SidebarLinkConfig)[];
99
+ styles?: StyleMap<'host' | 'header' | 'title' | 'subtitle' | 'items'>;
100
+ }
101
+ ```
102
+
103
+ Example:
104
+
105
+ ```typescript
106
+ {
107
+ type: 'section',
108
+ title: 'Administration',
109
+ items: [
110
+ { type: 'link', icon: 'users', label: 'Users', href: '/admin/users' },
111
+ { type: 'link', icon: 'database', label: 'Database', href: '/admin/db' },
112
+ ],
113
+ }
114
+ ```
115
+
116
+ ---
117
+
118
+ ### `SidebarButtonConfig` — `type: 'button'`
119
+
120
+ Action button — triggers a callback instead of navigating.
121
+
122
+ ```typescript
123
+ interface SidebarButtonConfig {
124
+ type: 'button';
125
+ icon?: string;
126
+ label: string | DirectiveResult;
127
+ onClick: () => void;
128
+ styles?: StyleMap<string>;
129
+ }
130
+ ```
131
+
132
+ Example:
133
+
134
+ ```typescript
135
+ { type: 'button', icon: 'logout', label: 'Sign Out', onClick: () => auth.signOut() }
136
+ ```
137
+
138
+ ---
139
+
140
+ ### `SidebarHtmlConfig` — `type: 'html'`
141
+
142
+ Renders a custom Lit template or raw HTML element. The `render` function
143
+ receives the current sidebar state so you can adapt the content.
144
+
145
+ ```typescript
146
+ interface SidebarHtmlConfig {
147
+ type: 'html';
148
+ render: (state: SidebarState) => TemplateResult<1> | HTMLElement | string;
149
+ }
150
+ ```
151
+
152
+ `SidebarState` values: `'default'` | `'slim'` | `'modal'` | `'mobile'` | `'mobile-open'`
153
+
154
+ Example:
155
+
156
+ ```typescript
157
+ {
158
+ type: 'html',
159
+ render: (state) => html`
160
+ <div class="user-card" ?hidden=${state === 'slim'}>
161
+ <img src="/avatar.png" />
162
+ <span>John Doe</span>
163
+ </div>
164
+ `,
165
+ }
166
+ ```
167
+
168
+ ---
169
+
170
+ ## Sidebar parts
171
+
172
+ Parts available for `styles` overrides on the root layout:
173
+
174
+ | Part | Element |
175
+ |------|---------|
176
+ | `host` | Outer layout shell |
177
+ | `mobile-header` | Top bar shown on mobile |
178
+ | `sidebar` | Sidebar panel |
179
+ | `sidebar-header` | Logo + title area |
180
+ | `sidebar-main` | Scrollable main nav area |
181
+ | `sidebar-footer` | Pinned footer area |
182
+ | `main` | Main content area |
183
+ | `progress` | Top progress bar |
184
+
185
+ ---
186
+
187
+ ## Responsive behaviour
188
+
189
+ | Screen width | Sidebar state |
190
+ |--------------|--------------|
191
+ | < breakpoints[0] | `mobile` / `mobile-open` |
192
+ | breakpoints[0] – breakpoints[1] | `slim` (icons only) |
193
+ | > breakpoints[1] | `default` (full labels) |
194
+
195
+ Default breakpoints: `[768, 1024]` px.
196
+ Override per app:
197
+
198
+ ```typescript
199
+ layout: {
200
+ type: 'sidebar',
201
+ breakpoints: [640, 1280],
202
+ // ...
203
+ }
204
+ ```