@naeil/ui 0.11.0 → 0.13.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 +38 -2
- package/DESIGN.md +13 -3
- package/README.md +13 -11
- package/THIRD_PARTY_NOTICES.md +4 -2
- package/dist/{chunk-KWEWR46R.js → chunk-5BXZPDHZ.js} +68 -8
- package/dist/{chunk-U3IZCFM4.js → chunk-A2DCQVTP.js} +1 -1
- package/dist/{chunk-KPE4KKYM.js → chunk-Y5LW3ADW.js} +24 -15
- package/dist/components/index.d.ts +3 -3
- package/dist/components/index.js +4 -4
- package/dist/components/naeil-footer.d.ts +2 -2
- package/dist/components/naeil-footer.js +1 -1
- package/dist/components/naeil-header.d.ts +23 -8
- package/dist/components/naeil-header.js +7 -5
- package/dist/components/ui/index.d.ts +22 -5
- package/dist/components/ui/index.js +181 -34
- package/dist/lib/ui-release.d.ts +1 -1
- package/dist/lib/ui-release.js +1 -1
- package/dist/lib/utils.js +1 -7
- package/dist/naeil-locale/checker.js +1 -1
- package/dist/naeil-locale/chrome.d.ts +25 -8
- package/dist/naeil-locale/chrome.js +91 -22
- package/dist/naeil-locale/{chunk-4U64K4J3.js → chunk-24IL53QO.js} +1 -1
- package/dist/naeil-locale/{chunk-U65OQHZY.js → chunk-WT6QXR7O.js} +14 -8
- package/dist/naeil-locale/client.js +45 -15
- package/dist/naeil-locale/index.js +1 -1
- package/dist/naeil-locale/messages.js +2 -2
- package/dist/naeil-locale/server.js +6 -6
- package/dist/{navigation-D8N1tMTu.d.ts → navigation-CXZapKac.d.ts} +3 -1
- package/dist/{site-footer-s113FZgC.d.ts → site-footer-C4jgkGDU.d.ts} +1 -1
- package/dist/source/components/naeil-header-core.d.ts +23 -8
- package/dist/source/components/naeil-header-core.js +66 -6
- package/dist/source/components/naeil-header.d.ts +6 -4
- package/dist/source/components/naeil-header.js +4 -2
- package/dist/source/components/typography.d.ts +1 -1
- package/dist/source/components/typography.js +1 -1
- package/dist/source/components/ui/button.js +7 -7
- package/dist/source/components/ui/card.js +1 -1
- package/dist/source/components/ui/dialog.d.ts +4 -2
- package/dist/source/components/ui/dialog.js +10 -6
- package/dist/source/components/ui/index.d.ts +1 -0
- package/dist/source/components/ui/index.js +22 -0
- package/dist/source/components/ui/navigation.d.ts +3 -1
- package/dist/source/components/ui/navigation.js +17 -8
- package/dist/source/components/ui/sheet.d.ts +20 -0
- package/dist/source/components/ui/sheet.js +176 -0
- package/dist/source/components/ui/sonner.js +2 -2
- package/dist/source/lib/ui-release.d.ts +1 -1
- package/dist/source/lib/ui-release.js +1 -1
- package/docs/components/README.md +4 -1
- package/docs/components/application-layout.md +27 -28
- package/docs/components/avatar.md +1 -1
- package/docs/components/badge.md +5 -1
- package/docs/components/button.md +9 -1
- package/docs/components/card.md +5 -1
- package/docs/components/checkbox.md +1 -1
- package/docs/components/common.md +22 -1
- package/docs/components/dialog.md +14 -4
- package/docs/components/dropdown-menu.md +1 -1
- package/docs/components/input.md +1 -1
- package/docs/components/naeil-footer.md +4 -2
- package/docs/components/naeil-header.md +41 -12
- package/docs/components/naeil-locale.md +36 -0
- package/docs/components/navigation.md +10 -4
- package/docs/components/select.md +1 -1
- package/docs/components/sheet.md +50 -0
- package/docs/components/sonner.md +7 -1
- package/docs/components/switch.md +1 -1
- package/docs/components/tabs.md +1 -1
- package/docs/components/textarea.md +1 -1
- package/docs/design/public-ui-support.md +3 -1
- package/docs/design/v2-migration.md +13 -7
- package/docs/package-release.md +24 -8
- package/package.json +11 -10
- package/src/styles/components.css +40 -2
- package/src/styles/globals.css +6 -2
- package/src/styles/theme.css +6 -0
- package/licenses/Nunito-Sans-OFL.txt +0 -93
- package/licenses/React-MIT.txt +0 -21
- package/licenses/Storybook-MIT.txt +0 -21
- package/licenses/popper-MIT.txt +0 -25
- package/licenses/store2-MIT.txt +0 -22
- package/licenses/toggle-selection-MIT.txt +0 -21
|
@@ -0,0 +1,176 @@
|
|
|
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 { XIcon } from "lucide-react";
|
|
35
|
+
import { Dialog as DialogPrimitive } from "radix-ui";
|
|
36
|
+
import { cn } from "../../lib/utils.js";
|
|
37
|
+
function Sheet(_a) {
|
|
38
|
+
var props = __objRest(_a, []);
|
|
39
|
+
return /* @__PURE__ */ jsx(DialogPrimitive.Root, __spreadValues({ "data-slot": "sheet" }, props));
|
|
40
|
+
}
|
|
41
|
+
function SheetTrigger(_b) {
|
|
42
|
+
var props = __objRest(_b, []);
|
|
43
|
+
return /* @__PURE__ */ jsx(DialogPrimitive.Trigger, __spreadValues({ "data-slot": "sheet-trigger" }, props));
|
|
44
|
+
}
|
|
45
|
+
function SheetPortal(_c) {
|
|
46
|
+
var props = __objRest(_c, []);
|
|
47
|
+
return /* @__PURE__ */ jsx(DialogPrimitive.Portal, __spreadValues({ "data-slot": "sheet-portal" }, props));
|
|
48
|
+
}
|
|
49
|
+
function SheetClose(_d) {
|
|
50
|
+
var props = __objRest(_d, []);
|
|
51
|
+
return /* @__PURE__ */ jsx(DialogPrimitive.Close, __spreadValues({ "data-slot": "sheet-close" }, props));
|
|
52
|
+
}
|
|
53
|
+
function SheetOverlay(_e) {
|
|
54
|
+
var _f = _e, {
|
|
55
|
+
className
|
|
56
|
+
} = _f, props = __objRest(_f, [
|
|
57
|
+
"className"
|
|
58
|
+
]);
|
|
59
|
+
return /* @__PURE__ */ jsx(
|
|
60
|
+
DialogPrimitive.Overlay,
|
|
61
|
+
__spreadValues({
|
|
62
|
+
"data-slot": "sheet-overlay",
|
|
63
|
+
className: cn("ui-popup fixed inset-0 z-50 bg-black/60 ", className)
|
|
64
|
+
}, props)
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
function SheetContent(_g) {
|
|
68
|
+
var _h = _g, {
|
|
69
|
+
className,
|
|
70
|
+
children,
|
|
71
|
+
side = "right",
|
|
72
|
+
showCloseButton = true,
|
|
73
|
+
closeLabel = "Close"
|
|
74
|
+
} = _h, props = __objRest(_h, [
|
|
75
|
+
"className",
|
|
76
|
+
"children",
|
|
77
|
+
"side",
|
|
78
|
+
"showCloseButton",
|
|
79
|
+
"closeLabel"
|
|
80
|
+
]);
|
|
81
|
+
return /* @__PURE__ */ jsxs(SheetPortal, { "data-slot": "sheet-portal", children: [
|
|
82
|
+
/* @__PURE__ */ jsx(SheetOverlay, {}),
|
|
83
|
+
/* @__PURE__ */ jsxs(
|
|
84
|
+
DialogPrimitive.Content,
|
|
85
|
+
__spreadProps(__spreadValues({
|
|
86
|
+
"data-slot": "sheet-content",
|
|
87
|
+
"data-side": side,
|
|
88
|
+
className: cn(
|
|
89
|
+
"bg-popover ui-popup fixed z-50 flex flex-col gap-4 overflow-y-auto border-border-strong p-[var(--ui-panel-padding)]",
|
|
90
|
+
{
|
|
91
|
+
right: "inset-y-0 right-0 w-[min(100%,var(--ui-width-short-form))] rounded-l-xl border-l",
|
|
92
|
+
left: "inset-y-0 left-0 w-[min(100%,var(--ui-width-short-form))] rounded-r-xl border-r",
|
|
93
|
+
top: "inset-x-0 top-0 max-h-[100dvh] w-full rounded-b-xl border-b",
|
|
94
|
+
bottom: "inset-x-0 bottom-0 max-h-[100dvh] w-full rounded-t-xl border-t"
|
|
95
|
+
}[side],
|
|
96
|
+
className
|
|
97
|
+
)
|
|
98
|
+
}, props), {
|
|
99
|
+
children: [
|
|
100
|
+
children,
|
|
101
|
+
showCloseButton && /* @__PURE__ */ jsxs(
|
|
102
|
+
DialogPrimitive.Close,
|
|
103
|
+
{
|
|
104
|
+
"data-slot": "sheet-close",
|
|
105
|
+
className: "ui-focus absolute top-2 right-2 flex size-10 items-center justify-center rounded-sm text-muted-foreground hover:text-foreground transition-colors disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
106
|
+
children: [
|
|
107
|
+
/* @__PURE__ */ jsx(XIcon, {}),
|
|
108
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: closeLabel })
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
)
|
|
112
|
+
]
|
|
113
|
+
})
|
|
114
|
+
)
|
|
115
|
+
] });
|
|
116
|
+
}
|
|
117
|
+
function SheetHeader(_i) {
|
|
118
|
+
var _j = _i, { className } = _j, props = __objRest(_j, ["className"]);
|
|
119
|
+
return /* @__PURE__ */ jsx(
|
|
120
|
+
"div",
|
|
121
|
+
__spreadValues({
|
|
122
|
+
"data-slot": "sheet-header",
|
|
123
|
+
className: cn("flex flex-col gap-2 pr-8 text-left", className)
|
|
124
|
+
}, props)
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
function SheetFooter(_k) {
|
|
128
|
+
var _l = _k, { className } = _l, props = __objRest(_l, ["className"]);
|
|
129
|
+
return /* @__PURE__ */ jsx(
|
|
130
|
+
"div",
|
|
131
|
+
__spreadValues({
|
|
132
|
+
"data-slot": "sheet-footer",
|
|
133
|
+
className: cn("mt-auto flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className)
|
|
134
|
+
}, props)
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
function SheetTitle(_m) {
|
|
138
|
+
var _n = _m, {
|
|
139
|
+
className
|
|
140
|
+
} = _n, props = __objRest(_n, [
|
|
141
|
+
"className"
|
|
142
|
+
]);
|
|
143
|
+
return /* @__PURE__ */ jsx(
|
|
144
|
+
DialogPrimitive.Title,
|
|
145
|
+
__spreadValues({
|
|
146
|
+
"data-slot": "sheet-title",
|
|
147
|
+
className: cn("text-xl leading-normal font-semibold", className)
|
|
148
|
+
}, props)
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
function SheetDescription(_o) {
|
|
152
|
+
var _p = _o, {
|
|
153
|
+
className
|
|
154
|
+
} = _p, props = __objRest(_p, [
|
|
155
|
+
"className"
|
|
156
|
+
]);
|
|
157
|
+
return /* @__PURE__ */ jsx(
|
|
158
|
+
DialogPrimitive.Description,
|
|
159
|
+
__spreadValues({
|
|
160
|
+
"data-slot": "sheet-description",
|
|
161
|
+
className: cn("text-muted-foreground text-sm", className)
|
|
162
|
+
}, props)
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
export {
|
|
166
|
+
Sheet,
|
|
167
|
+
SheetClose,
|
|
168
|
+
SheetContent,
|
|
169
|
+
SheetDescription,
|
|
170
|
+
SheetFooter,
|
|
171
|
+
SheetHeader,
|
|
172
|
+
SheetOverlay,
|
|
173
|
+
SheetPortal,
|
|
174
|
+
SheetTitle,
|
|
175
|
+
SheetTrigger
|
|
176
|
+
};
|
|
@@ -39,11 +39,11 @@ import { useTheme } from "next-themes";
|
|
|
39
39
|
import { Toaster as Sonner } from "sonner";
|
|
40
40
|
const Toaster = (_a) => {
|
|
41
41
|
var props = __objRest(_a, []);
|
|
42
|
-
const { theme = "system" } = useTheme();
|
|
42
|
+
const { theme = "system", forcedTheme } = useTheme();
|
|
43
43
|
return /* @__PURE__ */ jsx(
|
|
44
44
|
Sonner,
|
|
45
45
|
__spreadValues({
|
|
46
|
-
theme,
|
|
46
|
+
theme: forcedTheme != null ? forcedTheme : theme,
|
|
47
47
|
className: "toaster group font-sans",
|
|
48
48
|
position: "bottom-right",
|
|
49
49
|
duration: 4e3,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Component usage guides
|
|
2
2
|
|
|
3
|
-
These maintained guides
|
|
3
|
+
These maintained guides describe the repository APIs. See [release policy and identities](../package-release.md#publication-and-deployment) for the current published release. [Setup, themes, fonts and shared responsibilities](common.md). Run `pnpm storybook` for the current 18 Docs pages (16 UI and 2 Brand) and choose Docs or Usage. The [hosted Storybook](https://naeil-dev.github.io/naeil-ui/) has recorded [0.5.0 verification](https://github.com/naeil-dev/naeil-ui/releases/tag/v0.5.0); each subsequent deployment requires its own receipt. Select chooses a value; DropdownMenu chooses an action.
|
|
4
4
|
|
|
5
5
|
Start application layouts with the [shared navigation/footer selection and frame guide](application-layout.md). It includes a starter, the 1600px frame contract and instructions to place in consumer repositories so agents reuse the shared implementation.
|
|
6
6
|
|
|
@@ -10,6 +10,7 @@ Start application layouts with the [shared navigation/footer selection and frame
|
|
|
10
10
|
| Input | [Input](./input.md) | UI / Input / Usage |
|
|
11
11
|
| Card | [Card](./card.md) | UI / Card / Usage |
|
|
12
12
|
| Dialog | [Dialog](./dialog.md) | UI / Dialog / Usage |
|
|
13
|
+
| Sheet | [Sheet](./sheet.md) | UI / Sheet / Usage, LocalizedClose, Sides |
|
|
13
14
|
| DropdownMenu | [DropdownMenu](./dropdown-menu.md) | UI / DropdownMenu / Usage |
|
|
14
15
|
| Badge | [Badge](./badge.md) | UI / Badge / Usage |
|
|
15
16
|
| Avatar | [Avatar](./avatar.md) | UI / Avatar / Usage |
|
|
@@ -24,6 +25,8 @@ Start application layouts with the [shared navigation/footer selection and frame
|
|
|
24
25
|
| NaeilFooter | [NaeilFooter](./naeil-footer.md) | Brand / NaeilHeader / ReferenceFull, ReferenceCompact |
|
|
25
26
|
| NaeilHeader and account control | [NaeilHeader](./naeil-header.md) | Brand / NaeilHeader / Usage, JapaneseApplication, AccountStates |
|
|
26
27
|
|
|
28
|
+
[Owned-service language runtime](naeil-locale.md) documents `/naeil-locale`, `/client`, `/server`, `/messages`, `/checker` and `/chrome`. Owned Next services under the shared provider import header/account/footer from `/naeil-locale/chrome`; the standalone React adapters remain available.
|
|
29
|
+
|
|
27
30
|
[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.
|
|
28
31
|
|
|
29
32
|
Use opt-in `@naeil/ui/naeil-header` for the actual naeil.dev branded header and its `NaeilHeaderAccountControl` in `authSlot`, paired with `@naeil/ui/naeil-footer` on owned services. Consumers supply routing/save guards, theme providers and authentication state/actions; official artwork remains URL-referenced. Canonical footer and account examples are part of existing stories, without additional Docs families. Use generic `Navigation` and `SiteFooter` from `/ui` for different products. [Navigation / SiteFooter](navigation.md) also documents the compatibility adapters.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Application frame and shared navigation/footer
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Product navigation and controlled mobile APIs are available since 0.8.0; shared root reservation is available since 0.9.0. See [release identities and publication status](../package-release.md#publication-and-deployment) for the current published release; hosted Docs and service activation have separate receipts. See [consumer setup](../design/v2-migration.md) for CSS, Tailwind sources and fonts.
|
|
4
4
|
|
|
5
5
|
## Choose the shared composition first
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ Exact **@naeil/ui 0.8.0 is published** (`next` tag; `latest` remains 0.7.2 at th
|
|
|
8
8
|
| --- | --- |
|
|
9
9
|
| The actual naeil.dev brand, project catalog and controls | `NaeilHeader` from `@naeil/ui/naeil-header` |
|
|
10
10
|
| Owned-service login/account/logout presentation | `NaeilHeaderAccountControl` from `@naeil/ui/naeil-header` in `authSlot` |
|
|
11
|
-
| Owned naeil service with product menu data (0.8.0) | Complete `NaeilHeader
|
|
11
|
+
| Owned naeil service with product menu data (0.8.0) | Complete `NaeilHeader` with `service` and its own `appNavigation` |
|
|
12
12
|
| A different product menu without canonical naeil chrome | `Navigation` from `@naeil/ui/ui` |
|
|
13
13
|
| Owned naeil service footer | `NaeilFooter` from `@naeil/ui/naeil-footer` |
|
|
14
14
|
| Different product footer or explicit content override | `SiteFooter` from `@naeil/ui/ui` |
|
|
@@ -24,12 +24,13 @@ Choose one header per application frame. Supply menu data, links, route guards,
|
|
|
24
24
|
| Inline gutters | `clamp(16px, 2.4vw, 40px)` | `--ui-frame-gutter` |
|
|
25
25
|
| Reading / settings | Maximum 640px | `data-ui-layout="reading"` / `"settings"` |
|
|
26
26
|
| Short form | Maximum 480px | `data-ui-layout="short-form"` |
|
|
27
|
+
| Multi-step / card-choice input (workflow) | Maximum 896px; fields inside stay within 640/480px | `data-ui-layout="workflow"` |
|
|
27
28
|
| List / dashboard / table region | Available space inside the frame, capped at 1600px | `data-ui-layout="list"` |
|
|
28
29
|
| Intentionally unrestricted region | Available parent width | `data-ui-layout="fluid"` |
|
|
29
30
|
|
|
30
31
|
Use `.ui-app-frame` on main. Shared header/footer already apply the frame internally; **do not wrap them in another padded frame**. Put purpose-specific sections inside main. At 1920px the default frame has 1520px available after its 40px gutters. Small screens shrink the frame and reduce gutters to at least 16px. A narrow form must not constrain or move navigation.
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
Since 0.9.0, the package supplies `html { scrollbar-gutter: stable }` once in `components.css`, reached by `globals.css` or `theme.css + components.css`. Classic mode reserves one browser-sized gutter on short and long pages; overlay mode reserves none. The shared lock correction removes duplicate Radix body right-margin/gap compensation and retains its scroll prevention, focus and nested cleanup. Keep header/main/footer and their ordinary content horizontally stable. Compare matching root policies and measure before any fullPage capture. See the [adopted acceptance contract](https://github.com/naeil-dev/naeil-ui/blob/434b7d07235316097ca3a55265ee02c1422b0cfd/docs/superpowers/specs/2026-10-29-page-root-scrollbar-gutter.md).
|
|
33
34
|
|
|
34
35
|
For a different document policy, declare this on the root **before first paint**:
|
|
35
36
|
|
|
@@ -56,53 +57,60 @@ Frame and control tokens stay unchanged. At 1920px with a 15px classic gutter th
|
|
|
56
57
|
|
|
57
58
|
## Canonical naeil application starter
|
|
58
59
|
|
|
60
|
+
Owned Next services under `NaeilLocaleProvider` import header/account/footer chrome from `@naeil/ui/naeil-locale/chrome`; `/naeil-header` and `/naeil-footer` remain standalone React adapters. See [the owned-service language runtime](naeil-locale.md) for `/naeil-locale`, `/client`, `/server`, `/messages`, `/checker` and `/chrome` setup.
|
|
61
|
+
|
|
62
|
+
For the standalone React adapter:
|
|
63
|
+
|
|
64
|
+
Owned services use NaeilFooter's shared `full` default as-is; omit `variant`. `compact` is a deliberate, recorded exception (for example, a focused task screen), not the starter default.
|
|
65
|
+
|
|
59
66
|
```tsx
|
|
60
67
|
import type { ReactNode } from "react";
|
|
61
68
|
import { NaeilHeader, NaeilHeaderAccountControl,
|
|
62
|
-
type NaeilHeaderAccount, type NaeilHeaderProps } from "@naeil/ui/naeil-header";
|
|
69
|
+
type NaeilHeaderAccount, type NaeilHeaderProps, type NaeilService } from "@naeil/ui/naeil-header";
|
|
63
70
|
import { NaeilFooter } from "@naeil/ui/naeil-footer";
|
|
64
71
|
|
|
65
72
|
// Render inside the existing next-themes ThemeProvider.
|
|
66
73
|
// The app owns session state, its guarded logout and pending request lock.
|
|
67
74
|
type ApplicationLayoutProps = {
|
|
68
75
|
children: ReactNode;
|
|
76
|
+
service: NaeilService;
|
|
69
77
|
locale: NaeilHeaderProps["locale"];
|
|
70
78
|
Link: NaeilHeaderProps["Link"];
|
|
71
79
|
currentHref: string;
|
|
72
80
|
account: NaeilHeaderAccount;
|
|
73
81
|
};
|
|
74
82
|
|
|
75
|
-
export function ApplicationLayout({ children, locale, Link, currentHref,
|
|
83
|
+
export function ApplicationLayout({ children, service, locale, Link, currentHref,
|
|
76
84
|
account }: ApplicationLayoutProps) {
|
|
77
85
|
return <>
|
|
78
|
-
<NaeilHeader locale={locale} Link={Link} currentHref={currentHref}
|
|
86
|
+
<NaeilHeader service={service} locale={locale} Link={Link} currentHref={currentHref}
|
|
79
87
|
authSlot={<NaeilHeaderAccountControl locale={locale} Link={Link}
|
|
80
88
|
account={account} />} />
|
|
81
89
|
<main className="ui-app-frame"
|
|
82
90
|
style={{ marginTop: "calc(var(--ui-navigation-height) + 1px)" }}>
|
|
83
91
|
{children}
|
|
84
92
|
</main>
|
|
85
|
-
<NaeilFooter locale={locale}
|
|
93
|
+
<NaeilFooter locale={locale} Link={Link} />
|
|
86
94
|
</>;
|
|
87
95
|
}
|
|
88
96
|
|
|
89
|
-
<ApplicationLayout locale="en" Link={GuardedLink} currentHref={pathname}
|
|
97
|
+
<ApplicationLayout service={service} locale="en" Link={GuardedLink} currentHref={pathname}
|
|
90
98
|
account={account}>
|
|
91
99
|
<section data-ui-layout="settings">{/* Settings form */}</section>
|
|
92
100
|
</ApplicationLayout>
|
|
93
101
|
```
|
|
94
102
|
|
|
95
|
-
`GuardedLink`, `pathname` and `account` come from the consumer. Use `{ status: 'signed-out', loginHref }` or `{ status: 'signed-in', onLogout, busy, displayName, email }` for `account`; the app handles async cancellation, failure and retry. Keep pending state and the request lock above `NaeilHeader`, since desktop/mobile slot contents mount separately. See the [account contract](naeil-header.md#account-presentation).
|
|
103
|
+
`service`, `GuardedLink`, `pathname` and `account` come from the consumer. Use `{ status: 'signed-out', loginHref }` or `{ status: 'signed-in', onLogout, busy, displayName, email }` for `account`; the app handles async cancellation, failure and retry. Keep pending state and the request lock above `NaeilHeader`, since desktop/mobile slot contents mount separately. See the [account contract](naeil-header.md#account-presentation).
|
|
96
104
|
|
|
97
|
-
`NaeilHeader` is fixed; reserve its 65px height and account for it in sticky areas and focus/anchor scroll margins.
|
|
105
|
+
`NaeilHeader` is fixed; reserve its 65px height and account for it in sticky areas and focus/anchor scroll margins. The service brand defaults to `/`; footer destinations remain official naeil.dev URLs. Preserve routing/unsaved-change guards using the [header contract](naeil-header.md) and [footer interception contract](naeil-footer.md); a Link adapter must handle absolute official and GitHub destinations as well as local routes. Only pass `onLocaleChange` when the application supports its complete language set.
|
|
98
106
|
|
|
99
|
-
For owned naeil product navigation,
|
|
107
|
+
For owned naeil product navigation, set the matching `service` id and pass only product menu data through `appNavigation`; see the [Home profile and host conditions](naeil-header.md#host-typography-and-mobile-handoff-080). `mobileOpen`/`onMobileOpenChange` can coordinate the sheet with a consumer confirmation, while busy state and request locking remain above the header. For a different product menu without canonical chrome, use the [generic Navigation composition](navigation.md) with the same main/footer structure. Generic Navigation is in document flow by default and needs no fixed-header clearance. The [standalone React example](https://github.com/naeil-dev/naeil-ui/tree/main/examples/react) exercises the installed header/account control, narrow form and NaeilFooter together.
|
|
100
108
|
|
|
101
|
-
|
|
109
|
+
Without `service`, an empty desktop menu preserves B1's limitation: resizing alone leaves the sheet open; Escape/close/overlay/route change dismiss it. Service profiles dismiss on desktop crossing even with an empty center; the mobile sheet omits empty navigation. Controlled-mobile account callbacks wait for both closing layers and their focus handoff; desktop and uncontrolled-mobile account callbacks retain B1's selection-time behavior. A consumer confirmation owns cancellation focus, including a visible desktop account fallback after resize.
|
|
102
110
|
|
|
103
111
|
## Deliberate overrides
|
|
104
112
|
|
|
105
|
-
Apply overrides on a common ancestor or document root so header, main and footer inherit them together.
|
|
113
|
+
Apply overrides on a common ancestor or document root so header, main and footer inherit them together. Legacy (no `service`) `containerClassName` overrides still work; the owned-service profile rejects header class/style overrides. Do not give each route its own header cap or gutter.
|
|
106
114
|
|
|
107
115
|
```css
|
|
108
116
|
/* Product exception: record why this cap is needed. */
|
|
@@ -125,8 +133,12 @@ Put this in the **consumer application's** `AGENTS.md` (or equivalent repository
|
|
|
125
133
|
- Read the installed @naeil/ui/DESIGN.md and docs/components/application-layout.md before UI layout work.
|
|
126
134
|
- Use @naeil/ui/naeil-header for the canonical naeil header; use Navigation from @naeil/ui/ui for a different product menu. Use NaeilFooter from @naeil/ui/naeil-footer for owned service footers; SiteFooter remains the generic primitive. Keep existing Next Nav/Footer compatibility adapters when applicable.
|
|
127
135
|
- Put NaeilHeaderAccountControl from @naeil/ui/naeil-header in authSlot for owned apps. Supply real session state, the busy flag and guarded logout callback. Keep pending state and the request lock above the header so desktop/mobile copies share them; do not copy account/avatar/menu markup.
|
|
128
|
-
-
|
|
129
|
-
-
|
|
136
|
+
- For owned Next services under NaeilLocaleProvider, import header/account/footer from @naeil/ui/naeil-locale/chrome. Read docs/components/naeil-locale.md for the root controller, provider, server routing, message loading and checker. /naeil-header and /naeil-footer remain standalone React adapters.
|
|
137
|
+
- Owned services must set service="abstorm" / "home" / "estimate" / "jev". The DS renders logo → .naeil → lowercase id; no statusSlot, status dot or extra indicator beside the brand.
|
|
138
|
+
- Supply only the service's own appNavigation tabs; never copy naeil.dev Projects/Blog. Single-screen services leave appNavigation empty; the mobile menu still exposes theme/locale/account without empty navigation.
|
|
139
|
+
- No header escape hatches: brandSuffix/brandSlot/toolbarSlot/subNav/brandLinkComponent, catalog/translations overrides, brandLinkProps/mobileFooter, className/containerClassName/style or data-slot/data-naeil-* marker overrides. Keep the shared tab and toolbar styles/states.
|
|
140
|
+
- Local wrappers supply menu data, routing/guards, locale and shared auth state. Do not copy the menu, mobile sheet, controls or footer implementation. Extend the shared component for reusable missing behavior.
|
|
141
|
+
- Since 0.9.0, shared CSS reserves a stable root gutter through globals or theme+components; opt out on html with data-naeil-scrollbar-gutter="auto" before first paint to disable both root reservation and lock correction. Compare classic/overlay and short/long body content through menus/dialogs; measure before fullPage screenshots.
|
|
130
142
|
- Use ui-app-frame on main; navigation/footer own their internal frames. Keep reading/settings/short-form sections inside that frame. Apply frame overrides consistently across routes.
|
|
131
143
|
- Check the installed package version and CSS before creating a replacement for an apparently missing component.
|
|
132
144
|
- Verify actual shared navigation/footer rendering and stable anchors across routes, themes and supported sizes, including signed-out, signed-in, menu, busy, cancellation/failure and retry states. An import or anonymous-only check is not adoption evidence. Record intentional route exceptions.
|
|
@@ -141,16 +153,3 @@ Owned-service checks also verify canonical header/footer destinations, rendered
|
|
|
141
153
|
A blanket ban on `<header>`, `<nav>` or `<footer>` would also reject valid section landmarks and adapters. Review the root composition and its rendered output instead. This repository checks packed React/Next examples; external applications still need their own upgrades, adapter migrations and CI. Publication and deployment are separate work.
|
|
142
154
|
|
|
143
155
|
See [canonical footer content and save interception](naeil-footer.md). Version 0.7.2 includes package/contract identity; registry publication, hosted Docs and per-service operational rollout have separate receipts.
|
|
144
|
-
|
|
145
|
-
## Shared naeil.dev login (services on `*.naeil.dev`)
|
|
146
|
-
|
|
147
|
-
Live since 2026-10-22 for naeil.dev, abestimate.naeil.dev and jev.naeil.dev. A new service joins the same login instead of running its own session, relay or password. Home Console (`:2455` on the tailnet) is outside the cookie scope.
|
|
148
|
-
|
|
149
|
-
- **Session:** read the default `@supabase/ssr` 0.9.0 cookie `sb-dathsxdsaxooifnxjket-auth-token` (and its `.0`, `.1` chunks) with `Domain=.naeil.dev; Path=/; Secure; SameSite=Lax`. Write refreshed chunks with the same options; never set a host-only copy.
|
|
150
|
-
- **Identity and access:** on the server call `supabase.auth.getUser()`, then `rpc('access_decision', { p_service_id: '<service>' })` with that session's access token. `allowed` → continue; `not_granted` / `member_inactive` / `service_inactive` → 403 page; terminal session errors → treat as signed out; other failures → 503.
|
|
151
|
-
- **Signed out:** redirect (307) to `https://naeil.dev/login?next=<absolute URL>`. Add the service origin to the site's `canonicalRedirect` allowlist (`src/lib/auth/redirect.ts`).
|
|
152
|
-
- **Logout:** `signOut({ scope: 'local' })`, then expire every shared cookie chunk with the same Domain/Path. All services see the user as signed out on the next request.
|
|
153
|
-
- **Header:** `NaeilHeaderAccountControl` in `authSlot`, linked to the login URL and the logout callback.
|
|
154
|
-
- **Onboarding:** register the service id in the central catalog, mark it verified, and grant users from Home Console (`console_set_access`).
|
|
155
|
-
|
|
156
|
-
Reference code: Estimate `src/server/naeil-shared-session.ts`; site `src/lib/auth/site-session.ts` and `cookie-domain.ts`. Commit `c076391` handles a stale pre-2026-10-17 host-only apex cookie that otherwise breaks the OAuth callback.
|
|
@@ -10,7 +10,7 @@ Avatar forwards Radix Root span props and defaults **size="default"**; sizes `sm
|
|
|
10
10
|
|
|
11
11
|
Inherited [Avatar 1.1.10 declaration](https://unpkg.com/@radix-ui/react-avatar@1.1.10/dist/index.d.ts).
|
|
12
12
|
|
|
13
|
-
The [shared usage contract](common.md) covers setup, inherited/native prop typing, themes, density, font delivery and overrides. [Wrapper source](https://github.com/naeil-dev/naeil-ui/blob/
|
|
13
|
+
The [shared usage contract](common.md) covers setup, inherited/native prop typing, themes, density, font delivery and overrides. [Wrapper source](https://github.com/naeil-dev/naeil-ui/blob/main/src/components/ui/avatar.tsx) is the API authority.
|
|
14
14
|
|
|
15
15
|
## Composition
|
|
16
16
|
|
package/docs/components/badge.md
CHANGED
|
@@ -6,7 +6,9 @@ Use Badge for concise status/category text. Prefer readable body/helper text for
|
|
|
6
6
|
|
|
7
7
|
Exports: `Badge`, `badgeVariants`. Native span props plus `variant` and `asChild`. Defaults: **variant="default", asChild=false**. Variants: `default`, `secondary`, `outline`, `destructive`, `success`, `warning`, `error`, `info`. asChild uses Slot.Root; no disabled/loading or announcement API. Default text is intentionally small and single-line.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
**Server Components:** To call `badgeVariants` on the server, import it from `@naeil/ui/components/ui/badge`. The `@naeil/ui/ui` component barrel remains a client boundary. Import `cn` from `@naeil/ui/utils` or the RSC-safe deep path `@naeil/ui/lib/utils`.
|
|
10
|
+
|
|
11
|
+
The [shared usage contract](common.md) covers setup, inherited/native prop typing, themes, density, font delivery and overrides. [Wrapper source](https://github.com/naeil-dev/naeil-ui/blob/main/src/components/ui/badge.tsx) is the API authority.
|
|
10
12
|
|
|
11
13
|
## Composition
|
|
12
14
|
|
|
@@ -24,6 +26,8 @@ Static badges are not focusable. Name status by text, not only color/icon. Dynam
|
|
|
24
26
|
|
|
25
27
|
## States, resilience and mistakes
|
|
26
28
|
|
|
29
|
+
Use `variant="error"` for error feedback; destructive badges retain the filled background/foreground pair described in [feedback and destructive color roles](common.md#feedback-and-destructive-color-roles).
|
|
30
|
+
|
|
27
31
|
Pending/empty/error are literal status content; disabled is inapplicable to a static badge. Avoid essential long text in tiny tags; use a paragraph instead, or deliberate `whitespace-normal break-words max-w-full` overrides and review. Do not infer interactivity from hover or pass disabled to a span/link.
|
|
28
32
|
|
|
29
33
|
Check this example in light/dark, comfortable/compact and mobile, including long Korean, English and Japanese text. Consumer overrides remain supported; follow the [shared override and acceptance guidance](common.md#styling-themes-and-overrides). Actual engine results and manual limits are in the [support matrix](../design/public-ui-support.md).
|
|
@@ -6,6 +6,8 @@ Use Button for an action, a native anchor for navigation, and Select for a value
|
|
|
6
6
|
|
|
7
7
|
Exports: `Button`, `buttonVariants`.
|
|
8
8
|
|
|
9
|
+
**Server Components:** To call `buttonVariants` on the server, import it from `@naeil/ui/components/ui/button`. The `@naeil/ui/ui` component barrel remains a client boundary. Import `cn` from `@naeil/ui/utils` or the RSC-safe deep path `@naeil/ui/lib/utils`.
|
|
10
|
+
|
|
9
11
|
| Prop | Contract / wrapper default |
|
|
10
12
|
| --- | --- |
|
|
11
13
|
| `variant` | `default` (default), `destructive`, `outline`, `secondary`, `ghost`, `link` |
|
|
@@ -15,7 +17,7 @@ Exports: `Button`, `buttonVariants`.
|
|
|
15
17
|
|
|
16
18
|
`size` names are preserved; shared CSS controls actual height (40/36px; lg adds 4px; mobile minimum 44px). `xs` does not imply an extra-small touch target. `loading` is not an API.
|
|
17
19
|
|
|
18
|
-
The [shared usage contract](common.md) covers setup, inherited/native prop typing, themes, density, font delivery and overrides. [Wrapper source](https://github.com/naeil-dev/naeil-ui/blob/
|
|
20
|
+
The [shared usage contract](common.md) covers setup, inherited/native prop typing, themes, density, font delivery and overrides. [Wrapper source](https://github.com/naeil-dev/naeil-ui/blob/main/src/components/ui/button.tsx) is the API authority.
|
|
19
21
|
|
|
20
22
|
## Composition
|
|
21
23
|
|
|
@@ -34,6 +36,12 @@ Native buttons activate with Enter/Space and show focus-visible; disabled button
|
|
|
34
36
|
|
|
35
37
|
## States, resilience and mistakes
|
|
36
38
|
|
|
39
|
+
Use `variant="destructive"` for the filled dangerous-action pair and `text-error` for error copy; see [feedback and destructive color roles](common.md#feedback-and-destructive-color-roles).
|
|
40
|
+
|
|
37
41
|
Demonstrate normal, disabled, saving and recovery; no-data/error belong to the operation. Do not nest Button in a link or treat variant="link" as navigation. With asChild anchors, own href/activation/disabled policy. For long translated actions use `className="h-auto min-h-11 whitespace-normal py-2"` and check both densities.
|
|
38
42
|
|
|
39
43
|
Check this example in light/dark, comfortable/compact and mobile, including long Korean, English and Japanese text. Consumer overrides remain supported; follow the [shared override and acceptance guidance](common.md#styling-themes-and-overrides). Actual engine results and manual limits are in the [support matrix](../design/public-ui-support.md).
|
|
44
|
+
|
|
45
|
+
## Pressed state and compatibility (2026-11-12)
|
|
46
|
+
|
|
47
|
+
Use `aria-pressed={selected}` for toggle actions. Every variant now shows a persistent 2px inset indicator; default uses its hover surface, and outline/secondary/ghost/link use `surface-selected` with foreground text. The destructive variant retains its dangerous-action fill. Filled variants inset the indicator 2px from the edge so it is surrounded by the contrasting fill. Indicators meet 3:1 against their adjacent fill and text meets 4.5:1 in both themes, including destructive hover. This changes the appearance of existing toggle buttons. Consumer `aria-pressed:*` classes still merge last; override the indicator and its contrast deliberately. `aria-pressed={false}` and ordinary actions retain their existing appearance.
|
package/docs/components/card.md
CHANGED
|
@@ -6,7 +6,7 @@ Use Card to group related information or a form. Use plain sections/headings for
|
|
|
6
6
|
|
|
7
7
|
Exports: `Card`, `CardElevated`, `CardHeader`, `CardFooter`, `CardTitle`, `CardAction`, `CardDescription`, `CardContent`. All accept `React.ComponentProps<"div">`; no variant/size/asChild/loading API. Card and CardElevated use `data-slot="card"`, default panel spacing and consumer className; CardElevated adds shadow/elevated dark surface. CardTitle is a **div**, not a semantic heading. CardAction occupies the second header column.
|
|
8
8
|
|
|
9
|
-
The [shared usage contract](common.md) covers setup, inherited/native prop typing, themes, density, font delivery and overrides. [Wrapper source](https://github.com/naeil-dev/naeil-ui/blob/
|
|
9
|
+
The [shared usage contract](common.md) covers setup, inherited/native prop typing, themes, density, font delivery and overrides. [Wrapper source](https://github.com/naeil-dev/naeil-ui/blob/main/src/components/ui/card.tsx) is the API authority.
|
|
10
10
|
|
|
11
11
|
## Composition
|
|
12
12
|
|
|
@@ -30,3 +30,7 @@ Static cards are not tab stops. Put real buttons/links inside and a semantic hea
|
|
|
30
30
|
Loading/empty/error are consumer content with status/retry actions, not Card props; disabled is inapplicable to the static surface. Keep CardAction concise, wrap long localized headings and use `w-full max-w-*` instead of fixed widths. Avoid nested cards and implicit clickable-card behavior; an entire link card must contain no nested controls.
|
|
31
31
|
|
|
32
32
|
Check this example in light/dark, comfortable/compact and mobile, including long Korean, English and Japanese text. Consumer overrides remain supported; follow the [shared override and acceptance guidance](common.md#styling-themes-and-overrides). Actual engine results and manual limits are in the [support matrix](../design/public-ui-support.md).
|
|
33
|
+
|
|
34
|
+
## Typography and theme compatibility (2026-11-12)
|
|
35
|
+
|
|
36
|
+
CardTitle now uses the component-title role (16/25px), replacing `leading-none`; CardDescription uses supporting text (14/22px). Wrapped titles and descriptions can make cards taller. Explicit `className` typography/leading overrides still win. CardElevated now also uses its elevated dark surface under OS dark with no root theme class when importing globals.css.
|
|
@@ -8,7 +8,7 @@ Export: `Checkbox`. Forwards Radix Root props: `checked`/`defaultChecked`/`onChe
|
|
|
8
8
|
|
|
9
9
|
Inherited [Checkbox 1.3.3 declaration](https://unpkg.com/@radix-ui/react-checkbox@1.3.3/dist/index.d.ts).
|
|
10
10
|
|
|
11
|
-
The [shared usage contract](common.md) covers setup, inherited/native prop typing, themes, density, font delivery and overrides. [Wrapper source](https://github.com/naeil-dev/naeil-ui/blob/
|
|
11
|
+
The [shared usage contract](common.md) covers setup, inherited/native prop typing, themes, density, font delivery and overrides. [Wrapper source](https://github.com/naeil-dev/naeil-ui/blob/main/src/components/ui/checkbox.tsx) is the API authority.
|
|
12
12
|
|
|
13
13
|
## Composition
|
|
14
14
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Shared usage contract
|
|
2
2
|
|
|
3
|
-
These maintained guides
|
|
3
|
+
These maintained guides describe the repository APIs; for the current published release, see [release identities and status](../package-release.md#publication-and-deployment). The [hosted Docs](https://naeil-dev.github.io/naeil-ui/) were last verified for [0.5.0 on 2026-10-15](https://github.com/naeil-dev/naeil-ui/releases/tag/v0.5.0), while `pnpm storybook` shows this checkout. Start with the [consumer setup and migration](../design/v2-migration.md). Primitives and generic Navigation/SiteFooter are exported from `@naeil/ui/ui`; the actual branded whole header uses the opt-in [NaeilHeader](naeil-header.md) entry `@naeil/ui/naeil-header`. Both are React-only. The legacy root needs the optional Next/next-intl peers. Examples import real wrappers; product state, validation and network requests belong to consumers.
|
|
4
4
|
|
|
5
5
|
## Styling, themes and overrides
|
|
6
6
|
|
|
@@ -18,6 +18,12 @@ Loading is composition: state + meaningful text + `aria-busy`, with `disabled` w
|
|
|
18
18
|
|
|
19
19
|
Avoid nested buttons/links or embedding unrelated controls in a single clickable surface. A small Switch/Checkbox graphic has a 44px pseudo-element target; keep label rows at least 44px and leave room so adjacent targets do not overlap. Test actual pointer hit areas, keyboard focus and text wrapping in the consumer.
|
|
20
20
|
|
|
21
|
+
### Feedback and destructive color roles
|
|
22
|
+
|
|
23
|
+
Use `text-error` for error message text/icons, `bg-error-bg` for subtle error surfaces, and `border-error` for invalid boundaries. Reserve `destructive` / `destructive-foreground` for the filled background/foreground pair of a dangerous action (`Button variant="destructive"`) or destructive Badge.
|
|
24
|
+
|
|
25
|
+
Never use `text-destructive` (or destructive as text on canvas/card) for error copy: it fails normal-text contrast in dark mode (about 3.45:1 on the background, below 4.5:1). Use `text-error` instead.
|
|
26
|
+
|
|
21
27
|
## Long and localized content
|
|
22
28
|
|
|
23
29
|
Examples include Korean, English and Japanese. Set `lang` on translated content (Japanese selects Noto Sans JP in shared globals). Use responsive widths, `min-width: 0`, flexible heights and consumer wrapping classes for long actions/status text. The wrappers may use single-line truncation; retain the complete accessible name and offer the full value elsewhere if necessary. Do not shrink essential text to fit.
|
|
@@ -29,3 +35,18 @@ Run each affected composition under matching light/dark, comfortable/compact, mo
|
|
|
29
35
|
## Exact inherited contracts
|
|
30
36
|
|
|
31
37
|
Native wrappers use React 19 element props: inspect your installed `@types/react/index.d.ts`, or infer `React.ComponentProps<typeof Component>`. Radix wrappers use `React.ComponentProps<typeof Primitive.Part>`; inspect the installed declarations reached through `radix-ui/dist/index.d.ts`. Per-family guides link pinned declarations inspected locally during this work. Those links describe the inherited contract, not a promise that all primitive configurations have been tested. Wrapper defaults below are separate from primitive defaults; omitted primitive defaults are not invented here. Consumer examples and browser checks demonstrate only their recorded configuration.
|
|
38
|
+
|
|
39
|
+
## Theme and typography compatibility (2026-11-12)
|
|
40
|
+
|
|
41
|
+
The `dark:` variant in globals.css now follows the same conditions as generated dark tokens: `.dark` on an element or ancestor, plus OS dark under `:root:not(.light)` (including the root itself). Root `.light` opts out of OS dark; a deliberately nested `.dark` still opts into dark. Use only one root theme class. `.light` is a root OS opt-out, not a nested light-theme reset. Consumers composing theme.css + components.css with their own Tailwind entry must copy this variant into that entry to match the token contract:
|
|
42
|
+
|
|
43
|
+
```css
|
|
44
|
+
@custom-variant dark {
|
|
45
|
+
&:where(.dark, .dark *) { @slot; }
|
|
46
|
+
@media (prefers-color-scheme: dark) {
|
|
47
|
+
&:where(:root:not(.light), :root:not(.light) *) { @slot; }
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Generated font-size utilities now include the approved line heights: `text-3xl` 32/42px, `text-xl` 20/29px, `text-base` 16/25px (UI body, component title and controls), `text-sm` 14/22px and `text-xs` 13/20px at a 16px root. Reading body uses `text-base leading-relaxed` (16/30px). PageTitle, DialogTitle and CardTitle no longer override their role line heights. This changes wrapping and vertical rhythm; recheck constrained layouts. Explicit consumer `leading-*` utilities remain supported. Numeric values live in typography.json; regenerate with `pnpm build:tokens`.
|
|
@@ -6,14 +6,20 @@ Use Dialog for a focused modal task or detail that requires an interruption. Pre
|
|
|
6
6
|
|
|
7
7
|
Exports: `Dialog`, `DialogTrigger`, `DialogContent`, `DialogHeader`, `DialogFooter`, `DialogTitle`, `DialogDescription`, `DialogClose`, `DialogPortal`, `DialogOverlay`.
|
|
8
8
|
|
|
9
|
-
Root forwards Radix `open`/`defaultOpen`/`onOpenChange`/`modal`; wrapper adds no root defaults. Trigger/Close accept primitive button props including `asChild`. Content forwards primitive focus/outside/Escape handlers and **defaults `showCloseButton=true`**; it creates its own Portal and Overlay. Footer is a div with **`showCloseButton=false`**. Header is a div; Title/Description wrap primitive semantic elements.
|
|
9
|
+
Root forwards Radix `open`/`defaultOpen`/`onOpenChange`/`modal`; wrapper adds no root defaults. Trigger/Close accept primitive button props including `asChild`. Content forwards primitive focus/outside/Escape handlers and **defaults `showCloseButton=true`**; it creates its own Portal and Overlay. Footer is a div with **`showCloseButton=false`**. Header is a div; Title/Description wrap primitive semantic elements. Content and Footer each accept `closeLabel?: string` (default `"Close"`) for their built-in X accessible name and footer button text, respectively. Content does not expose a portal `container` prop; the separately exported DialogPortal is for a custom composition.
|
|
10
10
|
|
|
11
11
|
Inherited [Dialog 1.1.15 declaration](https://unpkg.com/@radix-ui/react-dialog@1.1.15/dist/index.d.ts).
|
|
12
12
|
|
|
13
|
-
The [shared usage contract](common.md) covers setup, inherited/native prop typing, themes, density, font delivery and overrides. [Wrapper source](https://github.com/naeil-dev/naeil-ui/blob/
|
|
13
|
+
The [shared usage contract](common.md) covers setup, inherited/native prop typing, themes, density, font delivery and overrides. [Wrapper source](https://github.com/naeil-dev/naeil-ui/blob/main/src/components/ui/dialog.tsx) is the API authority.
|
|
14
|
+
|
|
15
|
+
## Compatibility decision (2026-11-09)
|
|
16
|
+
|
|
17
|
+
Keep Content `showCloseButton=true`: changing it would silently remove the only visible pointer exit in pinned consumers that rely on it. Adding `closeLabel` is additive and non-breaking; Footer still defaults to `showCloseButton=false`.
|
|
14
18
|
|
|
15
19
|
## Composition
|
|
16
20
|
|
|
21
|
+
Choose one close affordance: either the built-in X with a localized `closeLabel`, or `showCloseButton={false}` plus a footer `DialogClose` (including Footer's built-in close button). Never ship English defaults in ko/ja UIs or add a second close control.
|
|
22
|
+
|
|
17
23
|
```tsx
|
|
18
24
|
<Dialog>
|
|
19
25
|
<DialogTrigger asChild><Button type="button">Edit name</Button></DialogTrigger>
|
|
@@ -28,7 +34,7 @@ The [shared usage contract](common.md) covers setup, inherited/native prop typin
|
|
|
28
34
|
</Dialog>
|
|
29
35
|
```
|
|
30
36
|
|
|
31
|
-
Import the named components from `@naeil/ui/ui`. Variables/handlers in snippets are consumer-owned. Run **UI / Dialog / Usage** in Storybook for a complete interactive example; its Docs page renders this same guide.
|
|
37
|
+
Import the named components from `@naeil/ui/ui`. Variables/handlers in snippets are consumer-owned. Run **UI / Dialog / Usage** in Storybook for a complete interactive example; its Docs page renders this same guide. **UI / Dialog / Localized Close** demonstrates built-in X labels in Korean, Japanese and English.
|
|
32
38
|
|
|
33
39
|
## Keyboard, focus and accessible content
|
|
34
40
|
|
|
@@ -36,6 +42,10 @@ For the modal example, opening moves focus inside, Tab/Shift+Tab stay inside, Es
|
|
|
36
42
|
|
|
37
43
|
## States, resilience and mistakes
|
|
38
44
|
|
|
39
|
-
Disabled applies to triggers/actions; loading/error/empty are content state. Keep failed input inside the dialog and enable retry. Long/mobile content
|
|
45
|
+
Disabled applies to triggers/actions; loading/error/empty are content state. Keep failed input inside the dialog and enable retry. Long/mobile content is scrollable within the dynamic viewport by default; preserve that behavior and do not hide all close actions. Consumer outside-dismissal overrides need keyboard/focus review. Do not nest modal roots casually or prevent Escape without an accessible exit.
|
|
40
46
|
|
|
41
47
|
Check this example in light/dark, comfortable/compact and mobile, including long Korean, English and Japanese text. Consumer overrides remain supported; follow the [shared override and acceptance guidance](common.md#styling-themes-and-overrides). Actual engine results and manual limits are in the [support matrix](../design/public-ui-support.md).
|
|
48
|
+
|
|
49
|
+
## Scroll default compatibility (2026-11-12)
|
|
50
|
+
|
|
51
|
+
Content now defaults to `max-h-[min(calc(100%-2rem),calc(100dvh-2rem))] overflow-y-auto`, keeping tall content and footer actions reachable inside the dynamic viewport. The percentage cap also respects CSS zoom, where viewport units alone can exceed the visible area. Remove equivalent consumer classes. To own a different scroll layout, pass a replacement max-height/overflow class; `className="max-h-[none] overflow-y-visible"` opts out (the consumer must keep all actions reachable). Title now follows the section-title 20/29px typography role.
|
|
@@ -10,7 +10,7 @@ Root: primitive `open`/`defaultOpen`/`onOpenChange`, `modal`, `dir`; no wrapper
|
|
|
10
10
|
|
|
11
11
|
Inherited [DropdownMenu 2.1.16 declaration](https://unpkg.com/@radix-ui/react-dropdown-menu@2.1.16/dist/index.d.ts), including its inherited Menu types.
|
|
12
12
|
|
|
13
|
-
The [shared usage contract](common.md) covers setup, inherited/native prop typing, themes, density, font delivery and overrides. [Wrapper source](https://github.com/naeil-dev/naeil-ui/blob/
|
|
13
|
+
The [shared usage contract](common.md) covers setup, inherited/native prop typing, themes, density, font delivery and overrides. [Wrapper source](https://github.com/naeil-dev/naeil-ui/blob/main/src/components/ui/dropdown-menu.tsx) is the API authority.
|
|
14
14
|
|
|
15
15
|
## Composition
|
|
16
16
|
|
package/docs/components/input.md
CHANGED
|
@@ -6,7 +6,7 @@ Use Input for native single-line text/value entry. Use [Textarea](./textarea.md)
|
|
|
6
6
|
|
|
7
7
|
Export: `Input`. Accepts `React.ComponentProps<"input">`; no wrapper default for `type`, value, disabled or validation. Native omitted type resolves to text. Important props: `value` + `onChange` or `defaultValue`, `name`, `type`, `required`, `readOnly`, `disabled`, `autoComplete`, `inputMode`, `min`/`max`, `minLength`/`maxLength`, `pattern`, form and ARIA props. No built-in label, error text or async validation API.
|
|
8
8
|
|
|
9
|
-
The [shared usage contract](common.md) covers setup, inherited/native prop typing, themes, density, font delivery and overrides. [Wrapper source](https://github.com/naeil-dev/naeil-ui/blob/
|
|
9
|
+
The [shared usage contract](common.md) covers setup, inherited/native prop typing, themes, density, font delivery and overrides. [Wrapper source](https://github.com/naeil-dev/naeil-ui/blob/main/src/components/ui/input.tsx) is the API authority.
|
|
10
10
|
|
|
11
11
|
## Composition
|
|
12
12
|
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
# NaeilFooter — canonical service footer
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Available since **0.7.1**, the first published release containing this entry. See [release identities and publication status](../package-release.md#publication-and-deployment) for the current published release; hosted Docs and service activation have separate receipts. React-only opt-in entry:
|
|
4
4
|
|
|
5
5
|
```tsx
|
|
6
6
|
import { NaeilFooter } from '@naeil/ui/naeil-footer';
|
|
7
|
-
<NaeilFooter locale="ja"
|
|
7
|
+
<NaeilFooter locale="ja" Link={GuardedLink} />
|
|
8
8
|
```
|
|
9
9
|
|
|
10
10
|
Use with [NaeilHeader and its account control](naeil-header.md) on owned user-facing services. Both `full` and `compact` contain the .naeil home link, Projects, Blog and the official naeil GitHub link. Full also displays the existing tagline. Compact reduces spacing/description, not the destinations. No social account or legal claim is invented. The website's former generic X homepage icon is not a canonical account link.
|
|
11
11
|
|
|
12
|
+
Owned services use the shared default (`full`) as-is, matching naeil.dev; omit `variant` in starters. Use `compact` only as a deliberate, recorded exception, such as a focused task screen. Compact link groups are top-aligned so their titles line up; an `items-*` utility in `containerClassName` still controls every child's vertical alignment.
|
|
13
|
+
|
|
12
14
|
`locale` selects `en`, `ko`, or `ja`. `resolveHref(path)` defaults to official localized naeil.dev destinations; a local website router may resolve unlocalized paths itself. `Link` handles **every** canonical anchor, including absolute GitHub URLs. It must forward href, target, rel, onClick, className and children. GitHub opens a new tab with noopener/noreferrer. The shared implementation does not itself trigger navigation or await saves.
|
|
13
15
|
|
|
14
16
|
`onNavigate(event, item)` reaches the brand and all group links. An application can synchronously preventDefault, flush its draft, and navigate only after successful saving. Preserve defaultPrevented, modified/middle clicks and target behavior according to the application's established guard. Do not run both adapter and onNavigate guards twice. Native explicit `external:true` links in generic SiteFooter bypass Link for compatibility, but **still call onNavigate**. This closes the former missing interception path.
|