@hawiyah/react 0.0.0-stage → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/LICENSE +73 -0
  2. package/NOTICE +19 -0
  3. package/PORTAL.md +74 -0
  4. package/README.md +461 -2
  5. package/dist/calendar.d.ts +52 -0
  6. package/dist/calendar.d.ts.map +1 -0
  7. package/dist/calendar.js +148 -0
  8. package/dist/calendar.js.map +1 -0
  9. package/dist/content-navigation.d.ts +15 -0
  10. package/dist/content-navigation.d.ts.map +1 -0
  11. package/dist/content-navigation.js +29 -0
  12. package/dist/content-navigation.js.map +1 -0
  13. package/dist/display.d.ts +421 -0
  14. package/dist/display.d.ts.map +1 -0
  15. package/dist/display.js +208 -0
  16. package/dist/display.js.map +1 -0
  17. package/dist/expanded-content.d.ts +217 -0
  18. package/dist/expanded-content.d.ts.map +1 -0
  19. package/dist/expanded-content.js +124 -0
  20. package/dist/expanded-content.js.map +1 -0
  21. package/dist/expanded-controls.d.ts +107 -0
  22. package/dist/expanded-controls.d.ts.map +1 -0
  23. package/dist/expanded-controls.js +142 -0
  24. package/dist/expanded-controls.js.map +1 -0
  25. package/dist/expanded-interactive.d.ts +116 -0
  26. package/dist/expanded-interactive.d.ts.map +1 -0
  27. package/dist/expanded-interactive.js +204 -0
  28. package/dist/expanded-interactive.js.map +1 -0
  29. package/dist/expanded-layout.d.ts +80 -0
  30. package/dist/expanded-layout.d.ts.map +1 -0
  31. package/dist/expanded-layout.js +26 -0
  32. package/dist/expanded-layout.js.map +1 -0
  33. package/dist/expanded-shared.d.ts +3 -0
  34. package/dist/expanded-shared.d.ts.map +1 -0
  35. package/dist/expanded-shared.js +14 -0
  36. package/dist/expanded-shared.js.map +1 -0
  37. package/dist/file-upload-list.d.ts +10 -0
  38. package/dist/file-upload-list.d.ts.map +1 -0
  39. package/dist/file-upload-list.js +7 -0
  40. package/dist/file-upload-list.js.map +1 -0
  41. package/dist/forms.d.ts +34 -0
  42. package/dist/forms.d.ts.map +1 -0
  43. package/dist/forms.js +65 -0
  44. package/dist/forms.js.map +1 -0
  45. package/dist/index.d.ts +20 -0
  46. package/dist/index.d.ts.map +1 -0
  47. package/dist/index.js +17 -0
  48. package/dist/index.js.map +1 -0
  49. package/dist/interactive.d.ts +60 -0
  50. package/dist/interactive.d.ts.map +1 -0
  51. package/dist/interactive.js +321 -0
  52. package/dist/interactive.js.map +1 -0
  53. package/dist/mobile-top-bar.d.ts +14 -0
  54. package/dist/mobile-top-bar.d.ts.map +1 -0
  55. package/dist/mobile-top-bar.js +8 -0
  56. package/dist/mobile-top-bar.js.map +1 -0
  57. package/dist/navigation-drawer.d.ts +20 -0
  58. package/dist/navigation-drawer.d.ts.map +1 -0
  59. package/dist/navigation-drawer.js +37 -0
  60. package/dist/navigation-drawer.js.map +1 -0
  61. package/dist/portal.d.ts +27 -0
  62. package/dist/portal.d.ts.map +1 -0
  63. package/dist/portal.js +16 -0
  64. package/dist/portal.js.map +1 -0
  65. package/dist/search-field.d.ts +15 -0
  66. package/dist/search-field.d.ts.map +1 -0
  67. package/dist/search-field.js +113 -0
  68. package/dist/search-field.js.map +1 -0
  69. package/dist/shared.d.ts +35 -0
  70. package/dist/shared.d.ts.map +1 -0
  71. package/dist/shared.js +78 -0
  72. package/dist/shared.js.map +1 -0
  73. package/dist/site-verification.d.ts +10 -0
  74. package/dist/site-verification.d.ts.map +1 -0
  75. package/dist/site-verification.js +16 -0
  76. package/dist/site-verification.js.map +1 -0
  77. package/package.json +54 -3
