@naeil/ui 0.3.0 → 0.5.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 (46) hide show
  1. package/CHANGELOG.md +22 -2
  2. package/DESIGN.md +27 -0
  3. package/README.md +16 -9
  4. package/THIRD_PARTY_NOTICES.md +6 -0
  5. package/dist/chunk-CE73AEJZ.js +667 -0
  6. package/dist/{chunk-QKVN6Q6C.js → chunk-QSTGUEEI.js} +60 -175
  7. package/dist/chunk-S64H3OYQ.js +1512 -0
  8. package/dist/components/index.d.ts +33 -10
  9. package/dist/components/index.js +101 -557
  10. package/dist/components/naeil-header.d.ts +54 -0
  11. package/dist/components/naeil-header.js +14 -0
  12. package/dist/components/ui/index.d.ts +3 -1
  13. package/dist/components/ui/index.js +40 -179
  14. package/dist/navigation-CL10a6Eb.d.ts +48 -0
  15. package/dist/site-footer-B67meCDv.d.ts +21 -0
  16. package/dist/source/components/brand-artwork.generated.d.ts +162 -0
  17. package/dist/source/components/brand-artwork.generated.js +1103 -0
  18. package/dist/source/components/footer.d.ts +13 -4
  19. package/dist/source/components/footer.js +74 -114
  20. package/dist/source/components/index.d.ts +6 -4
  21. package/dist/source/components/locale-switcher.js +1 -1
  22. package/dist/source/components/logo.d.ts +13 -4
  23. package/dist/source/components/logo.js +83 -44
  24. package/dist/source/components/naeil-header-content.d.ts +171 -0
  25. package/dist/source/components/naeil-header-content.js +176 -0
  26. package/dist/source/components/naeil-header.d.ts +52 -0
  27. package/dist/source/components/naeil-header.js +196 -0
  28. package/dist/source/components/nav.d.ts +12 -4
  29. package/dist/source/components/nav.js +51 -384
  30. package/dist/source/components/ui/index.d.ts +2 -0
  31. package/dist/source/components/ui/index.js +4 -0
  32. package/dist/source/components/ui/navigation.d.ts +48 -0
  33. package/dist/source/components/ui/navigation.js +347 -0
  34. package/dist/source/components/ui/site-footer.d.ts +21 -0
  35. package/dist/source/components/ui/site-footer.js +93 -0
  36. package/docs/components/README.md +6 -1
  37. package/docs/components/common.md +1 -1
  38. package/docs/components/naeil-header.md +50 -0
  39. package/docs/components/navigation.md +59 -0
  40. package/docs/design/public-ui-support.md +17 -2
  41. package/docs/design/v2-migration.md +75 -11
  42. package/docs/package-release.md +54 -13
  43. package/licenses/Lato-OFL.txt +93 -0
  44. package/package.json +18 -4
  45. package/src/styles/components.css +292 -0
  46. package/src/styles/theme.css +4 -0
