@naeil/ui 0.1.0 → 0.3.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 +18 -0
- package/DESIGN.md +186 -0
- package/LICENSE +21 -0
- package/README.md +85 -22
- package/THIRD_PARTY_NOTICES.md +49 -0
- package/dist/chunk-JBTDRVZ7.js +46 -0
- package/dist/chunk-QKVN6Q6C.js +438 -0
- package/dist/components/index.d.ts +87 -0
- package/dist/components/index.js +832 -0
- package/dist/components/ui/index.d.ts +105 -0
- package/dist/components/ui/index.js +736 -0
- package/dist/lib/utils.d.ts +5 -0
- package/dist/lib/utils.js +7 -0
- package/dist/source/components/footer.d.ts +15 -0
- package/dist/source/components/footer.js +172 -0
- package/dist/source/components/index.d.ts +11 -0
- package/dist/source/components/index.js +26 -0
- package/dist/source/components/locale-switcher.d.ts +17 -0
- package/dist/source/components/locale-switcher.js +66 -0
- package/dist/source/components/logo.d.ts +9 -0
- package/dist/source/components/logo.js +52 -0
- package/dist/source/components/nav.d.ts +39 -0
- package/dist/source/components/nav.js +412 -0
- package/dist/source/components/theme-provider.d.ts +6 -0
- package/dist/source/components/theme-provider.js +45 -0
- package/dist/source/components/theme-toggle-icon.d.ts +5 -0
- package/dist/source/components/theme-toggle-icon.js +62 -0
- package/dist/source/components/theme-toggle.d.ts +5 -0
- package/dist/source/components/theme-toggle.js +34 -0
- package/dist/source/components/typography.d.ts +12 -0
- package/dist/source/components/typography.js +49 -0
- package/dist/source/components/ui/avatar.d.ts +14 -0
- package/dist/source/components/ui/avatar.js +137 -0
- package/dist/source/components/ui/badge.d.ts +13 -0
- package/dist/source/components/ui/badge.js +76 -0
- package/dist/source/components/ui/button.d.ts +14 -0
- package/dist/source/components/ui/button.js +88 -0
- package/dist/source/components/ui/card.d.ts +13 -0
- package/dist/source/components/ui/card.js +135 -0
- package/dist/source/components/ui/checkbox.d.ts +7 -0
- package/dist/source/components/ui/checkbox.js +58 -0
- package/dist/source/components/ui/dialog.d.ts +20 -0
- package/dist/source/components/ui/dialog.js +182 -0
- package/dist/source/components/ui/dropdown-menu.d.ts +28 -0
- package/dist/source/components/ui/dropdown-menu.js +282 -0
- package/dist/source/components/ui/index.d.ts +19 -0
- package/dist/source/components/ui/index.js +131 -0
- package/dist/source/components/ui/input.d.ts +6 -0
- package/dist/source/components/ui/input.js +49 -0
- package/dist/source/components/ui/select.d.ts +16 -0
- package/dist/source/components/ui/select.js +197 -0
- package/dist/source/components/ui/sonner.d.ts +6 -0
- package/dist/source/components/ui/sonner.js +80 -0
- package/dist/source/components/ui/switch.d.ts +7 -0
- package/dist/source/components/ui/switch.js +54 -0
- package/dist/source/components/ui/tabs.d.ts +10 -0
- package/dist/source/components/ui/tabs.js +57 -0
- package/dist/source/components/ui/textarea.d.ts +6 -0
- package/dist/source/components/ui/textarea.js +46 -0
- package/dist/source/i18n/config.d.ts +6 -0
- package/dist/source/i18n/config.js +12 -0
- package/dist/source/i18n/routing.d.ts +371 -0
- package/dist/source/i18n/routing.js +17 -0
- package/dist/source/lib/design/modal-inert.d.ts +6 -0
- package/dist/source/lib/design/modal-inert.js +73 -0
- package/dist/source/lib/utils.d.ts +5 -0
- package/dist/source/lib/utils.js +8 -0
- package/dist/tokens.ts +217 -0
- package/docs/components/README.md +21 -0
- package/docs/components/avatar.md +47 -0
- package/docs/components/badge.md +29 -0
- package/docs/components/button.md +39 -0
- package/docs/components/card.md +32 -0
- package/docs/components/checkbox.md +35 -0
- package/docs/components/common.md +31 -0
- package/docs/components/composition.md +68 -0
- package/docs/components/dialog.md +41 -0
- package/docs/components/dropdown-menu.md +38 -0
- package/docs/components/input.md +32 -0
- package/docs/components/select.md +39 -0
- package/docs/components/sonner.md +37 -0
- package/docs/components/switch.md +33 -0
- package/docs/components/tabs.md +40 -0
- package/docs/components/textarea.md +35 -0
- package/docs/design/component-demand.md +30 -0
- package/docs/design/public-ui-final-verification.md +96 -0
- package/docs/design/public-ui-release-validation.md +86 -0
- package/docs/design/public-ui-stage3-verification.md +98 -0
- package/docs/design/public-ui-stage4-verification.md +129 -0
- package/docs/design/public-ui-support.md +40 -0
- package/docs/design/v2-migration.md +199 -0
- package/docs/package-release.md +89 -0
- package/licenses/Noto-Sans-JP-OFL.txt +93 -0
- package/licenses/Nunito-Sans-OFL.txt +93 -0
- package/licenses/Pretendard-OFL.txt +94 -0
- package/licenses/React-MIT.txt +21 -0
- package/licenses/Storybook-MIT.txt +21 -0
- package/licenses/popper-MIT.txt +25 -0
- package/licenses/react-remove-scroll-bar-MIT.txt +21 -0
- package/licenses/store2-MIT.txt +22 -0
- package/licenses/toggle-selection-MIT.txt +21 -0
- package/package.json +215 -26
- package/src/styles/components.css +276 -0
- package/src/styles/globals.css +18 -0
- package/src/styles/theme.css +293 -0
- package/messages/en.json +0 -221
- package/messages/ja.json +0 -221
- package/messages/ko.json +0 -221
- package/postcss.config.mjs +0 -7
- package/src/app/[locale]/(content)/baby-agent/page.tsx +0 -30
- package/src/app/[locale]/(content)/blog/page.tsx +0 -68
- package/src/app/[locale]/(content)/cc/page.tsx +0 -216
- package/src/app/[locale]/(content)/design/page.tsx +0 -703
- package/src/app/[locale]/(content)/layout.tsx +0 -7
- package/src/app/[locale]/(content)/pkm/page.tsx +0 -31
- package/src/app/[locale]/(content)/projects/page.tsx +0 -100
- package/src/app/[locale]/(content)/sa/page.tsx +0 -179
- package/src/app/[locale]/(landing)/page.tsx +0 -157
- package/src/app/[locale]/layout.tsx +0 -72
- package/src/app/[locale]/login/actions.ts +0 -10
- package/src/app/[locale]/login/oauth-buttons.tsx +0 -78
- package/src/app/[locale]/login/page.tsx +0 -85
- package/src/app/auth/callback/route.ts +0 -78
- package/src/app/favicon.ico +0 -0
- package/src/app/globals.css +0 -32
- package/src/app/layout.tsx +0 -22
- package/src/app/sitemap.ts +0 -26
- package/src/components/accent-picker.tsx +0 -62
- package/src/components/auth-slot.tsx +0 -96
- package/src/components/footer-wrapper.tsx +0 -8
- package/src/components/footer.tsx +0 -193
- package/src/components/hero-scene.tsx +0 -948
- package/src/components/hero-section.tsx +0 -57
- package/src/components/locale-switcher.tsx +0 -74
- package/src/components/logo.tsx +0 -51
- package/src/components/nav-server-wrapper.tsx +0 -22
- package/src/components/nav-wrapper.tsx +0 -45
- package/src/components/nav.tsx +0 -550
- package/src/components/paraglider-cursor.tsx +0 -112
- package/src/components/project-layout.tsx +0 -194
- package/src/components/theme-provider.tsx +0 -10
- package/src/components/theme-toggle-icon.tsx +0 -62
- package/src/components/theme-toggle.tsx +0 -39
- package/src/components/typography.tsx +0 -27
- package/src/components/ui/avatar.tsx +0 -109
- package/src/components/ui/badge.tsx +0 -47
- package/src/components/ui/button.tsx +0 -65
- package/src/components/ui/card.tsx +0 -106
- package/src/components/ui/dialog.tsx +0 -158
- package/src/components/ui/dropdown-menu.tsx +0 -257
- package/src/components/ui/input.tsx +0 -21
- package/src/components/ui/sonner.tsx +0 -54
- package/src/components/workflow-diagram.tsx +0 -414
- package/src/i18n/config.ts +0 -8
- package/src/i18n/request.ts +0 -16
- package/src/i18n/routing.ts +0 -12
- package/src/lib/supabase/client.ts +0 -18
- package/src/lib/supabase/middleware.ts +0 -42
- package/src/lib/supabase/server.ts +0 -37
- package/src/lib/utils.ts +0 -6
- package/src/proxy.ts +0 -62
- package/src/tokens/.gitkeep +0 -0
- package/src/tokens/color.json +0 -56
- package/src/tokens/radius.json +0 -9
- package/src/tokens/semantic.json +0 -81
- package/src/tokens/shadow.json +0 -7
- package/src/tokens/spacing.json +0 -16
- package/src/tokens/typography.json +0 -34
- package/src/types/i18n.d.ts +0 -6
- package/src/utils/.gitkeep +0 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Input
|
|
2
|
+
|
|
3
|
+
Use Input for native single-line text/value entry. Use [Textarea](./textarea.md) for multiline text, Select for a small known option set, and Checkbox/Switch for booleans.
|
|
4
|
+
|
|
5
|
+
## API and wrapper defaults
|
|
6
|
+
|
|
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
|
+
|
|
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/81fd395031dc5b20074c4d915a8585360ce2d28a/src/components/ui/input.tsx) is the API authority.
|
|
10
|
+
|
|
11
|
+
## Composition
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
<label htmlFor="email">Notification email</label>
|
|
15
|
+
<Input id="email" name="email" type="email" required
|
|
16
|
+
autoComplete="email" aria-invalid={invalid}
|
|
17
|
+
aria-describedby="email-help email-error" />
|
|
18
|
+
<p id="email-help">Used for important updates.</p>
|
|
19
|
+
<p id="email-error" role="alert">{invalid ? 'Enter a valid email address.' : ''}</p>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Import the named components from `@naeil/ui/ui`. Variables/handlers in snippets are consumer-owned. Run **UI / Input / Usage** in Storybook for a complete interactive example; its Docs page renders this same guide.
|
|
23
|
+
|
|
24
|
+
## Keyboard, focus and accessible content
|
|
25
|
+
|
|
26
|
+
Tab focuses the input; text/navigation/editing follow the native input type. Consumer validation should focus the first invalid field and retain its value. Use labels and described help/errors; announce asynchronous validation outcomes without announcing each keystroke. Read-only and disabled have different focus/form submission behavior.
|
|
27
|
+
|
|
28
|
+
## States, resilience and mistakes
|
|
29
|
+
|
|
30
|
+
Show normal, empty, disabled, read-only, invalid and recovery. Loading may disable submission while preserving editable values; a disabled input is omitted from FormData. Never use placeholder as the only label, mix controlled/uncontrolled values, or lower mobile font size. Long input values scroll inside the native control; wrap helper/error text outside it.
|
|
31
|
+
|
|
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).
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Select
|
|
2
|
+
|
|
3
|
+
Use Select for one value from a finite option set. Prefer native select when native platform behavior is essential; use DropdownMenu for actions and text input/search for very large or editable choices.
|
|
4
|
+
|
|
5
|
+
## API and wrapper defaults
|
|
6
|
+
|
|
7
|
+
Exports: `Select`, `SelectValue`, `SelectGroup`, `SelectTrigger`, `SelectContent`, `SelectItem`, `SelectLabel`, `SelectSeparator`, `SelectScrollUpButton`, `SelectScrollDownButton`.
|
|
8
|
+
|
|
9
|
+
Select/Value/Group alias Radix parts. Root: `value`/`defaultValue`/`onValueChange`, `open`/`defaultOpen`/`onOpenChange`, `name`, `required`, `disabled`, `form`, `dir`, `autoComplete`; wrapper sets no root defaults. Trigger forwards button/ARIA props and renders a chevron. Value accepts placeholder. Content creates Portal, Viewport and scroll buttons and defaults **position="popper", sideOffset=6**; forwards position/alignment/collision/Escape/close-focus/outside handlers. Item requires a nonempty string `value`, accepts `disabled` and `textValue`, and creates ItemText/Indicator. No searchable, multiselect or async data API; SelectPortal/Viewport/Icon/ItemText are not public exports.
|
|
10
|
+
|
|
11
|
+
Inherited [Select 2.2.6 declaration](https://unpkg.com/@radix-ui/react-select@2.2.6/dist/index.d.ts).
|
|
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/81fd395031dc5b20074c4d915a8585360ce2d28a/src/components/ui/select.tsx) is the API authority.
|
|
14
|
+
|
|
15
|
+
## Composition
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
<label htmlFor="language">Language</label>
|
|
19
|
+
<Select name="language" defaultValue="en">
|
|
20
|
+
<SelectTrigger id="language"><SelectValue placeholder="Choose language" /></SelectTrigger>
|
|
21
|
+
<SelectContent>
|
|
22
|
+
<SelectItem value="ko">한국어</SelectItem>
|
|
23
|
+
<SelectItem value="en">English</SelectItem>
|
|
24
|
+
<SelectItem value="ja">日本語</SelectItem>
|
|
25
|
+
</SelectContent>
|
|
26
|
+
</Select>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Import the named components from `@naeil/ui/ui`. Variables/handlers in snippets are consumer-owned. Run **UI / Select / Usage** in Storybook for a complete interactive example; its Docs page renders this same guide.
|
|
30
|
+
|
|
31
|
+
## Keyboard, focus and accessible content
|
|
32
|
+
|
|
33
|
+
Trigger is the labeled combobox. Enter/Space/arrows open; arrows/Home/End/typeahead navigate enabled options; Enter chooses, Escape cancels and focus returns. Name and value allow form submission. Put aria-invalid/aria-describedby on Trigger and visible help/error nearby; SelectLabel labels a group, not the form control.
|
|
34
|
+
|
|
35
|
+
## States, resilience and mistakes
|
|
36
|
+
|
|
37
|
+
Show placeholder, selected, disabled root/item, invalid, loading and empty option sets. Loading/empty data is consumer state: disable the trigger and provide external status/retry, not an empty-string SelectItem. Empty string is the clear/placeholder root value. Trigger truncates long text; ensure the full accessible name/value remains available and option text wraps. Theme/density must be on the document root because content is portaled.
|
|
38
|
+
|
|
39
|
+
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).
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Toaster
|
|
2
|
+
|
|
3
|
+
Use Toaster for brief nonblocking outcome feedback. Keep form errors inline and persistent; use Dialog only when a task needs focused interaction. Critical recovery must remain available after a toast expires.
|
|
4
|
+
|
|
5
|
+
## API and wrapper defaults
|
|
6
|
+
|
|
7
|
+
Export: `Toaster`. `toast` is **not** re-exported: import it from `sonner` (declare Sonner as a direct consumer dependency when using its API).
|
|
8
|
+
|
|
9
|
+
Accepts Sonner `ToasterProps`. Wrapper defaults: theme from next-themes (falls back to `system`), position **bottom-right**, duration **4000ms**, semantic icons and CSS-variable style. Props are spread last: consumer `theme`, `position`, `duration`, `icons`, `style`, `className`, `toastOptions`, `offset`, `mobileOffset`, `closeButton`, `hotkey`, `containerAriaLabel` override wrapper values; `style`/`icons` replace that object, not a deep merge. Mount one instance per intended notification scope.
|
|
10
|
+
|
|
11
|
+
Inherited [Sonner 2.0.7 declarations](https://unpkg.com/sonner@2.0.7/dist/index.d.ts). Installed Sonner implements polite notifications and Alt+T as its default focus hotkey; these are primitive behaviors, not new wrapper defaults.
|
|
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/81fd395031dc5b20074c4d915a8585360ce2d28a/src/components/ui/sonner.tsx) is the API authority.
|
|
14
|
+
|
|
15
|
+
## Composition
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { Toaster, Button } from '@naeil/ui/ui';
|
|
19
|
+
import { toast } from 'sonner';
|
|
20
|
+
<>
|
|
21
|
+
<Button type="button" onClick={() => toast.success('Settings saved.')}>Save</Button>
|
|
22
|
+
<Toaster closeButton containerAriaLabel="Notifications"
|
|
23
|
+
toastOptions={{ closeButtonAriaLabel: 'Dismiss notification' }} />
|
|
24
|
+
</>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Import the named components from `@naeil/ui/ui`. Variables/handlers in snippets are consumer-owned. Run **UI / Toaster / Usage** in Storybook for a complete interactive example; its Docs page renders this same guide.
|
|
28
|
+
|
|
29
|
+
## Keyboard, focus and accessible content
|
|
30
|
+
|
|
31
|
+
Sonner creates its notification region; meaningful text should convey status without color. Name notification/close controls in the product language. The tested keyboard flow uses Alt+T to focus notifications, Tab to an action. Escape collapses the notification stack; moving focus out restores the prior action. macOS WebKit may require Option+Tab/Option+Shift+Tab to include buttons under its default keyboard-navigation setting. Review conflicts before changing hotkeys. Verify actual announcements with a screen reader manually; axe does not prove them.
|
|
32
|
+
|
|
33
|
+
## States, resilience and mistakes
|
|
34
|
+
|
|
35
|
+
Demonstrate success/info/warning/error, loading updated by the same id, and an action. Toaster has no disabled/loading prop; those belong to toast calls and initiating controls. With no notifications it renders no message. Keep long multilingual text wrap-safe, use mobileOffset deliberately, avoid duplicate toasters and move persistent errors/retry to the page. Custom style/icons/animations need contrast and reduced-motion verification.
|
|
36
|
+
|
|
37
|
+
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).
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Switch
|
|
2
|
+
|
|
3
|
+
Use Switch for an on/off preference; tell the user whether it applies immediately or on Save. Use Checkbox for consent, selection or a group of options; use Select for more than two values.
|
|
4
|
+
|
|
5
|
+
## API and wrapper defaults
|
|
6
|
+
|
|
7
|
+
Export: `Switch`. Forwards Radix Root props: `checked`/`defaultChecked`/`onCheckedChange`, `disabled`, `required`, `name`, `value`, `form`, `id`, button/ARIA props. Wrapper sets no state defaults and always renders its own Thumb; no public Thumb, size, loading or indeterminate API. Graphic is 36×22px with a 44px pseudo-element hit area independent of density. Underlying form mirroring comes from Radix when used in a form.
|
|
8
|
+
|
|
9
|
+
Inherited [Switch 1.2.6 declaration](https://unpkg.com/@radix-ui/react-switch@1.2.6/dist/index.d.ts).
|
|
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/81fd395031dc5b20074c4d915a8585360ce2d28a/src/components/ui/switch.tsx) is the API authority.
|
|
12
|
+
|
|
13
|
+
## Composition
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
<div className="ui-choice-label">
|
|
17
|
+
<Switch id="summary" name="summary" defaultChecked />
|
|
18
|
+
<label htmlFor="summary">Weekly summary</label>
|
|
19
|
+
</div>
|
|
20
|
+
<p>Changes apply when you save.</p>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Import the named components from `@naeil/ui/ui`. Variables/handlers in snippets are consumer-owned. Run **UI / Switch / Usage** in Storybook for a complete interactive example; its Docs page renders this same guide.
|
|
24
|
+
|
|
25
|
+
## Keyboard, focus and accessible content
|
|
26
|
+
|
|
27
|
+
Tab focuses enabled switches; Space toggles and aria-checked exposes the boolean. Keep a stable label across on/off states. The label should activate the control; checked values submit, unchecked/disabled controls are omitted. No mixed state; choose Checkbox if mixed selection is needed.
|
|
28
|
+
|
|
29
|
+
## States, resilience and mistakes
|
|
30
|
+
|
|
31
|
+
Demonstrate on/off/disabled and consumer saving/error recovery. Put pending/error/status beside the preference; never claim disabling saves the value. Keep labels and pseudo-element hit areas from overlapping neighbors, check visible thumb/track/focus in forced colors, and let long localized labels wrap. Custom classes must retain state distinction and focus.
|
|
32
|
+
|
|
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).
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Tabs
|
|
2
|
+
|
|
3
|
+
Use Tabs to switch related panels within one view. Use links for navigation to routes, Select/native radios for values, and DropdownMenu for actions. Tabs does not own routing, data fetching or draft storage.
|
|
4
|
+
|
|
5
|
+
## API and wrapper defaults
|
|
6
|
+
|
|
7
|
+
Exports: `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`. Each accepts `React.ComponentProps<typeof RadixTabs.Part>`, including element `ref`, `className`, `style`, ARIA and event handlers. There are no custom variants. Root inherits `value`/`defaultValue`/`onValueChange`, `orientation`, `dir`, `activationMode`. List inherits `loop`; Trigger requires a unique string `value` and accepts `disabled`; Content requires the matching `value` and accepts `forceMount`.
|
|
8
|
+
|
|
9
|
+
The wrappers set only slots/classes. Installed [Radix Tabs 1.1.13 declarations](https://unpkg.com/@radix-ui/react-tabs@1.1.13/dist/index.d.ts) and [runtime](https://unpkg.com/@radix-ui/react-tabs@1.1.13/dist/index.js) supply horizontal orientation, automatic activation, looping focus and enabled triggers by default. **Supply an initial `defaultValue` or controlled `value`**: omitting both leaves no panel selected until activation. `forceMount` is omitted by default: inactive panel containers are hidden and their children unmount. Draft state inside those children resets on remount; hoist state to preserve it.
|
|
10
|
+
|
|
11
|
+
`forceMount` retains children and makes inactive panels present too. Consumers must explicitly hide inactive content, for example `hidden={value !== 'draft'}`, so it cannot receive focus or appear as a second visible panel. Root has no group-wide `disabled` prop; disable individual triggers. Keep matching values, generated IDs/roles and focus handlers intact.
|
|
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/81fd395031dc5b20074c4d915a8585360ce2d28a/src/components/ui/tabs.tsx) is the API authority.
|
|
14
|
+
|
|
15
|
+
## Composition
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@naeil/ui/ui';
|
|
19
|
+
|
|
20
|
+
<Tabs defaultValue="overview">
|
|
21
|
+
<TabsList aria-label="Workspace details">
|
|
22
|
+
<TabsTrigger value="overview">Overview</TabsTrigger>
|
|
23
|
+
<TabsTrigger value="history">History</TabsTrigger>
|
|
24
|
+
</TabsList>
|
|
25
|
+
<TabsContent value="overview"><p>Workspace summary.</p></TabsContent>
|
|
26
|
+
<TabsContent value="history"><p>Activity history.</p></TabsContent>
|
|
27
|
+
</Tabs>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Run **UI / Tabs / Usage** and its Docs page for automatic, controlled manual/vertical, disabled, localized overflow and explicitly hidden retained panels. `activationMode="manual"` is useful when switching requires delayed loading; the consumer owns loading/error/retry and status announcements. Automatic activation is suitable when panels are immediately available.
|
|
31
|
+
|
|
32
|
+
## Keyboard, focus and accessible content
|
|
33
|
+
|
|
34
|
+
Name the tablist with `aria-label` or `aria-labelledby`. Radix supplies tab/tabpanel relationships and roving focus: horizontal arrows or vertical Up/Down move through enabled tabs; Home/End reach endpoints. Automatic mode activates on focus; manual mode activates with Enter/Space. `dir="rtl"` adjusts horizontal navigation. List `loop={false}` stops endpoint wrap. Tab leaves the list for the selected panel or its controls. Panels have `tabIndex=0` and a visible shared focus outline. Disabled triggers are skipped.
|
|
35
|
+
|
|
36
|
+
## States, resilience and mistakes
|
|
37
|
+
|
|
38
|
+
Selection uses a neutral surface and a visible bottom border; focus has a separate outline. The list scrolls locally on one keyboard axis; each trigger is capped to available list width and long labels wrap without shrinking 16px text. Triggers grow above the density minimum to fit enlarged/multiline text. Do not wrap the list itself into ambiguous keyboard rows or truncate every label to fit. Vertical lists need a consumer-chosen width suitable for their labels; use horizontal orientation on narrow screens when needed. Panels wrap long words; check their contents separately from the list.
|
|
39
|
+
|
|
40
|
+
Theme, root density, 40/36px trigger minimum heights and the mobile/coarse 44px minimum follow the existing system; wrapped or enlarged text increases height. Reduced motion removes color transitions. `className`/style and inherited props remain overrides; test state/focus/overflow after changes. Empty/error/loading panels are consumer content, not Tabs props. See [composition guidance](./composition.md) and the [current support matrix](../design/public-ui-support.md) for manual limits.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Textarea
|
|
2
|
+
|
|
3
|
+
Use Textarea for plain multiline text. Use Input for single-line values. Rich-text/code editors, autosizing, chat submission shortcuts and file/paste processing belong to the product.
|
|
4
|
+
|
|
5
|
+
## API and wrapper defaults
|
|
6
|
+
|
|
7
|
+
Export: `Textarea`. Accepts `React.ComponentProps<"textarea">`, including React 19 `ref`, `rows`, `cols`, `name`, `form`, `value`/`defaultValue`, `onChange`, `onKeyDown`, `onPaste`, `required`, `readOnly`, `disabled`, `minLength`/`maxLength`, `autoComplete`, `wrap`, ARIA, `className` and `style`.
|
|
8
|
+
|
|
9
|
+
The wrapper sets only slot/classes; **omitted `rows` retains the native two-row default**. It defaults to vertical resizing, full available width, 16px text, 25px line height and 8px vertical padding. Root density supplies the existing 40/36px minimum, raised to at least 44px on mobile/coarse pointers; rows and text determine the actual multiline height. It has no fixed control height or content autosizing. Consumer `rows`, resizing classes, padding and styles remain available. No label, error, value, validation or disabled default is invented.
|
|
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/81fd395031dc5b20074c4d915a8585360ce2d28a/src/components/ui/textarea.tsx) is the API authority. Native props are defined by the installed React 19 declarations and HTML textarea behavior.
|
|
12
|
+
|
|
13
|
+
## Composition
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { Textarea } from '@naeil/ui/ui';
|
|
17
|
+
|
|
18
|
+
<label htmlFor="notes">Workspace notes</label>
|
|
19
|
+
<Textarea id="notes" name="notes" rows={4} required
|
|
20
|
+
aria-invalid={invalid} aria-describedby="notes-help notes-error" />
|
|
21
|
+
<p id="notes-help">Describe the workspace. Enter starts a new line.</p>
|
|
22
|
+
<p id="notes-error" role="alert">{invalid ? 'Enter workspace notes.' : ''}</p>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The screen owns `invalid` and submission. Run **UI / Textarea / Usage** and its Docs page for native row/ref/form behavior, invalid recovery, disabled/read-only controls, localization and field/radio/Select/action composition. See the complete [field composition guide](./composition.md).
|
|
26
|
+
|
|
27
|
+
## Keyboard, focus and accessible content
|
|
28
|
+
|
|
29
|
+
Use a visible label and unique ID; placeholder is supplementary. Connect existing help/error IDs with `aria-describedby`; set `aria-invalid` when validation fails. On failed submit preserve the value and focus the invalid field; announce asynchronous outcomes with a status/alert region. Tab focuses the textarea; Enter inserts a newline. The wrapper adds no shortcuts and does not intercept composition events. A consumer chat handler must account for IME and modifier keys itself.
|
|
30
|
+
|
|
31
|
+
## States, resilience and mistakes
|
|
32
|
+
|
|
33
|
+
Support empty/placeholder, editable, invalid/recovery, read-only and disabled states. Read-only controls remain focusable and submit their name/value; disabled controls do neither. Native `required` and length constraints are available; server validation remains consumer-owned. Use either controlled `value` + handler or `defaultValue`, and do not switch modes.
|
|
34
|
+
|
|
35
|
+
Long text scrolls inside the native control and can be resized vertically; labels/help/errors wrap outside it. Check long Korean/English/Japanese, both themes and densities, mobile input size, focus/error boundaries and override classes. Normal and invalid boundaries use existing semantic tokens, reduced motion removes transitions, and shared font delivery stays consumer-owned. Do not lower essential mobile text or remove focus to imitate a specialized editor. [Support and manual acceptance](../design/public-ui-support.md) distinguish engine checks from Safari/screen-reader/device certification.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Demand-led component decision — 2026-10-02
|
|
2
|
+
|
|
3
|
+
This extends the approved shared system with **Tabs and Textarea only**. Form fields and native exclusive choices receive composition guidance. It adds no dependencies, product workflows, website redesign or RadioGroup/FormField API. Prepared package 0.3.0 remains unpublished.
|
|
4
|
+
|
|
5
|
+
## Evidence and boundaries
|
|
6
|
+
|
|
7
|
+
A read-only audit inspected reachable application manifests and UI source, traced imports to route render sites, and distinguished installed package imports from local primitives. Exact private source/call-site/hash provenance was retained privately and directly checked before scope acceptance. Public roles below describe that bounded local sample, not an ecosystem survey or promised migration. Worktrees/snapshots, unused wrapper files and menu-radio definitions were excluded from adoption counts. No consumer repository was edited or executed.
|
|
8
|
+
|
|
9
|
+
| Candidate | Actual observed demand | Decision / smallest reusable contract |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| Tabs | Four route-reachable panel compositions across three distinct products: a report detail view, a collaboration shell, dashboard detail, and usage windows. All four use locally copied Radix tabs, including the product separately installing @naeil/ui 0.1.0 for navigation/theme/locale compatibility imports. | Adopt Root/List/Trigger/Content wrappers forwarding Radix props/ref/classes. Shared panel relationships, keyboard focus and neutral states are absent from the existing 11 families. No package Tabs adoption is claimed. |
|
|
12
|
+
| Textarea | Repeated routed description/brief fields, composer and correction flows in one copied-UI product. A second product's native file editor corroborates multiline input, but is not another styled-wrapper adopter. | Adopt native textarea props/ref and shared focus/invalid/type/density styling. Input cannot replace multiline entry. Native HTML supplies editing/form behavior; the wrapper avoids repeated visual drift. Autosizing, chat shortcuts and editor behavior remain product-owned. |
|
|
13
|
+
| FormField | Labels repeat around different controls; help/error/validation lives in the consumer and existing repository examples. No common form-state/context API is used in the sample. | Use label/ID, described help/error and state composition; no new wrapper or form-state dependency. |
|
|
14
|
+
| RadioGroup | No application RadioGroup/native-radio use was observed. One routed product uses exclusive answer buttons, establishing candidate single-choice composition demand. Unused legacy choice/workbench code and action-menu radio exports are not additional adopters. | Document native fieldset/legend/same-name radios and compact Select. Defer a styled RadioGroup API until a broader shared contract needs it; this is not a claim that exclusive-choice demand is absent. |
|
|
15
|
+
|
|
16
|
+
## Contract and cost
|
|
17
|
+
|
|
18
|
+
[Tabs](../components/tabs.md) keeps installed Radix defaults: horizontal, automatic activation, looping focus, no invented initial value or forced mounting. [Textarea](../components/textarea.md) keeps native two-row and vertical-resize defaults and all native handlers/constraints. Existing semantic palette, Pretendard/Japanese companion, 16px text, density/touch minima, purpose-specific widths, short/reduced motion and class overrides remain the authority in [DESIGN.md](https://github.com/naeil-dev/naeil-ui/blob/81fd395031dc5b20074c4d915a8585360ce2d28a/DESIGN.md). No new numeric token is needed.
|
|
19
|
+
|
|
20
|
+
The runtime cost is thin wrappers and existing shared CSS; Radix Tabs already comes through the installed radix-ui dependency. Maintenance includes two exact deep entries, docs/stories, state/keyboard/form/override regressions and packed type/runtime checks. There is no new dependency, unused visual variant, form context or cross-product business contract. Revisit deferred candidates when repeated real usage needs more than native composition.
|
|
21
|
+
|
|
22
|
+
## Reproducible repository evidence
|
|
23
|
+
|
|
24
|
+
The external audit is a private local sample; these repository artifacts reproduce the accepted common contracts without exposing private applications:
|
|
25
|
+
|
|
26
|
+
- [Field/choice guide](../components/composition.md), source `src/stories/extension-examples.tsx`, standalone `src/components/ui/{tabs,textarea}.stories.tsx`, and the real packed example `examples/react/src/App.tsx`.
|
|
27
|
+
- Units: `pnpm exec vitest run src/lib/design/__tests__/extension.test.ts`.
|
|
28
|
+
- Rendered Docs/Usage and behavior: `pnpm build:storybook`, then `pnpm exec playwright test e2e/extension.spec.ts --project=chromium --project=webkit`.
|
|
29
|
+
- Distribution: `pnpm build:pkg`, `pnpm check:package` (actual isolated React and Next tarball installs).
|
|
30
|
+
- [Dated Stage 4 verification](public-ui-stage4-verification.md) records commands, source identities, actual results and limits. The [Stage 3 report](public-ui-stage3-verification.md) remains dated evidence of its original 11-family scope.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Final CI correction follow-up — 2026-10-02
|
|
2
|
+
|
|
3
|
+
This follow-up starts from `bcf99fca3467ae2b1cd29d54fa24f2569fb6001c` and covers the bounded test-harness and React-fixture corrections. Stage 4 independent review at that baseline does not approve these later changes. At the initial local freeze, the corrected source still required its own scoped review and Linux CI acceptance; the completed results at `9f90493abb4fb1186c948084026c3f4c04074013` are appended below. No publication or deployment was performed.
|
|
4
|
+
|
|
5
|
+
## Baseline and causal evidence
|
|
6
|
+
|
|
7
|
+
The actual [push run 36945564443](https://github.com/naeil-dev/naeil-ui/actions/runs/36945564443) passed package-and-site; its browser job recorded 207 passed, 4 skipped and 2 failed: Chromium workspace axe concurrency and WebKit Avatar Docs text ambiguity. The actual [PR run 36945943651](https://github.com/naeil-dev/naeil-ui/actions/runs/36945943651) passed package-and-site; its browser job recorded 208 passed, 4 skipped and 1 failed: the same Avatar Docs ambiguity. Firefox application tests passed in both Linux baseline runs. Neither overall run was green.
|
|
8
|
+
|
|
9
|
+
Root's separate local baseline verification at that SHA recorded 140 Chromium/WebKit passes and 2 expected WebKit forced-colors skips; actual built 13-family Docs/Usage/notices, the independent packed React/Next consumers and both-engine packed extensions, website compatibility build with dummy public configuration, 21 pinned HTTP targets returning 200, and both-engine `/naeil-ui/` Docs-to-Usage/font/network checks passed. Those are historical baseline results, not results for this correction patch.
|
|
10
|
+
|
|
11
|
+
The baseline focused rerun passed 18 cases; that alone did not reproduce the intermittent Linux race. A deterministic rendered probe waited for both the guide code and real Avatar group, then reproduced the original strict-mode assertion failure in both engines: the snippet's token and the group's screen-reader count both match `3 additional members`.
|
|
12
|
+
|
|
13
|
+
A separate diagnostic probe instrumented only the served axe module in temporary browser contexts. It preserved the real addon `axe.run` and invoked real `runPartial` immediately after that run started, reproducing the exact concurrent-scan exception in Chromium and WebKit. This is a diagnostic overlap probe, not an unmodified acceptance run. Storybook 10.4.6's actual URL parser uses dot notation for objects: the earlier `a11y:(manual:!true)` URLs left the rendered `a11y.manual` **false**. Those spellings did not establish manual scan ownership. With `a11y.manual:!true`, the actual global is **true**, the diagnostic records zero automatic addon runs, and controlled workspace axe scans finish with zero violations in both engines after finite transitions settle.
|
|
14
|
+
|
|
15
|
+
The React fixture's old `closeBundle` copied font notices relative to process cwd. The existing clean-consumer checker starts and closes the fixture's Vite development server in the repository process; that close hook wrote duplicate notices into package `dist/licenses`. A scratch caller/fixture probe reproduced both misplaced OFLs. The persistent regression gate loads a copied unchanged real fixture config/helper through the scratch-installed Vite, executes only the named font-notices plugin from another cwd, and asserts exact installed font notice bytes in fixture output with no caller output. It failed on the old configuration with `Closing the fixture must not create caller output`, then passed after correcting paths to Vite's resolved root/output directory. Scratch cleanup restores cwd and runs on failure. No real consumer or caller output is deleted to pass the test.
|
|
16
|
+
|
|
17
|
+
## Changes and verification
|
|
18
|
+
|
|
19
|
+
- Workspace, component Usage and extension test URLs use the verified `a11y.manual:!true` global. Storybook's default addon configuration remains unchanged, and every existing controlled axe scan remains in place.
|
|
20
|
+
- Avatar Docs separately checks the displayed group/count code, the actual named group's accessibility snapshot, and visual `+3` shorthand. Mobile Avatar snapshots, accessible counts and axe checks remain in place.
|
|
21
|
+
- The React fixture resolves both notice inputs and output from Vite's actual resolved configuration. Both original font OFLs remain delivered in standalone consumer output.
|
|
22
|
+
- `node --test scripts/consumer-font-notices.test.mjs` is an explicit package-and-site CI gate. It installs only versions already declared by the fixture for Vite, the Tailwind Vite plugin and the two font packages; transitive peers are installed by npm. It does not add root dependencies, install the application package, or use root's transitive Storybook Vite as a substitute. As with clean-consumer checks, a cold npm cache requires network; installation uses `--ignore-scripts --no-audit --no-fund` without a lockfile.
|
|
23
|
+
- This report is included in the tarball so maintained support/release links resolve in packed consumption. No other manifest contract changes.
|
|
24
|
+
|
|
25
|
+
Correction verification commands and results are recorded separately from the baseline:
|
|
26
|
+
|
|
27
|
+
| Command / probe | Result |
|
|
28
|
+
| --- | --- |
|
|
29
|
+
| Focused workspace light/dark axe + Avatar Docs, Chromium/WebKit, three repetitions | 18 passed |
|
|
30
|
+
| Full `pnpm test:browser --project=chromium --project=webkit` | 140 passed / 2 expected WebKit forced-colors skips |
|
|
31
|
+
| `node --test scripts/consumer-font-notices.test.mjs` | Old configuration red; corrected configuration 1 passed |
|
|
32
|
+
| `pnpm test` | 8 files / 41 tests passed |
|
|
33
|
+
| `pnpm check:types` | Passed |
|
|
34
|
+
| Scoped ESLint for the three specs, fixture config and regression gate | Passed |
|
|
35
|
+
| `pnpm lint` | No errors; existing unused `locale` website warning retained |
|
|
36
|
+
| `pnpm check:contrast` | All token contrast combinations passed |
|
|
37
|
+
| `pnpm build:pkg` + generated-token drift check | Passed; generated CSS unchanged |
|
|
38
|
+
| `pnpm check:package` | Packed boundary and clean React/Next consumers passed; both-engine packed extensions and exact consumer font notices passed |
|
|
39
|
+
|
|
40
|
+
Local verification uses macOS arm64, Node 22.23.2, Playwright 1.63.0, Chromium 153.0.8010.12 and WebKit 26.6, Storybook 10.4.6 and axe Playwright 4.13.0. A fresh installed Firefox launch still failed before the application with `Could not find profile folder`; local Firefox acceptance remains blocked. At this initial local freeze, corrected Linux CI results were pending root freeze and branch push. The subsequent exact-commit acceptance is recorded below without extending it to a future documentation commit.
|
|
41
|
+
|
|
42
|
+
## Artifact identity and limits
|
|
43
|
+
|
|
44
|
+
At baseline, an isolated package build/boundary check recorded **103 files / 412,826 unpacked bytes**. After the old clean-consumer development-server close hook, the package archive recorded **105 files / 421,559 unpacked bytes**, SHA-256 `ffcf3dbbcd3854f8214780ff307c842f3c2148eadfc70b724306e90920149736`, identical to the prior link-only archive. The extra files were exactly the duplicate `dist/licenses/Pretendard-OFL.txt` and `dist/licenses/Noto-Sans-JP-OFL.txt`; root-level notices existed in both. Storybook/site build order alone did not explain them. These baseline artifacts are retained, not relabeled as correction artifacts.
|
|
45
|
+
|
|
46
|
+
The initial corrected package was built fresh through the normal package build, then checked before and after clean consumers. Its archive retained root-level notices, omitted the accidental duplicate dist notices and included this new report plus maintained CI-state documentation. That historical correction archive had **104 files / 424,537 unpacked bytes**, SHA-256 `65daffadb65aa2635fdb48b9340642a22d4cf7ba1d0faaca10d96da43e2351ee`: the package build removed the two accidental duplicate files through its normal clean operation, and the approved manifest included one new verification report. This identity remains the original freeze evidence. Later prose changes produce a different archive checksum; their exact manifests and SHA-256 are frozen separately, without a circular self-checksum in the packed report.
|
|
47
|
+
|
|
48
|
+
Root inspected 94 baseline broad-suite axe attachments: zero violations, with incomplete rules **aria-hidden-focus, aria-valid-attr-value, bypass and color-contrast**. Dialog hidden backgrounds/focus guards and DropdownMenu submenu `aria-controls` occur in those incomplete records. They remain manual/diagnostic obligations; this report does not label every incomplete as verified or suppress rules. Stage 4's narrower extension result remains a separate scope. Independent extraction of the corrected HTML report inspected all **94** axe attachments: **zero violations**, with the same four incomplete rule IDs retained. Existing keyboard/focus tests passed, but those results do not resolve every incomplete. The separate workspace axe assertions also passed. The clean-consumer development StrictMode probe produced the same 34 Vite font allow-list warnings as the baseline; its pass covers overlay ownership/replay, while loaded-font claims come from the production consumer probes.
|
|
49
|
+
|
|
50
|
+
Automated snapshots, keyboard/focus tests, media emulation and axe do not certify screen-reader announcements, physical devices, native Safari, native zoom or Windows high contrast. No runtime/API/style/token/export/guide-base change was made. Earlier dated evidence and prior findings remain intact.
|
|
51
|
+
|
|
52
|
+
## Frozen implementation source identities
|
|
53
|
+
|
|
54
|
+
These SHA-256 identities identify the correction files tested locally on top of the baseline, not a future commit. The bounded source and artifact manifests are frozen separately for scoped review.
|
|
55
|
+
|
|
56
|
+
| File | SHA-256 |
|
|
57
|
+
| --- | --- |
|
|
58
|
+
| `e2e/ui-v2.spec.ts` | `2ab2032eea88a52c752f1ec153c217d3eb8f59e8d833e4f6564c6a382c233de8` |
|
|
59
|
+
| `e2e/component-usage.spec.ts` | `787784da3ec6c896944096a20d50c486fbc96ab1b6dce256bfd2a2dc55bf4fd4` |
|
|
60
|
+
| `e2e/extension.spec.ts` | `fbdb377fe6f64f2675370825beb228dda85e21f50cefe0703cac074a0543eb68` |
|
|
61
|
+
| `examples/react/vite.config.mjs` | `48ffb3e5052c6f4e47f8668842a61574ed09b856c38596b2212e253ea56e4ef4` |
|
|
62
|
+
| `scripts/consumer-font-notices.test.mjs` | `3f858428c734634b3524a14e42231f95717a4c41640d03478728de5f4eb8e0cf` |
|
|
63
|
+
| `.github/workflows/verify.yml` | `e9d0c393c3b7cd461ae374c9526b46ba7187bd8d70070bca19c8bc7fe7233590` |
|
|
64
|
+
|
|
65
|
+
## Post-freeze review and Linux acceptance — source `9f90493`
|
|
66
|
+
|
|
67
|
+
The exact correction commit `9f90493abb4fb1186c948084026c3f4c04074013` received independent scoped review approval with no actionable findings. The reviewer freshly checked 8 focused browser cases, the font-notice gate, types, scoped lint, clean packed React/Next consumers and both-engine packed extensions; inspected the actual red/manual-true probes, ten frozen source hashes, archive identity and all 94 local axe attachments. This approval covers the correction source at that commit. It does not approve publication or complete the manual platform checks.
|
|
68
|
+
|
|
69
|
+
Both actual Linux workflows completed successfully at that exact source: [push run 36948276767](https://github.com/naeil-dev/naeil-ui/actions/runs/36948276767) and [PR run 36948280881](https://github.com/naeil-dev/naeil-ui/actions/runs/36948280881). The PR browser job recorded **209 passed / 4 expected forced-colors skips** across Chromium, Firefox and WebKit: 71 Chromium passes, 69 Firefox passes and 69 WebKit passes, with two skipped forced-colors cases in each of Firefox/WebKit. Package-and-site passed, including 41 unit tests, 46 token contrast combinations, types, lint, generated-token drift, package/site builds, clean consumers and the font-notice gate. Optional helper regressions also passed: Node suites of 12 and 16 tests, and 8 Python tests. These results certify the tested source and configured environments, not every browser/peer version or any later commit. The unchanged local Firefox profile launch block remains a separate limitation.
|
|
70
|
+
|
|
71
|
+
The actual PR [Storybook artifact 11202888717](https://github.com/naeil-dev/naeil-ui/actions/runs/36948280881/artifacts/11202888717) was downloaded and checked: 838 files, all 13 Docs/Usage families, and exact repository/font notice bodies. Root served that downloaded artifact under `/naeil-ui/` and exercised Docs-to-Usage navigation and locally loaded Pretendard/Japanese fonts in Chromium and WebKit, with zero failed requests/errors. This is verification of the downloaded CI artifact, not hosted Pages publication. No Vercel deployment record exists for this source; main remains `d963dc5c1bbd49a1159d445edbb4c30309d96db9`.
|
|
72
|
+
|
|
73
|
+
This result append changes only this report, the maintained support matrix and release guidance. Packed boundary/relative-link checks, unchanged implementation/runtime hashes and exact notice-byte comparisons verify the documentation update; no browser, consumer or Storybook rebuild is needed solely for this prose. Its three replacement hashes and new archive identity are frozen in a separate external manifest. The original six implementation-source hash rows, failed baseline counts, red/green evidence and historical archive identities remain intact. Root will commit/push the documentation update for the latest-head automatic checks; no future-head CI pass is claimed here. [Live PR checks](https://github.com/naeil-dev/naeil-ui/pull/1/checks) show that later status. Main merge, npm publication, tags, Releases and hosted documentation remain separate authorization decisions.
|
|
74
|
+
|
|
75
|
+
## Forced-colors keyboard timing follow-up — 2026-10-02
|
|
76
|
+
|
|
77
|
+
At exact source `e171f4b03b4afe9d2b645ecd4c4aa52c242a1052`, [PR run 36950041237](https://github.com/naeil-dev/naeil-ui/actions/runs/36950041237) succeeded with **209 passed / 4 expected forced-colors skips**, while the identical-head [push run 36950036947](https://github.com/naeil-dev/naeil-ui/actions/runs/36950036947) failed with **208 passed / 4 expected skips / 1 Chromium forced-colors failure**. The failed trace and screenshot show History selected and outlined while the test expected Overview's outline. The original `9f90493` review, green results and six source hash rows above remain historical evidence for that source.
|
|
78
|
+
|
|
79
|
+
The causal diagnostics used real compiled Storybook assets in an isolated scratch server, without rebuilding or changing component behavior. The downloaded `9f90493` CI artifact was retained byte-for-byte for replay; the e171 trace's captured HTML/CSS/fonts matched it. The local iframe assets also matched; differences in project metadata and two manager bundles were outside this iframe replay. Installed Radix Tabs 1.1.13 delegates directional movement to Roving Focus 1.1.11, whose key handler schedules `focusFirst(candidateNodes)` with `setTimeout`. Tabs selects automatically on focus.
|
|
80
|
+
|
|
81
|
+
An ungated instrumented replay recorded both ArrowRight and ArrowLeft targeting Overview before either directional callback ran. With two enabled tabs, both callbacks computed History as their first candidate; the second callback found History already focused and returned. A deterministic diagnostic gate held only the real directional callbacks: the old sequence ended with History focused/selected and Overview's outline `none`, failing the original solid-outline requirement. Releasing ArrowRight's callback and verifying History focus/selection before ArrowLeft made the reverse key target History; releasing that callback returned Overview focus/selection with a solid outline, 2px selected border and nontransparent border color. These are causal diagnostics, not additional platform acceptance or a change to production scheduling.
|
|
82
|
+
|
|
83
|
+
Only the existing forced-colors test block now asserts initial Overview focus/selection, then History focus/selection after ArrowRight, then Overview focus/selection after the real ArrowLeft key. All original outline, selected-border width/color and invalid-Textarea outline/alert assertions remain. No arbitrary delay, retry, skip, direct selection or runtime/Storybook configuration change was added.
|
|
84
|
+
|
|
85
|
+
Fresh scoped verification on macOS arm64 with Node 22.23.2 and Playwright 1.63.0:
|
|
86
|
+
|
|
87
|
+
| Command / probe | Result |
|
|
88
|
+
| --- | --- |
|
|
89
|
+
| Diagnostic directional callback gate, old sequence versus state barrier | Old sequence red; state barrier green; zero page errors |
|
|
90
|
+
| `pnpm exec playwright test e2e/extension.spec.ts --project=chromium --grep 'Extensions forced-colors' --repeat-each=10` | 10 passed |
|
|
91
|
+
| `pnpm exec playwright test e2e/extension.spec.ts --project=chromium --project=webkit --grep 'Tabs automatic\|Tabs controlled'` | 4 passed |
|
|
92
|
+
| `pnpm check:types` and `pnpm exec eslint e2e/extension.spec.ts` | Passed |
|
|
93
|
+
| `git diff --check` | Passed |
|
|
94
|
+
| `pnpm check:package:boundary` and fresh `npm pack --ignore-scripts --json` comparison | Passed; packed relative links resolve; only this report's tar entry changed |
|
|
95
|
+
|
|
96
|
+
The package comparison retains every other tar entry and exact legal notice bytes, with separate before/after archive identities in the external freeze manifest. The two-file patch, source hashes, failed trace and key/focus timelines are preserved for independent focused review. No broad suite, clean-consumer or Storybook rebuild was repeated for this test/report correction. At this local two-file freeze, independent review and corrected exact-head Linux CI were pending; [live PR checks](https://github.com/naeil-dev/naeil-ui/pull/1/checks) show subsequent status, without claiming a future commit identity or CI approval here. Existing local Firefox and manual platform limitations remain unchanged.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Native environment and remaining manual validation — 2026-10-02
|
|
2
|
+
|
|
3
|
+
**Native/manual acceptance remains open.** This follow-up records actual local capability probes and their blockers at accepted checkout `79d9382f62166e99eeb26c785e690bf90cbb5379`. It does not change the shared UI, close prior accessibility findings, certify every supported platform, or make a publication decision. Existing automated results remain in [the final verification report](public-ui-final-verification.md); maintained support policy is in [the support matrix](public-ui-support.md).
|
|
4
|
+
|
|
5
|
+
## Actual environment and execution
|
|
6
|
+
|
|
7
|
+
| Probe | Observed result | Scope |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| `sw_vers` | macOS 27.0.1, build `26A434` | Actual local OS. |
|
|
10
|
+
| `/usr/bin/safaridriver --version` | Safari 27.0.1, build `22625.1.29.11.28` | Installed native SafariDriver; version alone is not application verification. |
|
|
11
|
+
| Owned SafariDriver `/status` | `ready: true` | Driver started on a newly allocated local port. Readiness did not establish a session. |
|
|
12
|
+
| Actual `POST /session` with `browserName: safari` | HTTP 500, `session not created`; exact message below | No usable Safari automation session and no application page/control assertions. |
|
|
13
|
+
| AppleEvents no-prompt preflight | Initially `-600` while Safari was absent; subsequently `-1744` | Initial result was application-not-running, not permission acceptance. Subsequent result is `errAEEventWouldRequireUserConsent`. No AppleScript event was sent to request consent. |
|
|
14
|
+
| `AXIsProcessTrusted()` without a prompt | `false` | The diagnostic process lacked Accessibility trust. Native keyboard/menu control and accessible-tree inspection were not established. |
|
|
15
|
+
| VoiceOver process check; VoiceOver Utility Info.plist | VoiceOver not running; Utility version 10, build 993 installed | VoiceOver was not enabled or executed. No speech/captions were observed, and no screen-reader usability pass is claimed. |
|
|
16
|
+
| Playwright Firefox default launch | Firefox 155.0, build `15526.9.2`; process exit 1, `Could not find profile folder` | Failed before loading the application. |
|
|
17
|
+
| One Firefox follow-up with an existing new canonical temporary profile | Same exit 1 and `Could not find profile folder` | Explicit isolated persistent profile did not resolve the failure. Profile removed; no reinstall, global preference change or further blind retry. |
|
|
18
|
+
| Existing Storybook served on an owned random loopback port | `index.html`, `iframe.html`, `index.json` and one asset from each bundled font family returned HTTP 200 with bytes identical to disk | Artifact delivery only. Neither rendered font selection nor Safari layout was observed. |
|
|
19
|
+
|
|
20
|
+
SafariDriver's exact session error:
|
|
21
|
+
|
|
22
|
+
> Could not create a session: You must enable 'Allow remote automation' in the Developer section of Safari Settings to control Safari via WebDriver.
|
|
23
|
+
|
|
24
|
+
No global preference, TCC setting, VoiceOver state or existing browser profile was changed. The failed SafariDriver session attempt nevertheless launched Safari, which had not been running initially. It returned no session/window identifier. Existing or restored windows were not inspected, navigated or closed, and no global Safari quit/kill was issued. The owned driver and local artifact server were stopped. An operator's settings confirmation alone must not be promoted to acceptance: a future retry must obtain an actual successful `/session` response before testing pages.
|
|
25
|
+
|
|
26
|
+
## Artifact and source identity
|
|
27
|
+
|
|
28
|
+
This run reused the existing local `storybook-static` build without rebuilding or editing it. Its full sorted relative-path SHA-256 manifest contains **838 files**, and the manifest text has SHA-256 `0f996659cf9892376a897e9fb389732274c27cb0fa0def75e6baf86169015614`. Each line is `<file SHA-256><two spaces><relative POSIX path><newline>`, sorted by path. This identifies the reused artifact; it does not independently prove that it is a downloaded CI artifact or establish its build provenance.
|
|
29
|
+
|
|
30
|
+
| File | SHA-256 |
|
|
31
|
+
| --- | --- |
|
|
32
|
+
| `storybook-static/index.html` | `f351829970d55bcb8799daecbc2dcf96ce55faa8950f7c115273ae0faf2b1176` |
|
|
33
|
+
| `storybook-static/iframe.html` | `db62cb99d67cc6e87ce6cc3d3e5de3c366ad9d16498cd165df7012fb77971ef6` |
|
|
34
|
+
| `storybook-static/index.json` | `b4543eb1f0cbcb643ead1ee59b1c06de9271e38b8154cf3d62ac698796264993` |
|
|
35
|
+
| `e2e/ui-v2.spec.ts` | `2ab2032eea88a52c752f1ec153c217d3eb8f59e8d833e4f6564c6a382c233de8` |
|
|
36
|
+
| `e2e/extension.spec.ts` | `83f90028ca04d8fade46b6261e6363916f4aad09f44113106ed6a5ecdb7b54b4` |
|
|
37
|
+
| `src/styles/theme.css` | `210ce5a83f61eb1f856f405d2a25b1a2e1be835329c2b251221e4d052ba4fece` |
|
|
38
|
+
|
|
39
|
+
The served index contains `ui-v2--workspace`, `ui-tabs--usage` and `ui-textarea--usage`, pointing to their real repository stories. Raw capability responses, launch logs, served-byte comparisons and the full artifact manifest were retained outside the repository. No Safari screenshot, Safari DOM probe, native AX snapshot or observed screen-reader output was produced. Earlier Playwright WebKit results are separate engine evidence; they are not a substitute for native Safari. Earlier Linux Firefox application passes remain valid for their recorded source/run, while this local launch blocker remains unresolved.
|
|
40
|
+
|
|
41
|
+
## Reproduction and native checks still required
|
|
42
|
+
|
|
43
|
+
For a manual run, serve the unchanged build from the repository root on a newly allocated port:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
python3 -m http.server 0 --bind 127.0.0.1 --directory storybook-static
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Read the actual port from the server output. Use a new window owned by the tester and record its identity; preserve other windows and the user's theme, zoom, keyboard-navigation and assistive-technology settings. Close only the owned window and stop only the owned server/driver. Do not enable automation or change system settings as a side effect of a test script. The exact Safari settings blocker above must be resolved by the operator before automated native checks can run.
|
|
50
|
+
|
|
51
|
+
The session request used in this run was:
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{"capabilities":{"alwaysMatch":{"browserName":"safari"}}}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
It was sent to a locally launched `/usr/bin/safaridriver -p <unused-port>` at `POST http://127.0.0.1:<unused-port>/session`. Record the actual response and browser capabilities. A successful session may support WebDriver keyboard actions and programmatic DOM assertions; label those as automated native Safari checks. They do not establish human keyboard or screen-reader usability, and browser UI page zoom needs separate evidence.
|
|
58
|
+
|
|
59
|
+
Open `/iframe.html?id=ui-v2--workspace&viewMode=story&globals=a11y.manual:!true`, and family stories using `/iframe.html?id=ui-<family>--usage&viewMode=story&globals=theme:light;density:comfortable;a11y.manual:!true`. Repeat with `theme:dark` and `density:compact` as appropriate. The verified dot notation `a11y.manual:!true` avoids concurrent automatic addon scans when a controlled axe scan is actually used. It does not disable or replace human checks.
|
|
60
|
+
|
|
61
|
+
The expected behavior below comes from the actual `e2e/ui-v2.spec.ts`, `e2e/component-usage.spec.ts` and `e2e/extension.spec.ts`, rather than inferred component names. **All rows remain unrun in native Safari in this follow-up.**
|
|
62
|
+
|
|
63
|
+
| Check | Reproduction and expected result |
|
|
64
|
+
| --- | --- |
|
|
65
|
+
| Dialog (`ui-dialog--usage`) | Focus “Edit workspace”, Enter: focus “Workspace name”. Tab and Shift+Tab remain inside the modal. Empty save keeps focus and shows invalid state; long Korean/English/Japanese input survives recovery. Save completion and Escape return focus to the trigger. Confirm visible focus and scrollable content. |
|
|
66
|
+
| Select (`ui-select--usage`; workspace language input) | Enter opens value options; type `e` focuses English in the family story; Enter selects it and restores trigger focus. Submit reports `en`. Loading disables the trigger; empty state exposes retry. In the workspace, End focuses the final Japanese option, ArrowUp focuses English; Escape closes without changing the selected value. Check disabled-option skipping and popup scrolling. |
|
|
67
|
+
| DropdownMenu (`ui-dropdownmenu--usage`) | ArrowDown opens “Workspace actions” on “Copy reference”. Next eligible item is mixed “Show completed”; Enter changes it to checked and restores trigger focus. Radio action updates sort text. ArrowRight on “More actions” enters “Export details”; activation reports completion. Escape restores trigger focus. |
|
|
68
|
+
| Tabs (`ui-tabs--usage`) | Automatic horizontal arrows skip “Unavailable”, activate History and loop; Home/End work. Temporary panel draft resets after unmount. Vertical manual tabs move focus with arrows but change selection only on Enter/Space; disabled looping stops at the end. Retained hidden draft remains intact. Localized long tab labels scroll inside their tablist with the focused last tab visible. |
|
|
69
|
+
| Textarea (`ui-textarea--usage`) | Empty save focuses “Workspace notes”, sets invalid and displays the persistent error. Enter Korean/English/Japanese and a newline; editing clears invalid state without losing content. Confirm native two/four rows and resize policy. Native radio arrows change delivery; labels activate their radio. Save shows busy state then “Saved digest / en. (Example)”. |
|
|
70
|
+
| Light/dark, density and long content | Compare matching window dimensions and content. Use all three languages, long labels and multiline errors. Check focus and field boundaries, disabled state, overlays, persistent errors/retry, and text wrapping. Keep application frame and purpose-specific content widths separate. |
|
|
71
|
+
| Narrow window / mobile layout | In a desktop Safari narrow window record actual `innerWidth`, minimum supported window size, overflow and overlay scrolling. A desktop narrow window or responsive emulator is not a physical mobile pass. Physical-device checks remain below. |
|
|
72
|
+
| Native 200% and 400% page zoom | Use Safari's native page-zoom control in the owned window. Record the browser UI percentage and baseline/zoomed `innerWidth`, screenshots, long-dialog scrollability, labels and persistent recovery actions. Check the 320 CSS-pixel equivalent when attainable. CSS `zoom`, root font-size changes and device/media emulation do not meet this row. Restore owned-window zoom. |
|
|
73
|
+
|
|
74
|
+
Native controls can depend on the user's keyboard-navigation setting. Record it and actual key sequences; do not silently change a global setting to make a test pass. Screenshot inspection, DOM measurements, WebDriver focus assertions and AX snapshots should each be labeled with their actual method.
|
|
75
|
+
|
|
76
|
+
## Remaining human/platform acceptance
|
|
77
|
+
|
|
78
|
+
| Environment / task | Current status and evidence to collect |
|
|
79
|
+
| --- | --- |
|
|
80
|
+
| Safari 27.0.1 with VoiceOver | Blocked here by absent AppleEvents consent/Accessibility trust and no safe observed-output path. With an operator, preserve/restore VoiceOver state; observe actual speech or real captions for labels/help/errors, switch checked, checkbox mixed, selected language, dialog title/focus return, selected tabs/panels, textarea/radio values and toast outcomes once. Axe, AX snapshots and `say` are not screen-reader acceptance. |
|
|
81
|
+
| Windows with NVDA and actual high contrast | No Windows environment was exercised. Record Windows/browser/NVDA versions; hear the same state transitions and check real high-contrast text, field boundaries, focus, switch/checkbox indicators, selected tab and invalid textarea in enabled/disabled states. Chromium forced-colors emulation does not close this row. |
|
|
82
|
+
| Physical iOS Safari / Android Chrome | No physical device was exercised. Record hardware/OS/browser, portrait/landscape, software keyboard opening, native input auto-zoom, 44px targets without adjacent overlap and scrollable overlays. Desktop/mobile emulation does not close this row. |
|
|
83
|
+
| OS text enlargement, font/network failure and product-specific flows | Actual OS text enlargement and human fallback/readability checks remain open. Test long real data, slow/offline font loading and persistent error/empty/retry states in the consuming app. HTTP font delivery above does not prove loaded-face rendering or fallback usability. |
|
|
84
|
+
| Consumer keyboard overrides and prior axe incompletes | Recheck consumer class/focus/outside-handler overrides, nested Dialog/Select, removed/triggerless focus return and logical Tab order. Preserve prior `aria-hidden-focus`, `aria-valid-attr-value`, `bypass` and `color-contrast` incomplete findings until the affected states are reviewed; a launch diagnostic closes none of them. |
|
|
85
|
+
|
|
86
|
+
Record source/artifact identity, tester, date, actual OS/browser/AT version, method, expected/observed behavior, screenshot or observed-output evidence and remaining findings for each completed row. This report adds no native component pass and leaves all human/platform acceptance rows open.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Public UI usage verification — 2026-10-02
|
|
2
|
+
|
|
3
|
+
The original Stage 3 implementation was verified locally against the original source hashes below, based on frozen Stage 2 `8bbbdf62d9a3cc6794d2c5fcf7cfafeca034dbb8`, and frozen at `80732074349b35d41e31b674f248171101481742`. Independent review found one P3 Avatar example accessibility issue; the focused correction and its replacement hashes are recorded separately below, pending an independent scoped recheck. This evidence does not authorize publication. No commit, push, package publication, workflow dispatch or site deployment was performed by either executor.
|
|
4
|
+
|
|
5
|
+
Authority: DESIGN.md, the shared v2 specification/migration and actual wrappers/installed primitive declarations. Scope: maintained guides and runnable Docs/Usage for all 11 existing families, accessibility/resilience examples, documented public support, independently packed consumption, preview asset/notices boundary and manual-only deployment preparation. The public export allowlist, approved palette/fonts, numeric foundations and product/site scope remain unchanged.
|
|
6
|
+
|
|
7
|
+
## Original frozen broad verification
|
|
8
|
+
|
|
9
|
+
Local execution used Node 22.23.2, pnpm 10.30.3 and Playwright 1.63.0 on macOS. Installed browser metadata identifies Chromium 153.0.8010.12, WebKit 26.6 and Firefox 155.0.
|
|
10
|
+
|
|
11
|
+
| Command / check | Observed result |
|
|
12
|
+
| --- | --- |
|
|
13
|
+
| `pnpm test` | 7 suites, **37/37 passed**. |
|
|
14
|
+
| `pnpm check:types` | Passed. |
|
|
15
|
+
| `pnpm lint` | 0 errors; one pre-existing unused `locale` warning in the website blog route. Generated preview/test reports are excluded from lint. |
|
|
16
|
+
| `pnpm build:tokens` then `git diff --exit-code -- src/styles/theme.css` | Passed; generated CSS has no drift. No generated CSS was hand-edited. |
|
|
17
|
+
| `pnpm check:contrast` | **40/40 semantic pairs passed** across light/dark. |
|
|
18
|
+
| `pnpm build:pkg` | Bundled/deep builds and declarations passed, including the internal-only helper entry. |
|
|
19
|
+
| `pnpm check:package` | Packed boundary and clean independently installed React + Next consumers passed. Finalized guide/report links and payload also passed `pnpm check:package:boundary`. |
|
|
20
|
+
| Independent React browser | 390px light/dark: actual local Pretendard Korean/Latin + Noto Sans JP faces loaded; no page overflow; native required-field rejection, preserved localized input and save recovery passed. Deliberately blocked fonts remained unavailable while fallback form stayed operable. **47 actual consumer-code package notices** and exact consumer-owned font OFLs passed. |
|
|
21
|
+
| Mixed public imports | Bundled `/ui` and deep menu imports passed overlapping inert ownership in both close orders, forced removal, existing inert preservation, object refs and focus restoration. Independently installed Vite development mode confirmed **two StrictMode effect setups**, then reran the same checks; production mode also passed. |
|
|
22
|
+
| `pnpm build:storybook` | 11 actual built Docs entries + 11 runnable Usage entries; exact repo/shadcn/OFL copies and bundled fonts; no website images/SVG/HTML copied. **74 actual Vite preview package notices + 254 conservative manager package-version notice entries** passed. |
|
|
23
|
+
| `python3 scripts/refresh-storybook-notices.py` | Opt-in source/npm refresh actually completed: 254 complete entries. Python syntax check also passed. Ordinary builds use checked-in notice bodies offline. |
|
|
24
|
+
| `pnpm exec playwright test --project=chromium --project=webkit` | **111 passed, 1 expected skip, 0 failures (1.7m)**. The skip is WebKit forced-colors emulation; Chromium's control/state/focus probe passed. Both projects rendered all 11 actual manager Docs pages. |
|
|
25
|
+
| Single Firefox Docs test | Failed before application launch: `Could not find profile folder`. This is an installed-browser environment block, not a passing Firefox application result. Firefox stays configured in CI; no remote CI pass is claimed. |
|
|
26
|
+
| `git diff --check` | Passed. |
|
|
27
|
+
|
|
28
|
+
Browser coverage includes default/compact/light/dark, 320px localized open overlays, keyboard/modal traps and trigger restoration, disabled/mixed/radio/menu/submenu states, real submitted values, invalid/loading/error/retry forms and toast loading/action/keyboard behavior. Axe runs use WCAG 2/2.1/2.2 A/AA tags on whole documents including opened overlays, with full violations/incomplete attachments. No rule is suppressed or threshold reduced. The addon is set to manual in external-axe test URLs to avoid concurrent axe instances; the external scan still runs normally. Finite theme/open animations settle before scans. Incomplete findings are preserved for manual review.
|
|
29
|
+
|
|
30
|
+
## Reproductions and repairs
|
|
31
|
+
|
|
32
|
+
Chromium forced-colors emulation reproduced an invisible Switch: both track and thumb became Canvas white. Shared CSS now uses system-color boundaries/track/thumb states and retains visible focus; no numeric token or normal-theme palette change was made.
|
|
33
|
+
|
|
34
|
+
Radix-managed hidden Select/menu background branches remained programmatically focusable and produced whole-document `aria-hidden-focus` findings. The internal helper follows Radix's `data-aria-hidden` ownership, adds inert, and restores each element's original inert state. A document-owned symbol registry coordinates bundled/deep/multiple module instances. Default modal, nonmodal, nested Dialog/Select, forced unmount, callback-ref cleanup, object refs and consumer handlers were checked. This remains the main global-interaction review surface; arbitrary consumer focus/outside overrides and third-party overlay combinations require their own acceptance checks.
|
|
35
|
+
|
|
36
|
+
Consumer examples corrected 320px/200% root-text wrapping and used percentage-based dialog max-height for the separate 200% CSS-zoom probe. These probes are not native desktop browser zoom. macOS WebKit's default navigation requires Option+Shift+Tab to include buttons; the toast test uses that setting and verifies actual document/trigger focus return. No Sonner component repair was needed.
|
|
37
|
+
|
|
38
|
+
Earlier local manager loads intermittently failed with `ERR_CONNECTION_RESET`/`ERR_SOCKET_NOT_CONNECTED` from Python's local static server, leaving a spinner/unstyled state. The verification/local-preview route now uses a persistent Node static server. The fresh complete suite passed through it; earlier failed runs are diagnostic history rather than final validation.
|
|
39
|
+
|
|
40
|
+
## Artifact and image review
|
|
41
|
+
|
|
42
|
+
The local reproducible preview is `storybook-static/`, served with `pnpm preview:storybook`; CI is configured to upload `storybook-preview` and browser results, but no hosted artifact or remote passing execution is claimed. Local Storybook results/HTML report, Firefox launch log, packed-consumer captures, command logs and a built manager Docs screenshot were retained privately; they are not npm payload or checked-in evidence. This checked-in dated command/source record and its reproducible commands identify the verified scope.
|
|
43
|
+
|
|
44
|
+
Actually opened and visually inspected final captures: Chromium light 2560px/default workspace; WebKit dark 2560px/default workspace; WebKit light and Chromium dark 390px/compact workspace; WebKit light and Chromium dark mobile open dialogs; Chromium forced-colors Switch; packed React light/dark/font-fallback pages; and the actual built Button manager Docs page. The inspected captures show bounded content, wrapped multilingual text, stacked mobile panels, visible overlay recovery/focus and distinguishable forced-color Switch states. Screenshot inspection does not certify every glyph, viewport or screen-reader behavior. Matching engine/theme/mobile captures for the remaining combinations are retained, but are not claimed as separately visually inspected.
|
|
45
|
+
|
|
46
|
+
Preview notices preserve complete installed/original LICENSE/NOTICE bodies, including Lucide's mixed ISC/Feather MIT text. Source-less use-composed-ref@1.4.0 has exact MIT metadata plus full declared terms, explicitly without an invented copyright holder/year. Four manager vendored modules lack exact embedded versions; their conservative original npm notice sources are identified without an exact-version claim. Next-vendored manifests omit versions and are similarly labeled. These concrete inventory/source limitations remain visible in the notice JSON and [source ledger](https://github.com/naeil-dev/naeil-ui/blob/main/docs/design/frontend-sources.md); this is artifact-level notice preservation, not blanket provenance or legal certification. Unknown website-art rights remain unresolved, with those assets excluded.
|
|
47
|
+
|
|
48
|
+
## Unrun / blocked acceptance
|
|
49
|
+
|
|
50
|
+
Screen readers, actual Safari, native desktop 200%/400% zoom, actual Windows high contrast, physical iOS/Android devices, all peer-version combinations, deployed behavior and arbitrary consumer overrides were not tested. Local Firefox application checks are blocked as above. Use the [support matrix and manual acceptance checklist](public-ui-support.md) before a release. The manual-only Pages workflow/recipe is prepared, unexecuted, and still requires future explicit publication authorization and protected-environment setup.
|
|
51
|
+
|
|
52
|
+
## Original frozen source identity
|
|
53
|
+
|
|
54
|
+
SHA-256 identifies the scope of the original broad run frozen at `80732074349b35d41e31b674f248171101481742`. These historical hashes remain attached to that run; only the affected files have replacement hashes in the focused follow-up below. Orchestration-plan edits are outside executor ownership.
|
|
55
|
+
|
|
56
|
+
| File | SHA-256 |
|
|
57
|
+
| --- | --- |
|
|
58
|
+
| `src/lib/design/modal-inert.ts` | `4cb6e1bab2d651d1724c994a2eb03aa2211281c3e2a2f9525a96d31fb152815c` |
|
|
59
|
+
| `src/components/ui/select.tsx` | `27aa623255a7992f044a3882ba71450e75942b2d3062daee7b99476a653ed32e` |
|
|
60
|
+
| `src/components/ui/dropdown-menu.tsx` | `b9f398c3bff1aa4c7fca7eed16ea4e9a10f7e933d0c67015dda0ccaec13fac02` |
|
|
61
|
+
| `src/styles/components.css` | `5bd58b7a6ada0945e9457c9b642dcb4f25283c43d1d341130681b4310c847d1d` |
|
|
62
|
+
| `src/stories/component-examples.tsx` | `5a8c33b840047c91f3669a4d1c30c41d9ff3ac363d3b885b1026bc04041f7dd5` |
|
|
63
|
+
| `e2e/component-usage.spec.ts` | `90204b7035c5cfb04be5931d026b43a56a0ca265329a22ce476f09958502e980` |
|
|
64
|
+
| `playwright.config.ts` | `6621ef61f8b47b90716ae5848cb9fc75458f6176de18a7bce12dd9c5b0a7a28b` |
|
|
65
|
+
| `scripts/serve-storybook.ts` | `190d65a8dae7eec2dedfdf574198ea2d603f92ca4a023bfb50b91da0df5a2370` |
|
|
66
|
+
| `scripts/bundled-notices.ts` | `6f0514cb7c818c8246f9efdbb339c80b22f0501dfe8754337e90f4cc6d484ddf` |
|
|
67
|
+
| `.storybook/manager-notices.json` | `8e13782901d7c31845d313861cb3251dd90e47b38a995be82d67f51daad34f8c` |
|
|
68
|
+
| `scripts/check-consumer-browser.ts` | `09144d0bac7b91a1e0524cbb4f18a475d403008ff79cf8563aa2b46716bdb838` |
|
|
69
|
+
| `examples/react/src/App.tsx` | `865fbabe907ddad39f22724f744de582075f88e1acdb71c6fd39db5901dfa2c1` |
|
|
70
|
+
| `package.json` | `6d327bae94fae383eb37c3c0e9c6f0b8e06d4291b3c77075ed509820ed925665` |
|
|
71
|
+
| `pnpm-lock.yaml` | `807b43593d1362afe9278091a5220dc146ecfb46b210f45fb2a68ddb0f56e65c` |
|
|
72
|
+
| `.storybook/main.ts` | `162591a47e0b2095be02d24ffb4c13e9be33e5c36e455389c992d7f08ae1ea74` |
|
|
73
|
+
|
|
74
|
+
## Focused P3 Avatar follow-up — 2026-10-02
|
|
75
|
+
|
|
76
|
+
The named generic AvatarGroup lacked a supported role, and AvatarGroupCount's generic-div `aria-label` did not convey the additional-member count. The consumer-owned Usage example now supplies `role="group"` and keeps the visual `+3` in an `aria-hidden` span with actual `sr-only` text, `3 additional members`. The Avatar guide documents both compositions. No package component runtime, API, style, token or override changed.
|
|
77
|
+
|
|
78
|
+
The existing Avatar mobile checks now require the exact group accessibility snapshot, including both named fallback images and the additional-member text; they also check the loaded image, failed-image fallback and visual count. Full axe violations/incomplete attachments remain unsuppressed, and Avatar checks additionally reject any `aria-prohibited-attr` incomplete result. The existing actual Docs checks verify that the rebuilt guide includes the correction.
|
|
79
|
+
|
|
80
|
+
| Focused command / check | Observed result |
|
|
81
|
+
| --- | --- |
|
|
82
|
+
| `pnpm build:storybook` | Passed: 11 built Docs/Usage entries, exact notices/fonts and asset boundary; 74 preview + 254 manager notice entries. |
|
|
83
|
+
| `pnpm exec playwright test e2e/component-usage.spec.ts --grep avatar --project=chromium --project=webkit` | **6/6 passed (5.6s)**: actual manager Avatar Docs in both engines; 320px light/dark compact Usage in both engines, including loaded/failed/missing image content and localized reflow. |
|
|
84
|
+
| Actual snapshot / axe inspection | All four Usage snapshots expose group `Team members`, images `Alex Lee`/`田中 遥`, and text `3 additional members`, with no accessible `+3` duplicate. All four scans have **0 violations** and **no `aria-prohibited-attr` incomplete**. The sole remaining incomplete rule is `bypass` on the standalone story document's `html` (no heading/landmark/skip link); retained for manual review, outside this group/count correction. |
|
|
85
|
+
| Actual visual inspection | Opened rebuilt Avatar manager Docs through Aside REPL; inspected its capture and a Chromium capture of the corrected guide section, plus all four Chromium/WebKit light/dark 320px Usage captures. Circular graphics, overlapping group, visual `+3` and localized wrapping remain intact; accessible count text is visually hidden. |
|
|
86
|
+
| `pnpm check:types`, `pnpm lint`, `pnpm check:contrast` | Passed; lint retains only the pre-existing website unused-`locale` warning; **40/40** semantic contrast pairs pass. |
|
|
87
|
+
| Final `pnpm check:package:boundary` and packed Avatar guide inspection | Passed: the freshly packed consumer-facing guide matches repository bytes and includes the named-group role and actual hidden count text; document links and package boundary pass. |
|
|
88
|
+
| `git diff --check` | Passed. |
|
|
89
|
+
|
|
90
|
+
Focused command logs, browser HTML report with full attachments, extracted axe/snapshot attachments, four Usage screenshots, actual manager Docs snapshots/captures, packed tarball/content inspection and replacement source hashes were retained privately, separately from the original broad artifacts. This checked-in dated command/source record and the reproducible commands in this record describe that correction. The original **111 passed / 1 expected skip** browser result, unit/package-runtime/clean-consumer results and manual/environment limitations are historical frozen evidence; the full browser suite, package runtime build and clean React/Next consumer browser checks were not rerun for this example/packed-guide-only correction. No new screen-reader, Safari or Firefox pass is claimed.
|
|
91
|
+
|
|
92
|
+
The following SHA-256 values supersede only these affected files for the focused follow-up. Source is frozen for the independent scoped recheck; the orchestrator owns the orchestration record and any later freeze commit.
|
|
93
|
+
|
|
94
|
+
| File | Focused follow-up SHA-256 |
|
|
95
|
+
| --- | --- |
|
|
96
|
+
| `src/stories/component-examples.tsx` | `4386e8ca9b7cf58da083258568bb97427bf331c237d52396399202081f1eb04a` |
|
|
97
|
+
| `e2e/component-usage.spec.ts` | `ed9f67d33283e07d3c3698a6035ae21a1598cbccaabc7466c0dfb9eddfb75a99` |
|
|
98
|
+
| `docs/components/avatar.md` | `5be6ff754f98336a6a5ace144ed7d757ec92929eaf38d0af42c686f102041231` |
|