package/LICENSE ADDED
@@ -0,0 +1,73 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
10
+
11
+ "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
12
+
13
+ "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
14
+
15
+ "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
16
+
17
+ "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
18
+
19
+ "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
20
+
21
+ "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
22
+
23
+ "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
24
+
25
+ "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
26
+
27
+ "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
28
+
29
+ 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
30
+
31
+ 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
32
+
33
+ 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
34
+
35
+ (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
36
+
37
+ (b) You must cause any modified files to carry prominent notices stating that You changed the files; and
38
+
39
+ (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
40
+
41
+ (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
42
+
43
+ You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
44
+
45
+ 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
46
+
47
+ 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
48
+
49
+ 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
50
+
51
+ 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
52
+
53
+ 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
54
+
55
+ END OF TERMS AND CONDITIONS
56
+
57
+ APPENDIX: How to apply the Apache License to your work.
58
+
59
+ To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
60
+
61
+ Copyright [yyyy] [name of copyright owner]
62
+
63
+ Licensed under the Apache License, Version 2.0 (the "License");
64
+ you may not use this file except in compliance with the License.
65
+ You may obtain a copy of the License at
66
+
67
+ http://www.apache.org/licenses/LICENSE-2.0
68
+
69
+ Unless required by applicable law or agreed to in writing, software
70
+ distributed under the License is distributed on an "AS IS" BASIS,
71
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
72
+ See the License for the specific language governing permissions and
73
+ limitations under the License.
package/NOTICE ADDED
@@ -0,0 +1,19 @@
1
+ Hawiyah — هوية
2
+
3
+ تم التطوير بواسطة فريق التطوير في قطاع المنصات الوطنية - مركز المعلومات الوطني
4
+ Developed by the Development Team in the National Platforms Sector — National Information Center.
5
+
6
+ Copyright 2026 Mohamed Embarak
7
+
8
+ Licensed under the Apache License, Version 2.0. See LICENSE.
9
+
10
+ This distribution includes an Umm al-Qura calendar table derived from
11
+ HijriDate 2.3.0, Copyright (c) 2018 Mohammed Alshehri, licensed under MIT.
12
+ The full notice and provenance are in packages/core/third-party in the
13
+ source repository and third-party in the core package.
14
+
15
+ Brand source fonts are licensed separately under the SIL Open Font License
16
+ 1.1. The unmodified notices accompany their files in brand/third-party.
17
+ Font software and third-party dependencies retain their own licenses.
18
+ The Hawiyah name and identity do not imply endorsement by Saudi Arabia's
19
+ Digital Government Authority. Apache 2.0 does not grant trademark rights.
package/PORTAL.md ADDED
@@ -0,0 +1,74 @@
1
+ # Portal layout
2
+
3
+ `PortalLayout` composes `PortalHeader`, optional `SecondaryNav`, `Breadcrumb`,
4
+ a focusable main container and `PortalFooter`. It defaults to Arabic and RTL.
5
+ It is a public portal shell; keep application routing and page content in the app.
6
+
7
+ ```tsx
8
+ import { PortalLayout } from '@hawiyah/react';
9
+ import '@hawiyah/styles/styles.css';
10
+
11
+ <PortalLayout brand="هوية"
12
+ headerProps={{ brandHref: '/', navigation: [{ label: 'الخدمات', href: '/services', current: true }] }}
13
+ secondaryNavigation={[{ label: 'دليل الخدمات', href: '/services', current: true }]}
14
+ breadcrumbs={[{ label: 'الرئيسية', href: '/' }, { label: 'الخدمات', current: true }]}
15
+ footerProps={{ links: [{ label: 'الخصوصية', href: '/privacy' }] }}>
16
+ <h1>الخدمات</h1>
17
+ </PortalLayout>
18
+ ```
19
+
20
+ `headerProps` accepts the existing header API plus `menuLabel`, `navigationId`,
21
+ and `navigationLabel`. `actions`/`notice` remain composable React nodes. Supply
22
+ your actual brand SVG as `brand`; no government artwork is inferred. `footerProps`
23
+ accepts the existing footer's brand, description, links, linksLabel and legal.
24
+ `mainId` is generated with React `useId` unless supplied. `skipLabel`,
25
+ `breadcrumbLabel` and `secondaryLabel` override the Arabic accessible labels.
26
+
27
+ For English, set `lang="en" dir="ltr"` and translate the content and labels,
28
+ including `skipLabel`, `breadcrumbLabel`, `secondaryLabel`,
29
+ `headerProps.navigationLabel`, `headerProps.menuLabel`, `footerProps.linksLabel`.
30
+ Language and direction are independent props.
31
+
32
+ Standalone `PortalHeader` adds a mobile disclosure when `navigation` has items;
33
+ set `mainId` to render its skip link. Escape closes and focuses the toggle;
34
+ selection, outside press and focus leaving close it. Tab is not trapped. The
35
+ links stay visible at desktop widths. Multiple headers receive separate IDs.
36
+ `SecondaryNav` takes `items` and `label`; these are ordinary page links with
37
+ `aria-current`, not tabs or menus.
38
+
39
+ The 48rem disclosure breakpoint is an intentional Hawiyah policy. The layout uses
40
+ shared tokens in DGA, Hawiyah and custom themes, and introduces no animation.
41
+ Source-informed composition does not claim pixel parity or DGA certification.
42
+
43
+ ## Mobile navigation bar
44
+
45
+ Use `variant="mobile"` on `SecondaryNav` (React/Vue) or `hw-secondary-nav`
46
+ (Angular). Items accept an optional decorative `icon` name from the bundled icon
47
+ stylesheet, such as `home-01`, `search-01` or `menu-01`; labels remain required.
48
+ Vanilla uses `data-variant="mobile"` and the same semantic HTML:
49
+
50
+ ```html
51
+ <nav class="hw-secondary-nav" data-variant="mobile" aria-label="تنقل الجوال">
52
+ <ul>
53
+ <li><a href="/" aria-current="page"><span class="hw-icon" data-icon="home-01" aria-hidden="true"></span><span>الرئيسية</span></a></li>
54
+ <li><a href="/services"><span class="hw-icon" data-icon="search-01" aria-hidden="true"></span><span>الخدمات</span></a></li>
55
+ </ul>
56
+ </nav>
57
+ ```
58
+
59
+ The bar uses equal-width items, 24 px icons, a 70 px minimum height, wrapped labels
60
+ and a top current-page indicator. It is in normal flow and does not cover page
61
+ content. Safe-area bottom padding accommodates mobile devices; the source OS
62
+ home indicator is not reproduced. The application owns route/current updates.
63
+ These are page links, not content tabs: preserve native Tab/Enter navigation.
64
+
65
+ DGA/custom color transitions use `--hw-duration-tab-bar: 300ms` and
66
+ `--hw-ease-tab-bar: cubic-bezier(0, 0, 0.58, 1)`. Hawiyah remains immediate by
67
+ default. Reduced motion removes transitions; forced colors retains the selected
68
+ top border and keyboard focus. Hover does not mark a route selected.
69
+
70
+ Source Mobile `8332:24202` → `8332:24242` → `8332:24282` supports color changes,
71
+ not a sliding indicator or tab-panel animation. Semantic colors adapt to theme;
72
+ icon color follows the label, since imported icon override endpoints were not
73
+ resolved. Arabic width differs from English in the source, so no fixed item
74
+ width is imposed. This is a source-informed composition, not certified parity.
package/README.md CHANGED
@@ -1,3 +1,462 @@
1
- # Temporary Holding Version
1
+ # @hawiyah/react
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Native React 19 components for Hawiyah — هوية, a UI library built to comply with
4
+ the DGA Platforms Code. React owns
5
+ all rendered elements. No vanilla controller is mounted into a React tree.
6
+
7
+ ```tsx
8
+ import { useState } from 'react';
9
+ import '@hawiyah/styles/styles.css';
10
+ // Optional, only when the application wants the reset:
11
+ import '@hawiyah/styles/reset.css';
12
+ import { Button, Combobox, Dialog, TextField } from '@hawiyah/react';
13
+
14
+ function Application() {
15
+ const [city, setCity] = useState('');
16
+ return (
17
+ <form>
18
+ <TextField label="الاسم الكامل" name="name" autoComplete="name" required />
19
+ <Combobox
20
+ label="المدينة"
21
+ name="city"
22
+ options={[{ value: 'riyadh', label: 'الرياض' }]}
23
+ value={city}
24
+ onValueChange={setCity}
25
+ required
26
+ />
27
+ <Button type="submit">متابعة</Button>
28
+ </form>
29
+ );
30
+ }
31
+ ```
32
+
33
+ Install with `npm install @hawiyah/styles @hawiyah/react`. This is a pre-1.0
34
+ release, so a minor version may still change its API. React and React DOM are
35
+ peers; `@hawiyah/core` provides the shared, pure keyboard and combobox behavior.
36
+ Styles are never imported implicitly. When consuming this repository's workspace
37
+ instead of npm, build the packages before importing their exports. Importing and server-rendering this package require no browser globals.
38
+ Stable associations use React `useId`, including during hydration.
39
+
40
+ ## Common conventions
41
+
42
+ Native attributes, events, `className`, ARIA attributes and React 19 `ref` props
43
+ are passed to the component's principal native element. `TextField`, `Textarea`,
44
+ `Select`, `Checkbox`, `Radio`, `Switch`, `Combobox` and `FileUpload` forward refs
45
+ to their actual inputs, not their label wrappers. Use normal React
46
+ `value`/`defaultValue` or `checked`/`defaultChecked` and `onChange` for native
47
+ controls. `Button` defaults to `type="button"`; explicitly opt into form submit.
48
+
49
+ Labels, hints and errors are renderable React content. Hints/errors are associated
50
+ with the control and errors set `aria-invalid`. Required native controls retain
51
+ browser constraint validation. `Field` provides label/description context for one
52
+ control; use the same `controlId` and input `id` if choosing your own identifier.
53
+ Use native `fieldset` and `legend` for related checkbox/radio groups.
54
+
55
+ Set `dir` and `lang` independently at the document or subtree. Horizontal Tabs
56
+ follow computed direction; vertical arrows stay vertical. Theme, mode and density
57
+ are CSS attributes on an ancestor:
58
+
59
+ ```html
60
+ <div dir="rtl" lang="ar" data-hw-theme="dga"
61
+ data-hw-mode="dark" data-hw-density="comfortable">…</div>
62
+ ```
63
+
64
+ Default auxiliary labels are English; provide Arabic or another language through
65
+ label/message props. Business dates and numbers can use `@hawiyah/core` formatters
66
+ with explicit locale, calendar and time zone. No component infers locale from RTL.
67
+
68
+ ## Catalog
69
+
70
+ | Component | Composition and important props |
71
+ | --- | --- |
72
+ | `Button` | Native button; `variant="primary\|secondary\|ghost\|danger"`, `size="sm\|md\|lg"`, `loading` disables and sets busy. |
73
+ | `Field` | `label`, `hint`, `error`, `required`, `controlId`; wrap one form control as children. |
74
+ | `TextField` | Native input attributes plus `label`, `hint`, `error`; supports text/email/date/number/etc. |
75
+ | `Textarea` | Native textarea plus label/hint/error. |
76
+ | `Select` | Native select plus label/hint/error; `options: Option[]` or native option/optgroup children. Native `multiple` is supported. |
77
+ | `Checkbox` | Native checkbox plus label/hint/error and `indeterminate`. |
78
+ | `Radio` | Native radio plus label/hint/error; supply a shared `name` for a native group. |
79
+ | `Switch` | Native checkbox with `role="switch"`, label/hint/error. |
80
+ | `Tabs` | `items: {value,label,content,disabled?}[]`, required `label`, `value`/`defaultValue`, `onValueChange`, `orientation`, `activationMode`, `overflow: menu/scroll` (default menu), configurable `overflowLabel`. Horizontal compact containers move excess labels into a keyboard-accessible disclosure; vertical tabs remain visible. |
81
+ | `Dialog` | Required `title`; `description`, `trigger`, `open`/`defaultOpen`, `onOpenChange`, `closeLabel`, `closeOnBackdrop`; children/footer can render from `{close}`. |
82
+ | `Combobox` | Single selection from `Option[]`; label/hint/error, `value`/`defaultValue`, `onValueChange(value, option?)`, `onInputChange(query)`, `open`/`defaultOpen`, `onOpenChange`, native form attributes. |
83
+ | `Accordion` | `items: {value,title,content,open?,defaultOpen?,onOpenChange?}[]`; `exclusive` uses a native details name group. |
84
+ | `Card` | Native article; header/footer slots and body children. |
85
+ | `Badge` | Static label; `tone="neutral\|success\|warning\|danger\|info"`, `variant="soft\|outline\|dot"`; [source and adaptations](../../docs/nic-badges.md). |
86
+ | `Alert` | `tone="info\|success\|warning\|danger"`, optional text `title`, children. Default role is status (danger: alert); override role for static content. |
87
+ | `Table<Row>` | `caption`, `columns: {key,header,cell(row,index),rowHeader?}[]`, `rows`, `rowKey`, `wrapperProps`; alternatively native table children. |
88
+ | `Pagination` | `totalPages`, `page`/`defaultPage`, `onPageChange`; Arabic default `label`, `previousLabel`, `nextLabel`, `getPageLabel`, `formatPage`; `siblingCount` (default 1), `disabled`, `hideOnSinglePage` (default false). Ellipsis disclosures use `getGapLabel(start,end)`, `jumpLabel`, `goLabel`; gaps above 100 pages use bounded integer entry. Parent fetches/slices data; no navigation or scrolling is imposed. |
89
+ | `Breadcrumb` | `items: {label,href?,current?}[]`, localized `label`; last item is current. |
90
+ | `Progress` | Native progress with required accessible `label`, `value`, `max` (100); omit value for indeterminate. |
91
+ | `Skeleton` | Decorative span, hidden from assistive technology; set dimensions via className/style. |
92
+ | `Spinner` | Status span; localized `label` (default “Loading”). |
93
+ | `Avatar` | Required `name`; optional `src`, `initials`, `size="40\|64\|80\|120\|sm\|md\|lg\|xl"`, `variant="circle\|square"`. Failed images fall back to initials; changing `src` retries. [Source notes](../../docs/nic-avatars.md). |
94
+ | `Divider` | Native horizontal rule. |
95
+ | `Tooltip` | Plain string `content`; one focusable React element as children. |
96
+ | `FileUpload` | Native file input with label/hint/error; `accept`, `multiple`, native `onChange`, `onFilesChange(File[])`, optional supplementary children. |
97
+ | `Stepper` | `items: {value,label,description?,status?}[]`, `current`, accessible `label`. Status: complete/current/upcoming. Static progress, not navigation. |
98
+ | `EmptyState` | Required `title`; optional `description`, decorative `icon`, `action`, children. |
99
+
100
+ All prop interfaces, `TabItem`, `AccordionItem`, `TableColumn<Row>`,
101
+ `BreadcrumbItem`, `StepItem`, and core `Option`, `Direction`, `Orientation` types
102
+ are exported. Option values and item values must be unique stable strings.
103
+
104
+ ## Stateful controls
105
+
106
+ ### Tabs
107
+
108
+ ```tsx
109
+ <Tabs
110
+ label="Application sections"
111
+ value={section}
112
+ onValueChange={setSection}
113
+ activationMode="manual"
114
+ items={[
115
+ { value: 'details', label: 'Details', content: <Details /> },
116
+ { value: 'history', label: 'History', content: <History /> },
117
+ ]}
118
+ />
119
+ ```
120
+
121
+ Arrows wrap and skip disabled items; Home/End move to the first/last enabled tab.
122
+ Automatic activation (default) selects while moving focus. Manual activation
123
+ selects through the native button's Enter/Space/click behavior. Inactive panels
124
+ stay mounted with `hidden`, preserving their form state. The first enabled tab
125
+ is the default and the fallback if the supplied selection is unavailable.
126
+
127
+ ### Dialog
128
+
129
+ ```tsx
130
+ <Dialog title="Review request" trigger={<Button>Review</Button>}
131
+ footer={({ close }) => <Button onClick={close}>Done</Button>}>
132
+ {({ close }) => <p>Review your details before continuing.</p>}
133
+ </Dialog>
134
+ ```
135
+
136
+ The wrapper invokes native `showModal()` and `close()`; it requires native modal
137
+ dialog support. Native modality contains focus and makes the background inert.
138
+ Escape requests closing; set `onCancel={event => event.preventDefault()}` to
139
+ cancel that action. Backdrop closing defaults on and can be disabled.
140
+ `autoFocus` on a child selects initial focus. Closing restores the prior focused
141
+ element when it remains connected. The optional trigger is one button-like React
142
+ element that forwards `onClick`; its original handler runs first and can prevent
143
+ opening. You can also use a separate button with controlled `open`.
144
+
145
+ Server rendering emits a closed dialog; a default-open dialog becomes modal after
146
+ hydration. For controlled state, handle `onOpenChange` to apply requests from the
147
+ close button, Escape or native dialog forms.
148
+
149
+ ### Combobox
150
+
151
+ The editable single-select input shares `transitionCombobox` and
152
+ `getComboboxOptions` with other adapters. Arabic diacritics/alef variants and
153
+ letter case are folded for searching, without changing labels or stored values.
154
+ Opening a committed selection displays all options. Typing clears the committed
155
+ selection; arrows/Home/End change the active option, and Enter or pointer click
156
+ commits an enabled option. Focus stays in the input. Escape/Tab close the list;
157
+ blur keeps the query without silently selecting.
158
+
159
+ `name` belongs to a hidden native input containing the selected option value.
160
+ Arbitrary text does not become a submitted value. `required` uses custom validity
161
+ to require a committed choice; localize its message with `requiredMessage`.
162
+ `disabled`, `readOnly`, `form`, forwarded input refs, and native form resets are
163
+ supported. A reset restores an uncontrolled `defaultValue`; controlled state
164
+ continues to belong to the caller. When controlled, apply `onValueChange`, including
165
+ its empty string when the user edits the selection.
166
+
167
+ Use `getResultsMessage(count)` for the live region and `emptyMessage` for the
168
+ empty list. There is no multiselect, remote fetching, option virtualization, or
169
+ custom option rendering in this version. The caller can supply updated options.
170
+
171
+ ### Tooltip and file upload
172
+
173
+ Tooltip adds a description to its focusable child, opens on hover/focus, and closes
174
+ on Escape or when focus/pointer leave. Custom child components must forward
175
+ `aria-describedby`. Keep tooltip text noninteractive; essential instructions belong
176
+ in visible labels/hints. FileUpload is a native file picker. It does not upload,
177
+ validate file contents, or create network requests.
178
+
179
+ ## Example and checks
180
+
181
+ `examples/react` is a complete local service application: conditional applicant
182
+ fields, Arabic city search, review dialog, request history/filter/pagination,
183
+ independent locale/direction/mode toggles and a service guide. Its sample requests
184
+ are ephemeral in-memory data. Build the workspace packages, then run Vite using
185
+ `examples/react/vite.config.ts` on port 4174.
186
+
187
+ Focused tests cover import/server rendering without DOM globals, field/ref/form
188
+ associations, RTL/manual/controlled tabs, Arabic combobox search and commits,
189
+ controlled typing, reset, modal lifecycle calls, tooltip dismissal and bounded
190
+ pagination. JSDOM dialog methods are test doubles; native modality and browser
191
+ focus containment need the browser integration suite.
192
+
193
+ ## Expanded catalog: 50 additional components
194
+
195
+ The package now exports 77 components. Added components use the same native
196
+ attributes/ref conventions and shared styles. Array values and item identifiers
197
+ must be unique. Custom form controls support `value`/`defaultValue` and
198
+ `onValueChange`; uncontrolled values return to their defaults on native form reset,
199
+ including forms associated through `form`. Canceled reset events are respected.
200
+ Controlled values remain owned by the application.
201
+
202
+ | Added control | API and behavior |
203
+ | --- | --- |
204
+ | `InputGroup` | `start`/`end` adornments and control children; surrounding `Field` supplies the accessible label. |
205
+ | `PasswordField` | TextField props plus `visible`/`defaultVisible`, `onVisibleChange`, `showLabel`, `hideLabel`. Reveal preserves the native input and its value. |
206
+ | `SearchField` | String `value/defaultValue`, `onValueChange`, native `onChange`, `clearLabel`; optional `suggestions`, `maxSuggestions` (8), `resultsMessage(count)`, `onSearch(query)`, `showSearchButton` (false), `searchLabel`. Native input props/ref and field decoration are preserved. |
207
+ | `NumberField` | Native number input with label/hint/error, min/max/step and normal React input events. Empty remains empty; it is not coerced to zero. |
208
+ | `Slider` | Numeric value/defaultValue, `onValueChange`, min/max/step, `showValue`, `formatValue`; one labelled native range. |
209
+ | `RangeSlider` | Numeric `[lower,upper]`, `onValueChange`, min/max/step; required `lowerLabel`/`upperLabel`, optional group `label`. `lowerName`/`upperName` or repeated `name` submit both values. Native handles cannot cross. |
210
+ | `Rating` | Numeric value/defaultValue, `onValueChange`, max (1–20, default 5), label/name/required, `getRatingLabel`; one native radio group. |
211
+ | `RatingDisplay` | Required `label`, numeric `value=0`, `max=5` (1–20), `showValue=true`, `getValueLabel(value,max)`; noninteractive fractional aggregate score with one accessible image name. |
212
+ | `PinInput` | String value/defaultValue, `onValueChange`, `length` (default 6), optional decorative `showSlots`; one accessible OTP input normalizes Arabic-Indic and Eastern Arabic digits, including pasted input. |
213
+ | `TagInput` | String array value/defaultValue, `onValueChange`, name, required, `getRemoveLabel`, `requiredMessage`; Enter/comma commits a unique tag, empty Backspace removes the last tag. Each committed tag produces a native form value. |
214
+ | `MultiSelect` | Required label and options; string array value/defaultValue, `onValueChange`, name, required, `searchLabel`, `emptyMessage`, `requiredMessage`. Searchable native checkboxes; filtered-out selections retain repeated form values. |
215
+ | `Calendar` | ISO Gregorian value/defaultValue, `onValueChange`; month/defaultMonth, `onMonthChange`; locale/calendar/weekStartsOn, min/max, `isDateDisabled`, disabled/readOnly, optional form/name and localized toolbar/day labels. |
216
+ | `DatePicker` | Typed ISO text input plus Calendar in a native modal. Adds calendar/defaultCalendar, `onCalendarChange`, localized calendar selector labels, calendar button/dialog labels and unsupported-range message. |
217
+ | `FileDropzone` | Native file input props plus `onFilesChange(File[])`, `dropLabel`, `getRejectedMessage`; accepted dropped files populate the actual native input. The picker and dropped selection participate in forms. No upload occurs automatically. |
218
+
219
+ Native `TextField` input types retain their platform semantics: text/search/tel/url
220
+ are strings; email uses native validation; number uses native numeric constraints
221
+ and permits empty input; date/month/week/time/datetime-local use HTML's date/time
222
+ value formats; color uses the native picker. Use `FileUpload`/`FileDropzone` for
223
+ file values, Checkbox/Radio/Switch for checked state, and Slider/RangeSlider for
224
+ range controls. A native date input stores Gregorian ISO dates regardless of the
225
+ calendar used to select or describe a date.
226
+
227
+ ### Saudi date selection
228
+
229
+ ```tsx
230
+ const [date, setDate] = useState('2026-10-28');
231
+ const [calendar, setCalendar] = useState<'gregory' | 'islamic-umalqura'>('islamic-umalqura');
232
+
233
+ <DatePicker
234
+ label="تاريخ الموعد"
235
+ name="appointment"
236
+ value={date}
237
+ onValueChange={setDate}
238
+ calendar={calendar}
239
+ onCalendarChange={setCalendar}
240
+ locale="ar-SA"
241
+ calendarSystemLabel="التقويم"
242
+ gregorianLabel="ميلادي"
243
+ hijriLabel="هجري — أم القرى"
244
+ calendarLabel="اختر تاريخ الموعد"
245
+ buttonLabel="فتح التقويم"
246
+ previousLabel="الشهر السابق"
247
+ nextLabel="الشهر التالي"
248
+ closeLabel="إغلاق"
249
+ required
250
+ />
251
+ ```
252
+
253
+ The picker includes a Gregorian/Umm al-Qura selector. Switching display systems
254
+ preserves the exact ISO day, and reopening returns to the selected date's month.
255
+ Calendar names, navigation, picker controls and validation messages default to Arabic. Set `locale="en"` for English copy or pass a label prop to override a particular control.
256
+
257
+ Direction and locale remain independent of calendar. Calendar's arrows follow
258
+ visual RTL/LTR direction; Up/Down move a week, Home/End move within the week,
259
+ PageUp/PageDown move a month, and Shift+PageUp/PageDown move a year. Disabled days
260
+ are skipped and focus remains on one roving date button.
261
+
262
+ Umm al-Qura arithmetic and numeric date labels use the shared Saudi reference
263
+ table through `@hawiyah/core`, including `formatCalendarDate`; they do not rely on
264
+ ICU's differing historical/future numeric Hijri fields. The supported table spans
265
+ **1924-08-01 through 2077-11-16 (AH 1343–1500)**. Navigation stops at these
266
+ boundaries. When a Gregorian selected date lies outside this range, switching to
267
+ Hijri preserves the ISO input, shows a localized unsupported-range message, and
268
+ renders no fabricated Hijri selection, and blocks native form submission with custom validity and `aria-invalid`. Switching back to Gregorian clears the calendar-range error while keeping application min/max constraints. Use `unsupportedCalendarMessage` to
269
+ translate that explanation. Read the workspace calendar evidence for provenance
270
+ and verification; this interface is not an official service or certification.
271
+
272
+ `getDayLabel` customizes Calendar date announcements. Supply stable initial
273
+ `month`/`defaultMonth` or `value` when a server-rendered view must be independent of
274
+ the current date. Standalone Calendar submits its selected supported date through
275
+ an optional hidden `name`; use DatePicker when native required/date validation is
276
+ needed. `isDateDisabled` applies to both the calendar and the DatePicker's native
277
+ input validation. `unavailableMessage` localizes custom date validation.
278
+
279
+ | Added interaction | API and behavior |
280
+ | --- | --- |
281
+ | `ToggleGroup` | Required label/options, `variant="segmented\|outline"`. Default single selection uses native radios with string value; `multiple` uses pressed buttons with string-array value and repeated hidden form values. Both support defaultValue/onValueChange/name/form/disabled/readOnly. |
282
+ | `Popover` | Required title/trigger, open/defaultOpen/onOpenChange and region children. Trigger toggles a labelled disclosure; outside press and Escape close. Interactive content uses ordinary Tab. |
283
+ | `Menu` | Required trigger/label and `items: {value,label,disabled?,href?,onSelect?}[]`; open/defaultOpen/onOpenChange and `onAction(value)`. Up/Down/Home/End skip disabled entries; Escape/action return focus. |
284
+ | `Drawer` | Dialog props plus `side="start\|end\|bottom"`; same native modal and focus behavior as Dialog. |
285
+ | `ToastRegion` | `messages: {id,title?,description,tone?,urgent?,dismissible?}[]`, `onDismiss(id)`, `label`, `getDismissLabel(message)`. Choose `variant="banner"` and optionally `position="inline"` or a corner. Buttons require an `onDismiss` handler. Arabic labels default to الإشعارات and إغلاق الإشعار with available message text. |
286
+ | `Collapsible` | Native details with title, open/defaultOpen/onOpenChange, disabled and children. |
287
+ | `TreeView` | Required label and `items: {id,label,disabled?,children?}[]`; value/defaultValue/onValueChange and expanded/defaultExpanded/onExpandedChange. Visible nodes have tree levels, positions and roving focus; horizontal expansion follows RTL. |
288
+ | `Carousel` | Required label/slides; `index/defaultIndex/onIndexChange`, `loop=false`, renderable `slides`, zero-based `getSlideLabel(index,total)`. `showIndicators=true`, localized control/indicator/description/empty labels, inert inactive slides, explicit navigation and no autoplay. [Source and motion notes](../../docs/nic-carousel.md). |
289
+
290
+ ```tsx
291
+ // Toast state is ordinary application state. There is no global singleton.
292
+ const [messages, setMessages] = useState([
293
+ { id: 'saved', description: 'تم حفظ المسودة', tone: 'success' as const },
294
+ ]);
295
+ <ToastRegion label="الإشعارات" messages={messages}
296
+ getDismissLabel={() => 'إغلاق الإشعار'}
297
+ onDismiss={id => setMessages(current => current.filter(item => item.id !== id))} />
298
+ ```
299
+
300
+ Popover/Menu trigger elements must forward native button event and ARIA props.
301
+ TreeView is a single-selection navigation structure, without drag/drop or virtual
302
+ rows. Caller-owned items and toast messages remain unchanged until the caller
303
+ applies a callback.
304
+
305
+ | Added content/pattern | API and behavior |
306
+ | --- | --- |
307
+ | `Link` | Native anchor with optional `external` (new tab and noopener/noreferrer). |
308
+ | `IconButton` | Button props plus required accessible `label`; icon children are decorative. |
309
+ | `ButtonGroup` | Native group with required accessible `label` and button children. |
310
+ | `Chip` | Text children, optional tone/onRemove/removeLabel/disabled. |
311
+ | `DescriptionList` | `items: {term,description,key?}[]`; native dl/dt/dd. |
312
+ | `Stat` | Required label/value; optional description/trend content. Values are formatted by the caller. |
313
+ | `Timeline` | `items: {id,title,description?,dateTime?,dateLabel?,status?}[]`; status complete/active/upcoming and optional label. |
314
+ | `Meter` | Native meter attributes with required accessible label. |
315
+ | `AvatarGroup` | Required items/label, max (default 5), getOverflowLabel. Items use AvatarProps. |
316
+ | `DataTable<Row>` | Required caption/rows/columns; columns have key/label/sortable/render/rowHeader. Optional rowKey, sort/defaultSort/onSortChange, locale, emptyMessage, getSortLabel. Stable immutable localized sorting and native aria-sort headers. |
317
+ | `ServiceCard` | Required title; description/category/icon/metadata, href/actionLabel or action slot, body children. |
318
+ | `ServiceDetails` | Required title; description/metadata/requirements/requirementsLabel/action and children. |
319
+ | `PortalHeader` | Required brand slot, optional brandHref/navigation/navigationLabel/actions/notice and children. Navigation items are label/href/current. Optional top-level `groups` contain a label and child links. See [grouped navigation](../../docs/nic-portal-navigation.md). |
320
+ | `PortalFooter` | Optional brand/description/links/linksLabel/legal and children. |
321
+ | `Feedback` | Required question label; value/defaultValue/onValueChange for `{helpful:'yes'\|'no'\|'',comment}` and `onFeedback` on native submit; translated yes/no/comment/submit/success labels. |
322
+ | `CookieConsent` | Required title/description; value/defaultValue/onValueChange (`accepted`/`rejected`/null), translated buttons and optional policy link. Null displays the notice. Persistence and cookie activation belong to the application. |
323
+ | `NotificationList` | Required label/items; items id/title/description/href/dateTime/dateLabel/read. Optional emptyMessage, onMarkRead, getMarkReadLabel, unreadLabel. |
324
+
325
+ Platform patterns are configurable ordinary content. Supply your own brand,
326
+ service facts, legal text and links. They do not provide government seals, verified
327
+ trust status, identity verification, authentication, submissions or storage.
328
+ Feedback callbacks report local user choices; applications own asynchronous
329
+ submission outcomes. DataTable provides native sorting, without editing,
330
+ virtualization, selection or a remote data engine; existing Table remains fully
331
+ composable for those integrations.
332
+
333
+ ### Layout primitives
334
+
335
+ | Primitive | Native composition |
336
+ | --- | --- |
337
+ | `Container` | Div with optional maxWidth. |
338
+ | `Stack` | Vertical flex div. |
339
+ | `Inline` | Horizontal flex div with optional wrap. |
340
+ | `Grid` | Div with responsive columns or minItemWidth. |
341
+ | `Columns` | Div with responsive explicit columns. |
342
+ | `Sidebar` | Two child areas; side/sidebarWidth/collapseAt control responsive placement. |
343
+ | `Center` | Centered content div. |
344
+ | `Spacer` | Flexible space div. |
345
+ | `AspectRatio` | Div with positive numeric ratio. |
346
+ | `Section` | Semantic section with common layout props. |
347
+ | `AppShell` | Supply `header`, `sidebar` and `footer` content around the main landmark. The Arabic skip link focuses main. Set `mainId` to choose its target ID and `skipLabel` to localize the link. |
348
+ | `PageHeader` | Native header with title h1, description/breadcrumb/actions and children. |
349
+
350
+ The first ten primitives accept shared `LayoutProps` and native attributes/refs.
351
+ `gap` and `padding` accept token numbers 0–16 or CSS lengths, including responsive
352
+ objects `{base,sm,md,lg,xl}`. `columns` accepts positive integer values or the same
353
+ responsive object. Common props also include align, justify and wrap. Styles come
354
+ from `layoutStyles` CSS variables; there are no JS viewport measurements. Consumer
355
+ `style` overrides can explicitly replace generated properties.
356
+
357
+ ```tsx
358
+ <Container maxWidth="76rem" padding={{ base: 4, lg: 8 }}>
359
+ <Stack gap={6}>
360
+ <PageHeader title="الخدمات" description="اختر الخدمة المناسبة" />
361
+ <Sidebar side="start" sidebarWidth="18rem" collapseAt="md" gap={6}>
362
+ <aside>مرشحات الخدمات</aside>
363
+ <Grid columns={{ base: 1, md: 2, xl: 3 }} gap={4}>
364
+ <ServiceCard title="تصريح نشاط" href="/services/permit" actionLabel="تفاصيل الخدمة" />
365
+ </Grid>
366
+ </Sidebar>
367
+ </Stack>
368
+ </Container>
369
+ ```
370
+
371
+ ### Integration boundaries and evidence
372
+
373
+ Maps, rich-text editors, charts, code editors, PDF viewers, CAPTCHA and payment
374
+ engines belong to their specialist libraries/services. Mount the real engine in
375
+ an ordinary React-owned region or Card/AspectRatio with its own accessible name,
376
+ loading/error UI and lifecycle cleanup. Use Field hint/error associations around
377
+ real input engines and Dialog only when an engine's focus model supports native
378
+ modality. Pass theme tokens/direction through the engine's documented API, use
379
+ its documented ref/event integration, and adapt actual resulting values into your
380
+ form state. These components do not simulate an engine, credential exchange or
381
+ payment outcome, and do not inject unsafe HTML.
382
+
383
+ Expansion regressions cover all 50 additions under DOM-free server rendering,
384
+ responsive CSS-variable output, PIN digit normalization, repeated values and
385
+ external/canceled resets, ordered range handles, Saudi calendar navigation and
386
+ range boundaries, picker system switching/reopening/unsupported dates, menu and
387
+ tree focus, popover dismissal, table sorting, caller-owned toasts and explicit
388
+ feedback/consent callbacks. Native dialog modality and browser accessibility
389
+ remain covered by the existing browser suite; server/unit tests are not a claim
390
+ of complete assistive-technology testing for every new pattern.
391
+
392
+ ## Portal shell
393
+
394
+ [`PortalLayout`, `PortalHeader` and `SecondaryNav`](PORTAL.md) provide a complete
395
+ Arabic-default portal shell with responsive navigation, breadcrumbs and footer.
396
+ Run the React example with `?preview=portal` to use the native composed shell.
397
+
398
+ ## NIC card composition
399
+
400
+ See [cards](../../docs/nic-cards.md) for source-mapped spacing, variants, whole-card links, disabled semantics and content classes. Native framework consumers include `?preview=nic`; vanilla uses the documented semantic HTML recipe.
401
+
402
+ See [NIC button variants](../../docs/nic-buttons.md) for all ten appearances,
403
+ interaction states, dark-surface use and density adaptations.
404
+
405
+ [Ready component examples](../../docs/ready-component-examples.md) cover all five
406
+ alert/notification tones, header/footer composition and guide-only navigation containment.
407
+
408
+
409
+ [Site verification](../../docs/site-verification.md) composes a native disclosure above the header with deployment-owned domain and certificate information. [Grouped portal footers](../../docs/nic-footer.md) add navigation groups, support actions, legal links and identity slots.
410
+
411
+ [Link and breadcrumb states](../../docs/nic-links.md) document neutral/standalone variants, disabled destinations, source contrast substitutions and native adapter APIs.
412
+
413
+
414
+ ### Table presentation and empty results
415
+
416
+ `Table` and `DataTable` expose `variant="default|striped|bordered"`,
417
+ `size="sm|md|lg"` and `emptyMessage`. DGA cells have a 48px default minimum;
418
+ 40px/56px alternate sizes and striped/bordered variants are Hawiyah extensions.
419
+ Captions name keyboard-focusable horizontal scroll containers; row-header
420
+ columns render native `th scope="row"`. Empty data spans the column count and
421
+ uses an Arabic message by default. Sorting does not mutate caller records.
422
+ React/Vue native `data-variant` and `data-size` attributes remain supported;
423
+ typed props take precedence. `wrapperProps` customizes the scroll container.
424
+ See [table evidence and adaptations](../../docs/nic-tables.md).
425
+
426
+ Rating inputs select whole numbers; finite prefilled values are clamped to the normalized maximum and floored. Fractional averages belong in `RatingDisplay`. Nonfinite maxima fall back to 5 and nonfinite values to 0. Arabic labels and Latin digits are the defaults.
427
+
428
+
429
+ ### Search suggestions
430
+
431
+ SearchField accepts local string suggestions and remains a free-text input.
432
+ With suggestions supplied it exposes an editable combobox/listbox, highlighted
433
+ matches and a polite result count. Arrow keys wrap through matches; Enter on an
434
+ active suggestion selects it and prevents form submission. Ordinary Enter with
435
+ no active suggestion emits `onSearch` with a trimmed, nonempty query and keeps
436
+ native form submission available. Choose either the search callback or the
437
+ form's submit handler for the application request; wiring both duplicates it.
438
+ The optional icon search action is a non-submit button and uses the callback.
439
+ Arabic clear/search labels are defaults and can be localized.
440
+
441
+ Escape dismisses an open list before reaching an enclosing dialog; another
442
+ Escape can dismiss the dialog. Composition/modifier keys do not commit matches.
443
+ Readonly inputs remain searchable but cannot edit/clear/select suggestions;
444
+ disabled inputs cannot search. Blur, outside press, reset and changed suggestion
445
+ configuration clear active-option state. No remote search service is called.
446
+
447
+ ### Content navigation
448
+
449
+ `ContentNavigation` follows application-owned section IDs using nested
450
+ `items: {id, label, disabled?, children?}[]`. Options are `label` (Arabic default),
451
+ `showHeading` (true), `sticky` (false), `scrollRoot` (null/window) and `offset`
452
+ (80px). `onCurrentChange(id)` reports the current section ID. Ordinary
453
+ activation scrolls without changing the URL; keyboard activation also focuses
454
+ the target. Modified anchor clicks retain browser behavior. Disabled entries
455
+ remain noninteractive text links; no section content is created or hidden.
456
+
457
+ ```tsx
458
+ import {ContentNavigation} from '@hawiyah/react';
459
+
460
+ <ContentNavigation items={[{id: 'requirements', label: 'المتطلبات'}]} />
461
+ <section id="requirements"><h2>المتطلبات</h2></section>
462
+ ```