@@ -0,0 +1,347 @@
1
+ "use client";
2
+ var __defProp = Object.defineProperty;
3
+ var __defProps = Object.defineProperties;
4
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
5
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
8
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
+ var __spreadValues = (a, b) => {
10
+ for (var prop in b || (b = {}))
11
+ if (__hasOwnProp.call(b, prop))
12
+ __defNormalProp(a, prop, b[prop]);
13
+ if (__getOwnPropSymbols)
14
+ for (var prop of __getOwnPropSymbols(b)) {
15
+ if (__propIsEnum.call(b, prop))
16
+ __defNormalProp(a, prop, b[prop]);
17
+ }
18
+ return a;
19
+ };
20
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
21
+ var __objRest = (source, exclude) => {
22
+ var target = {};
23
+ for (var prop in source)
24
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
25
+ target[prop] = source[prop];
26
+ if (source != null && __getOwnPropSymbols)
27
+ for (var prop of __getOwnPropSymbols(source)) {
28
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
29
+ target[prop] = source[prop];
30
+ }
31
+ return target;
32
+ };
33
+ import { jsx, jsxs } from "react/jsx-runtime";
34
+ import * as React from "react";
35
+ import { Dialog, Popover } from "radix-ui";
36
+ import { Check, ChevronDown, Menu, X } from "lucide-react";
37
+ import { cn } from "../../lib/utils.js";
38
+ const NativeLink = React.forwardRef(
39
+ function NativeLink2(props, ref) {
40
+ return /* @__PURE__ */ jsx("a", __spreadValues({ ref }, props));
41
+ }
42
+ );
43
+ function DesktopGroup({
44
+ item,
45
+ active,
46
+ open,
47
+ anotherGroupOpen,
48
+ onOpenChange,
49
+ children
50
+ }) {
51
+ var _a;
52
+ const hoverOpened = React.useRef(false);
53
+ const closeTimer = React.useRef(
54
+ void 0
55
+ );
56
+ const panel = React.useRef(null);
57
+ const trigger = React.useRef(null);
58
+ const pointerType = React.useRef("");
59
+ const cancelClose = React.useCallback(() => {
60
+ clearTimeout(closeTimer.current);
61
+ }, []);
62
+ React.useEffect(() => {
63
+ return cancelClose;
64
+ }, [open, cancelClose]);
65
+ const leave = (event) => {
66
+ if (event.pointerType !== "mouse") return;
67
+ cancelClose();
68
+ const duration = getComputedStyle(event.currentTarget).getPropertyValue("--ui-motion-standard").trim();
69
+ closeTimer.current = setTimeout(
70
+ () => {
71
+ if (hoverOpened.current) onOpenChange(false);
72
+ },
73
+ parseFloat(duration) * (duration.endsWith("ms") ? 1 : 1e3)
74
+ );
75
+ };
76
+ return /* @__PURE__ */ jsxs(
77
+ Popover.Root,
78
+ {
79
+ open,
80
+ onOpenChange: (next) => {
81
+ cancelClose();
82
+ if (next) hoverOpened.current = false;
83
+ onOpenChange(next);
84
+ },
85
+ children: [
86
+ /* @__PURE__ */ jsxs(
87
+ Popover.Trigger,
88
+ {
89
+ ref: trigger,
90
+ className: "ui-navigation-link ui-focus",
91
+ "data-active": active || void 0,
92
+ onPointerEnter: (event) => {
93
+ if (event.pointerType !== "mouse") return;
94
+ cancelClose();
95
+ if (!open) {
96
+ hoverOpened.current = true;
97
+ onOpenChange(true);
98
+ }
99
+ },
100
+ onPointerLeave: leave,
101
+ onPointerDown: (event) => {
102
+ pointerType.current = event.pointerType;
103
+ },
104
+ onClick: (event) => {
105
+ var _a2, _b;
106
+ if (event.detail > 0 && pointerType.current === "mouse") {
107
+ event.preventDefault();
108
+ cancelClose();
109
+ hoverOpened.current = true;
110
+ onOpenChange(true);
111
+ return;
112
+ }
113
+ if (open && hoverOpened.current) {
114
+ event.preventDefault();
115
+ hoverOpened.current = false;
116
+ cancelClose();
117
+ (_b = (_a2 = panel.current) == null ? void 0 : _a2.querySelector("a[href]")) == null ? void 0 : _b.focus();
118
+ }
119
+ },
120
+ children: [
121
+ item.label,
122
+ /* @__PURE__ */ jsx(ChevronDown, { size: 16, "aria-hidden": "true" })
123
+ ]
124
+ }
125
+ ),
126
+ /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsx(
127
+ Popover.Content,
128
+ {
129
+ ref: panel,
130
+ "data-slot": "navigation-popover",
131
+ className: "ui-navigation-popover ui-popup",
132
+ "data-columns": (_a = item.columns) != null ? _a : 1,
133
+ align: "start",
134
+ sideOffset: 8,
135
+ "aria-label": item.label,
136
+ onPointerEnter: cancelClose,
137
+ onPointerLeave: leave,
138
+ onFocusCapture: () => {
139
+ hoverOpened.current = false;
140
+ cancelClose();
141
+ },
142
+ onOpenAutoFocus: (event) => {
143
+ if (hoverOpened.current) event.preventDefault();
144
+ },
145
+ onCloseAutoFocus: (event) => {
146
+ if (hoverOpened.current || anotherGroupOpen) event.preventDefault();
147
+ },
148
+ onInteractOutside: (event) => {
149
+ var _a2;
150
+ if ((_a2 = trigger.current) == null ? void 0 : _a2.contains(event.target))
151
+ event.preventDefault();
152
+ },
153
+ children: /* @__PURE__ */ jsx("nav", { "aria-label": item.label, className: "ui-navigation-menu-grid", children })
154
+ }
155
+ ) })
156
+ ]
157
+ }
158
+ );
159
+ }
160
+ function Navigation(_a) {
161
+ var _b = _a, {
162
+ brand,
163
+ items,
164
+ labels,
165
+ Link = NativeLink,
166
+ currentHref,
167
+ toolbar,
168
+ mobileToolbar,
169
+ mobileFooter,
170
+ containerClassName,
171
+ onNavigate,
172
+ className
173
+ } = _b, props = __objRest(_b, [
174
+ "brand",
175
+ "items",
176
+ "labels",
177
+ "Link",
178
+ "currentHref",
179
+ "toolbar",
180
+ "mobileToolbar",
181
+ "mobileFooter",
182
+ "containerClassName",
183
+ "onNavigate",
184
+ "className"
185
+ ]);
186
+ const [mobileOpen, setMobileOpen] = React.useState(false);
187
+ const [openGroup, setOpenGroup] = React.useState(null);
188
+ const trigger = React.useRef(null);
189
+ const desktop = React.useRef(null);
190
+ React.useEffect(() => {
191
+ setMobileOpen(false);
192
+ setOpenGroup(null);
193
+ }, [currentHref]);
194
+ React.useEffect(() => {
195
+ const element = desktop.current;
196
+ if (!element || typeof ResizeObserver === "undefined") return;
197
+ const observer = new ResizeObserver(() => {
198
+ if (element.getBoundingClientRect().width > 0) setMobileOpen(false);
199
+ else setOpenGroup(null);
200
+ });
201
+ observer.observe(element);
202
+ return () => observer.disconnect();
203
+ }, []);
204
+ const active = (item) => {
205
+ var _a2;
206
+ return (_a2 = item.active) != null ? _a2 : !item.external && item.href === currentHref;
207
+ };
208
+ function link(item, close, entry = false) {
209
+ const Component = item.external ? NativeLink : Link;
210
+ return /* @__PURE__ */ jsxs(
211
+ Component,
212
+ {
213
+ href: item.href,
214
+ target: item.target,
215
+ rel: item.target === "_blank" ? "noopener noreferrer" : void 0,
216
+ "aria-current": active(item) ? "page" : void 0,
217
+ className: cn(
218
+ "ui-navigation-link ui-focus",
219
+ entry && "ui-navigation-entry"
220
+ ),
221
+ onClick: (event) => {
222
+ onNavigate == null ? void 0 : onNavigate(event, item);
223
+ if (event.button === 0 && !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey)
224
+ close == null ? void 0 : close();
225
+ },
226
+ children: [
227
+ item.icon && /* @__PURE__ */ jsx("span", { className: "ui-navigation-icon", "aria-hidden": "true", children: item.icon }),
228
+ /* @__PURE__ */ jsxs("span", { className: "ui-navigation-copy", children: [
229
+ /* @__PURE__ */ jsxs("span", { className: "ui-navigation-title", children: [
230
+ /* @__PURE__ */ jsx("span", { children: item.label }),
231
+ item.badge,
232
+ entry && active(item) && /* @__PURE__ */ jsx(Check, { size: 16, className: "shrink-0", "aria-hidden": "true" })
233
+ ] }),
234
+ item.description && /* @__PURE__ */ jsx("span", { className: "ui-navigation-description", children: item.description })
235
+ ] })
236
+ ]
237
+ },
238
+ item.id
239
+ );
240
+ }
241
+ return /* @__PURE__ */ jsx(
242
+ "header",
243
+ __spreadProps(__spreadValues({
244
+ "data-slot": "navigation",
245
+ className: cn("ui-navigation", className)
246
+ }, props), {
247
+ children: /* @__PURE__ */ jsx("div", { className: cn("ui-navigation-frame", containerClassName), children: /* @__PURE__ */ jsxs("div", { className: "ui-navigation-row", children: [
248
+ /* @__PURE__ */ jsx("div", { className: "ui-navigation-brand", children: brand }),
249
+ /* @__PURE__ */ jsx(
250
+ "nav",
251
+ {
252
+ ref: desktop,
253
+ "aria-label": labels.navigation,
254
+ className: "ui-navigation-desktop",
255
+ children: items.map(
256
+ (item) => "items" in item ? /* @__PURE__ */ jsx(
257
+ DesktopGroup,
258
+ {
259
+ item,
260
+ active: item.items.some(active),
261
+ open: openGroup === item.id,
262
+ anotherGroupOpen: openGroup !== null && openGroup !== item.id,
263
+ onOpenChange: (open) => setOpenGroup(
264
+ (current) => open ? item.id : current === item.id ? null : current
265
+ ),
266
+ children: item.items.map(
267
+ (child) => link(child, () => setOpenGroup(null), true)
268
+ )
269
+ },
270
+ item.id
271
+ ) : link(item)
272
+ )
273
+ }
274
+ ),
275
+ /* @__PURE__ */ jsx("div", { className: "ui-navigation-toolbar", children: toolbar }),
276
+ /* @__PURE__ */ jsxs(Dialog.Root, { open: mobileOpen, onOpenChange: setMobileOpen, children: [
277
+ /* @__PURE__ */ jsx(
278
+ Dialog.Trigger,
279
+ {
280
+ ref: trigger,
281
+ className: "ui-navigation-toggle ui-navigation-link ui-focus",
282
+ "aria-label": labels.menu,
283
+ children: /* @__PURE__ */ jsx(Menu, { "aria-hidden": "true", size: 20 })
284
+ }
285
+ ),
286
+ /* @__PURE__ */ jsxs(Dialog.Portal, { children: [
287
+ /* @__PURE__ */ jsx(
288
+ Dialog.Overlay,
289
+ {
290
+ "data-slot": "navigation-overlay",
291
+ className: "ui-navigation-overlay ui-popup"
292
+ }
293
+ ),
294
+ /* @__PURE__ */ jsxs(
295
+ Dialog.Content,
296
+ {
297
+ "data-slot": "navigation-sheet",
298
+ className: "ui-navigation-sheet ui-popup",
299
+ "aria-describedby": void 0,
300
+ onCloseAutoFocus: (event) => {
301
+ var _a2, _b2;
302
+ if (trigger.current && trigger.current.getBoundingClientRect().width === 0) {
303
+ event.preventDefault();
304
+ (_b2 = (_a2 = desktop.current) == null ? void 0 : _a2.querySelector("button, a")) == null ? void 0 : _b2.focus();
305
+ }
306
+ },
307
+ children: [
308
+ /* @__PURE__ */ jsxs("div", { className: "ui-navigation-sheet-heading", children: [
309
+ /* @__PURE__ */ jsx(Dialog.Title, { className: "text-xl font-semibold", children: labels.menu }),
310
+ /* @__PURE__ */ jsx(
311
+ Dialog.Close,
312
+ {
313
+ className: "ui-navigation-link ui-focus",
314
+ "aria-label": labels.close,
315
+ children: /* @__PURE__ */ jsx(X, { size: 20, "aria-hidden": "true" })
316
+ }
317
+ )
318
+ ] }),
319
+ /* @__PURE__ */ jsx(
320
+ "nav",
321
+ {
322
+ "aria-label": labels.navigation,
323
+ className: "ui-navigation-mobile-links",
324
+ children: items.map(
325
+ (item) => "items" in item ? /* @__PURE__ */ jsxs("section", { "aria-label": item.label, children: [
326
+ /* @__PURE__ */ jsx("h3", { className: "ui-navigation-group-label", children: item.label }),
327
+ item.items.map(
328
+ (child) => link(child, () => setMobileOpen(false), true)
329
+ )
330
+ ] }, item.id) : link(item, () => setMobileOpen(false))
331
+ )
332
+ }
333
+ ),
334
+ (mobileToolbar != null ? mobileToolbar : toolbar) && /* @__PURE__ */ jsx("div", { className: "ui-navigation-mobile-toolbar", children: mobileToolbar != null ? mobileToolbar : toolbar }),
335
+ mobileFooter && /* @__PURE__ */ jsx("div", { className: "ui-navigation-mobile-footer", children: mobileFooter })
336
+ ]
337
+ }
338
+ )
339
+ ] })
340
+ ] })
341
+ ] }) })
342
+ })
343
+ );
344
+ }
345
+ export {
346
+ Navigation
347
+ };
@@ -0,0 +1,21 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as react from 'react';
3
+ import { NavigationLink, NavigationLinkComponent } from './navigation.js';
4
+
5
+ interface SiteFooterProps extends react.ComponentPropsWithoutRef<"footer"> {
6
+ brand?: react.ReactNode;
7
+ description?: react.ReactNode;
8
+ groups?: Array<{
9
+ id: string;
10
+ label: string;
11
+ links: NavigationLink[];
12
+ }>;
13
+ Link?: NavigationLinkComponent;
14
+ meta?: react.ReactNode;
15
+ containerClassName?: string;
16
+ variant?: "full" | "compact";
17
+ }
18
+ /** Product content and translations belong to the consumer, including legal copy. */
19
+ declare function SiteFooter({ brand, description, groups, Link, meta, children, containerClassName, className, variant, ...props }: SiteFooterProps): react_jsx_runtime.JSX.Element;
20
+
21
+ export { SiteFooter, type SiteFooterProps };
@@ -0,0 +1,93 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ var __objRest = (source, exclude) => {
21
+ var target = {};
22
+ for (var prop in source)
23
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
24
+ target[prop] = source[prop];
25
+ if (source != null && __getOwnPropSymbols)
26
+ for (var prop of __getOwnPropSymbols(source)) {
27
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
28
+ target[prop] = source[prop];
29
+ }
30
+ return target;
31
+ };
32
+ import { jsx, jsxs } from "react/jsx-runtime";
33
+ import { cn } from "../../lib/utils.js";
34
+ function SiteFooter(_a) {
35
+ var _b = _a, {
36
+ brand,
37
+ description,
38
+ groups = [],
39
+ Link,
40
+ meta,
41
+ children,
42
+ containerClassName,
43
+ className,
44
+ variant = "full"
45
+ } = _b, props = __objRest(_b, [
46
+ "brand",
47
+ "description",
48
+ "groups",
49
+ "Link",
50
+ "meta",
51
+ "children",
52
+ "containerClassName",
53
+ "className",
54
+ "variant"
55
+ ]);
56
+ return /* @__PURE__ */ jsx(
57
+ "footer",
58
+ __spreadProps(__spreadValues({
59
+ "data-slot": "site-footer",
60
+ "data-variant": variant,
61
+ className: cn("ui-site-footer", className)
62
+ }, props), {
63
+ children: /* @__PURE__ */ jsxs("div", { className: cn("ui-site-footer-frame", containerClassName), children: [
64
+ /* @__PURE__ */ jsxs("div", { className: "ui-site-footer-brand", children: [
65
+ brand,
66
+ description && /* @__PURE__ */ jsx("div", { className: "ui-site-footer-description", children: description }),
67
+ children
68
+ ] }),
69
+ groups.map((group) => /* @__PURE__ */ jsxs("nav", { "aria-label": group.label, children: [
70
+ /* @__PURE__ */ jsx("h2", { className: "ui-site-footer-label", children: group.label }),
71
+ group.links.map((item) => {
72
+ const Component = !item.external && Link ? Link : "a";
73
+ return /* @__PURE__ */ jsx(
74
+ Component,
75
+ {
76
+ href: item.href,
77
+ target: item.target,
78
+ rel: item.target === "_blank" ? "noopener noreferrer" : void 0,
79
+ className: "ui-site-footer-link ui-focus",
80
+ children: item.label
81
+ },
82
+ item.id
83
+ );
84
+ })
85
+ ] }, group.id)),
86
+ meta && /* @__PURE__ */ jsx("div", { className: "ui-site-footer-meta", children: meta })
87
+ ] })
88
+ })
89
+ );
90
+ }
91
+ export {
92
+ SiteFooter
93
+ };
@@ -1,6 +1,6 @@
1
1
  # Component usage guides
2
2
 
3
- Prepared `@naeil/ui@0.3.0` (unpublished). [Setup, themes, fonts and shared responsibilities](common.md). Run `pnpm storybook` and choose each family’s Docs or Usage entry. Select chooses a value; DropdownMenu chooses an action.
3
+ These guides target the **@naeil/ui 0.5.0 release candidate**; final archive verification and publication are pending. [Setup, themes, fonts and shared responsibilities](common.md). Run `pnpm storybook` for the current 17 Docs pages (15 UI and 2 Brand) and choose Docs or Usage. The [hosted Storybook](https://naeil-dev.github.io/naeil-ui/) was last verified for [0.4.0 on 2026-10-11](https://github.com/naeil-dev/naeil-ui/releases/tag/v0.4.0); this candidate does not establish a new hosted deployment. Select chooses a value; DropdownMenu chooses an action.
4
4
 
5
5
  | Family | Guide | Runnable story |
6
6
  | --- | --- | --- |
@@ -17,5 +17,10 @@ Prepared `@naeil/ui@0.3.0` (unpublished). [Setup, themes, fonts and shared respo
17
17
  | Checkbox | [Checkbox](./checkbox.md) | UI / Checkbox / Usage |
18
18
  | Tabs | [Tabs](./tabs.md) | UI / Tabs / Usage |
19
19
  | Textarea | [Textarea](./textarea.md) | UI / Textarea / Usage |
20
+ | Navigation | [Navigation](./navigation.md) | UI / Navigation / Usage |
21
+ | SiteFooter | [SiteFooter](./navigation.md) | UI / SiteFooter / Usage |
22
+ | NaeilHeader | [NaeilHeader](./naeil-header.md) | Brand / NaeilHeader / Usage, JapaneseApplication |
20
23
 
21
24
  [Field, native radio, Select and action composition](./composition.md) explains consumer state and semantic choices. [Demand decision](../design/component-demand.md) records why these two families were added.
25
+
26
+ Use opt-in `@naeil/ui/naeil-header` for the actual naeil.dev branded header, with official artwork URL references and consumer-owned routing, theme provider and authentication. Use generic `Navigation` from `/ui` for different product menus. [Navigation / SiteFooter](navigation.md) also documents the compatibility adapters.
@@ -1,6 +1,6 @@
1
1
  # Shared usage contract
2
2
 
3
- These guides describe the prepared **0.3.0, unpublished** package. Start with the [consumer setup and migration](../design/v2-migration.md). All listed exports are available from `@naeil/ui/ui`; 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 guides target the **@naeil/ui 0.5.0 release candidate**; final archive verification and registry publication are pending. The [hosted Docs](https://naeil-dev.github.io/naeil-ui/) were last verified for [0.4.0 on 2026-10-11](https://github.com/naeil-dev/naeil-ui/releases/tag/v0.4.0), while `pnpm storybook` shows the current candidate. 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
 
@@ -0,0 +1,50 @@
1
+ # NaeilHeader — branded whole-header composition
2
+
3
+ This guide targets the **@naeil/ui 0.5.0 release candidate**. Final archive verification and registry publication are pending. 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.
4
+
5
+ ```tsx
6
+ import { NaeilHeader } from '@naeil/ui/naeil-header';
7
+ import '@naeil/ui/globals.css';
8
+
9
+ // Inside the consumer's existing next-themes ThemeProvider:
10
+ <NaeilHeader locale="ja" Link={GuardedLink} currentHref={pathname}
11
+ authSlot={<LogoutButton />} />
12
+ ```
13
+
14
+ 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#시작하기).
15
+
16
+ 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.
17
+
18
+ ## Consumer contract
19
+
20
+ Consumers provide an existing next-themes provider, routing/dirty-save guard, current locale and real authentication behavior. Default destinations point to `https://naeil.dev/{locale}`; the site's localized Link instead uses `resolveHref={path => path}`. A supplied `Link` handles default official destinations, including cross-origin URLs. `onNavigate` can intercept brand and menu links; honor modified clicks and target behavior. Explicit `external: true` project overrides and `projectsExternal` use native anchors, so intercept them with `onNavigate` when a dirty guard is required.
21
+
22
+ Only provide `onLocaleChange` when the application's interface supports all three canonical locales (`en`, `ko`, `ja`). Omitting it removes the locale control; the component does not create application translations or shared preferences. `authSlot` contains the consumer's real session action and must retain at least 44px touch targets. It does not integrate site SSO, share credentials or create accounts. Theme preferences remain consumer-origin state. Without a working provider, rendering alone does not establish theme switching.
23
+
24
+ | Prop | Contract |
25
+ | --- | --- |
26
+ | `locale` | Required `en`, `ko` or `ja`; selects header-only copy. |
27
+ | `resolveHref` | Resolves canonical unlocalized paths; defaults to the official localized site. |
28
+ | `Link`, `currentHref`, `onNavigate` | Routing adapter, active destination and navigation interception inherited from Navigation. |
29
+ | `onLocaleChange` | Optional supported-language callback; omit for a single-language application. |
30
+ | `authSlot` | Consumer-owned authentication action in the default toolbar. |
31
+ | `brandHref`, `brandSlot`, `brandLinkProps`, `brandLinkComponent` | Explicit brand destination/content/anchor overrides; default accessible brand name is `.naeil`. |
32
+ | `projects`, `projectsExternal`, `showProjects`, `showBlog` | Legacy catalog/visibility/native-link overrides; defaults use the complete canonical catalog. |
33
+ | `subNav`, `currentProjectKey`, `currentProjectHref` | Legacy extra links and current-project destination. |
34
+ | `translations`, `labels` | Legacy header/project lookup overrides and navigation/menu/close accessible labels. |
35
+ | `toolbarSlot` | Replaces the complete default theme/locale/auth toolbar. |
36
+ | `mobileFooter`, `className`, `containerClassName` | Inherited mobile slot and style overrides; recheck frame/behavior after changes. |
37
+
38
+ Standard header attributes and the remaining inherited [Navigation props](navigation.md) remain available. Compatibility overrides preserve existing consumers; new applications normally use the canonical composition.
39
+
40
+ ## Frame and content clearance
41
+
42
+ The fixed header is 65px: the current 64px navigation token plus a 1px border. Clear it in the application's main, sticky regions and focus/anchor scroll margins with `calc(var(--ui-navigation-height) + 1px)`. The branded header uses the actual site's 1280px cap and 24px gutters; body/footer widths belong to the product. A 1600px workspace body does not change this header cap or create a new shared foundation token.
43
+
44
+ ## Artwork and package boundary
45
+
46
+ 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.
47
+
48
+ 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. The local Storybook provides **Brand / NaeilHeader / Docs, Usage, JapaneseApplication**; the Japanese example omits unsupported locale switching and uses a consumer-owned auth slot. Candidate examples do not establish hosted deployment.
49
+
50
+ 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).
@@ -0,0 +1,59 @@
1
+ # Navigation and SiteFooter
2
+
3
+ This guide targets the **0.5.0 release candidate**; final archive verification and publication are pending. These are generic product-menu primitives. Applications inheriting the actual naeil.dev brand/catalog/control composition use the canonical opt-in [NaeilHeader](naeil-header.md) from `@naeil/ui/naeil-header`, with its fixed 65px/1280px frame and official artwork URL dependency.
4
+
5
+ `Navigation` and `SiteFooter` are React-only exports from `@naeil/ui/ui` (also `@naeil/ui/components/ui/navigation` and `@naeil/ui/components/ui/site-footer`). They require shared theme/components CSS and the normal consumer font/Tailwind setup. They do not import Next, next-intl, authentication, product routes or message dictionaries.
6
+
7
+ ```tsx
8
+ import { Navigation, SiteFooter } from "@naeil/ui/ui";
9
+
10
+ <Navigation
11
+ brand={<a href="/">My product</a>}
12
+ labels={{ navigation: "Main navigation", menu: "Menu", close: "Close menu" }}
13
+ items={[{ id: "ai", label: "AI", items: [
14
+ { id: "dashboard", label: "Dashboard", href: "/", description: "Usage and balance" },
15
+ { id: "keys", label: "API keys", href: "/keys" },
16
+ ] }]}
17
+ currentHref={pathname}
18
+ containerClassName="product-frame"
19
+ toolbar={<ProductControls />}
20
+ onNavigate={(event, item) => {
21
+ if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
22
+ event.preventDefault();
23
+ guardedNavigate(item.href);
24
+ }}
25
+ />
26
+ <main className="product-frame">
27
+ <section data-ui-layout="settings">{/* narrow form inside stable frame */}</section>
28
+ </main>
29
+ <SiteFooter
30
+ brand={<ProductBrand />}
31
+ variant="compact"
32
+ meta="Product information"
33
+ containerClassName="product-frame"
34
+ />
35
+ ```
36
+
37
+ These are composition snippets: `pathname`, controls, brand, frame and `guardedNavigate` belong to the application. Keep its unsaved-change guard and modified-click behavior. Alternatively supply a `Link` adapter which forwards anchor props, `onClick`, `aria-current`, `className`, target and children. Links explicitly marked `external: true` render native anchors; an absolute URL alone does not bypass the supplied `Link`. `active` overrides exact `currentHref` matching when a route family needs custom matching.
38
+
39
+ `Navigation` accepts links and one level of labeled groups, optional description/icon/badge, `toolbar`, `mobileToolbar` and `mobileFooter`. Brand is a slot; the component does not invent a home destination or label. Groups use Radix Popover with actual links (Tab navigation, Escape and focus return); they are not action menus. Mobile uses a modal Radix Dialog with scroll locking, trapped focus and a labeled close control. The sheet closes on destination selection, currentHref changes or a return to desktop.
40
+
41
+ Desktop groups open on mouse hover without moving keyboard focus. Leaving both the trigger and panel closes the hover preview after the standard 180ms token duration, allowing travel across the portal gap. Entering the panel cancels the pending close. Mouse clicks keep the menu open while the pointer remains in the trigger/panel and do not pin it: leaving still dismisses it. Keyboard-activating a hovered trigger takes over the open menu and focuses its first link; subsequent keyboard activation, Escape, outside interaction or navigation dismisses it. A menu being used with keyboard focus remains open when the pointer leaves. Mouse dismissal preserves the previous focus; keyboard activation retains Radix focus return. Touch uses click/tap, and the mobile sheet behavior is unchanged. Reduced motion disables visual transitions while retaining pointer travel tolerance.
42
+
43
+ Container queries select desktop layout at 48rem of available header content width. Product frame classes control max width and gutters. Header height uses `ui.navigation-height` (64px); toolbar controls should remain short, with volatile counts in the content area. Navigation itself is in document flow; `className` can make it sticky or fixed. Fixed consumers must reserve its height in page composition. A narrow form must not constrain the header. Explicit className/style remain consumer overrides.
44
+
45
+ `SiteFooter` supports `brand`, `description`, `groups: [{ id, label, links }]`, `meta`, children, `Link`, `containerClassName` and `variant="full|compact"`. Each link group is a labeled navigation landmark. Translated titles, destinations, legal/version text and social links belong to the consumer. A product may place a compact footer in its mobile sheet; a full marketing footer is not mandatory in a dashboard.
46
+
47
+ Desktop reserves `ui.navigation-brand-width` (160px) for the brand, followed by the existing 16px row gap. With matching frame, viewport and font conditions, `.naeil` and `.naeil Home` therefore start their first menu at the same position. Mobile keeps intrinsic brand width. For a larger custom brand, override `--ui-navigation-brand-width` on the header or shared product frame; use the same value across related compositions and ensure the brand fits the reserved area.
48
+
49
+ ## Compatibility
50
+
51
+ Menu groups accept `columns?: 1 | 2` (default 1). The candidate sample uses 2 for the website project catalog and 1 for Home's functional menu; the mobile sheet remains one column. Menu widths use `ui.navigation-menu-width` (320px) and `ui.navigation-menu-wide-width` (640px), capped by available space. Group links share a 40px icon area, 16px icon-to-copy gap, title/description hierarchy and badges beside the title. Supply decorated brand artwork through `icon`; the shared package does not own product illustrations. Current group links use background, semibold title and a check indicator, retaining `aria-current` and keyboard focus without underlining descriptions.
52
+
53
+ Existing `Nav` and `Footer` imports and props remain. `Nav` adapts the canonical NaeilHeader, which renders this generic core; `Footer` composes SiteFooter. `Nav` retains its fixed positioning and max-w-7xl site frame; menus gain keyboard/focus support, an opaque shared surface, 16px labels and a 64px minimum row. Its `projects` prop replaces the old default inventory; the old naeil projects remain the fallback for existing consumers. Optional `labels`, `className` and `containerClassName` support localization and frame overrides.
54
+
55
+ `Footer` adds `brandSlot`, `description`, `groups`, `socialSlot`, `meta`, `variant`, `className` and `containerClassName`. Use empty groups or null description/socialSlot to omit defaults. Existing linksExternal behavior remains for the default naeil links. Custom groups define their own absolute destinations and external/target settings.
56
+
57
+ `Nav`/`Footer` continue to need Next/next-intl as documented. React-only services use this core for their own product menus or `/naeil-header` for the canonical branded composition. ThemeProvider, ThemeToggleIcon, LocaleSwitcher and Logo remain available through their existing React-safe deep imports. Reusing controls does not synchronize theme/language/authentication between origins.
58
+
59
+ Storybook: `UI/Navigation` (website, Home composition, long labels), `UI/SiteFooter` (full and compact). Naeil Home is a synthetic composition based on the inspected live menu, not proof of a deployed migration. The repository task note `docs/design/navigation-footer-integration.md` tracks rollout; it is not included in the npm package.
@@ -1,10 +1,25 @@
1
1
  # Public UI support and manual acceptance
2
2
 
3
- This support matrix targets `@naeil/ui@0.3.0`; it does not assert registry or hosted Docs availability. Current documentation covers 13 families. This matrix describes the current examples/checks, not every consumer or every allowed dependency version. [Dated Stage 3 results](public-ui-stage3-verification.md) identify commands, source hashes and local limitations. [Stage 4 extension results](public-ui-stage4-verification.md) separately cover Tabs/Textarea and native field/choice composition.
3
+ This support matrix targets published `@naeil/ui@0.4.0` (2026-10-11); the [release](https://github.com/naeil-dev/naeil-ui/releases/tag/v0.4.0) and [publication report](https://github.com/naeil-dev/naeil-ui/blob/main/docs/design/naeil-brand-0.4.0-publication.md) identify its source, npm archive, independent brand 1.1.1 and deployed Docs/site. Current documentation covers 13 component families plus Brand / Logo. Coverage describes the checked examples and dependency versions; native/manual acceptance remains open.
4
+
5
+ ## 0.4.0 publication coverage
6
+
7
+ - Source `e8fe5afc13788a0cac0244b72e78975812f4a2ee`: 46 local unit tests passed. [Merged Linux CI](https://github.com/naeil-dev/naeil-ui/actions/runs/38113742370) passed package/site checks and 233 browser tests with 4 expected forced-colors skips; the [Docs deployment workflow](https://github.com/naeil-dev/naeil-ui/actions/runs/38114879574) also passed that browser suite.
8
+ - [Registry/consumer receipt](https://github.com/naeil-dev/naeil-ui/releases/download/v0.4.0/npm-publication-verification.json): fresh actual npm installs with separate empty caches passed React/Next types and production builds, SSR/deep imports, exact notices, production fonts/fallback, mixed-import ownership/StrictMode and Chromium/WebKit Tabs/Textarea contracts. The React fixture contains no Next dependency. Fixtures do not establish real-product adoption.
9
+ - [Hosted Docs receipt](https://github.com/naeil-dev/naeil-ui/releases/download/v0.4.0/hosted-docs-verification.json): Chromium 153.0.8010.12 and Playwright WebKit 26.6 passed 28 Docs visits, 28 actual Docs-to-Usage transitions, 4 brand light/dark checks, canonical artwork, exact notices, local fonts and pinned links at the deployed `/naeil-ui/` URL.
10
+ - [Production receipt](https://github.com/naeil-dev/naeil-ui/releases/download/v0.4.0/production-verification.json): the same engines passed 24 homepage/login conditions across three locales, two viewport sizes and two themes; header/footer/login logos, mobile menu and 3 deployed icons were checked. These observations cover page rendering and navigation; full OAuth authentication was not exercised.
11
+
12
+ The environment matrix below retains dated foundation coverage. The 0.4.0 checks above add evidence for their exact source and do not close the native/manual items below.
13
+
14
+ ## Foundation evidence — 0.3.0
15
+
16
+ Version 0.3.0 was published on 2026-10-03; its [registry/consumer receipt](https://github.com/naeil-dev/naeil-ui/releases/download/v0.3.0/npm-publication-verification.json) and [release](https://github.com/naeil-dev/naeil-ui/releases/tag/v0.3.0) remain historical evidence. [Dated Stage 3 results](public-ui-stage3-verification.md) identify commands, source hashes and local limitations. [Stage 4 extension results](public-ui-stage4-verification.md) separately cover Tabs/Textarea and native field/choice composition.
4
17
 
5
18
  Linux CI at `bcf99fca3467ae2b1cd29d54fa24f2569fb6001c` passed package-and-site in both the [push run](https://github.com/naeil-dev/naeil-ui/actions/runs/36945564443) and [PR run](https://github.com/naeil-dev/naeil-ui/actions/runs/36945943651). Their browser jobs failed: respectively 207 passed/4 skipped/2 failed (axe scan collision and Avatar Docs ambiguity), and 208 passed/4 skipped/1 failed (Avatar Docs ambiguity). Firefox application tests passed in both Linux runs. These failed baseline results remain historical evidence in the [final correction follow-up](public-ui-final-verification.md).
6
19
 
7
- The corrected source `9f90493abb4fb1186c948084026c3f4c04074013` passed independent scoped review with no actionable findings and both actual Linux workflows: [push](https://github.com/naeil-dev/naeil-ui/actions/runs/36948276767) and [PR](https://github.com/naeil-dev/naeil-ui/actions/runs/36948280881). The PR browser suite passed **209 tests with 4 expected forced-colors skips** (two each in Firefox/WebKit); package-and-site and optional helper checks also passed. Root verified the downloaded PR Storybook artifact and its `/naeil-ui/` navigation/fonts in Chromium/WebKit. This evidence applies to that exact source; [live PR checks](https://github.com/naeil-dev/naeil-ui/pull/1/checks) report later documentation-head status. Review/CI acceptance applies to its recorded source and does not complete native/manual platform coverage. Publication authorization is a separate decision; unrun checks remain limitations rather than completed results.
20
+ The corrected source `9f90493abb4fb1186c948084026c3f4c04074013` passed independent scoped review with no actionable findings and both actual Linux workflows: [push](https://github.com/naeil-dev/naeil-ui/actions/runs/36948276767) and [PR](https://github.com/naeil-dev/naeil-ui/actions/runs/36948280881). The PR browser suite passed **209 tests with 4 expected forced-colors skips** (two each in Firefox/WebKit); package-and-site and optional helper checks also passed. Root verified the downloaded PR Storybook artifact and its `/naeil-ui/` navigation/fonts in Chromium/WebKit. This evidence applies to that exact source; [merged PR checks](https://github.com/naeil-dev/naeil-ui/pull/1/checks) retain the release-preparation checks. Subsequent documentation changes require their own verification. Review/CI acceptance applies to its recorded source and does not complete native/manual platform coverage. Publication completed separately on 2026-10-03; unrun checks remain limitations rather than completed results.
21
+
22
+ Fresh npm-registry installs in clean-cache React/Next apps passed types/build, production fonts and blocked-font fallback, mixed-import ownership/StrictMode, Chromium/WebKit Tabs/Textarea contracts and Next 16.1.7 root/deep prerender; the [publication receipt](https://github.com/naeil-dev/naeil-ui/releases/download/v0.3.0/npm-publication-verification.json) gives their scope. The development overlay replay probe retains 34 Vite font allowlist warnings; it certifies overlay replay only, and font success applies to production. These fixtures do not establish adoption in a real consumer product.
8
23
 
9
24
  | Environment / capability | Contract and evidence |
10
25
  | --- | --- |