universal-modal-monorepo 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/AUTHORS +5 -0
  2. package/CHANGELOG.md +10 -0
  3. package/LICENSE +21 -0
  4. package/NOTICE +6 -0
  5. package/README.md +239 -0
  6. package/SECURITY.md +21 -0
  7. package/docs/ACCESSIBILITY.md +43 -0
  8. package/docs/API.md +56 -0
  9. package/docs/THEMING.md +65 -0
  10. package/examples/angular/app.component.html +137 -0
  11. package/examples/angular/app.component.ts +53 -0
  12. package/examples/angular/index.html +99 -0
  13. package/examples/angular/main.js +121 -0
  14. package/examples/angular/styles.css +25 -0
  15. package/examples/index.html +53 -0
  16. package/examples/react/App.tsx +242 -0
  17. package/examples/react/index.html +26 -0
  18. package/examples/react/main.js +144 -0
  19. package/examples/react/styles.css +1 -0
  20. package/examples/shared/gallery.css +177 -0
  21. package/examples/vanilla/index.html +89 -0
  22. package/examples/vanilla/main.js +174 -0
  23. package/examples/vanilla/styles.css +1 -0
  24. package/examples/vue/App.vue +190 -0
  25. package/examples/vue/index.html +23 -0
  26. package/examples/vue/main.js +174 -0
  27. package/examples/vue/styles.css +1 -0
  28. package/examples/web-component/index.html +190 -0
  29. package/examples/web-component/main.js +58 -0
  30. package/examples/web-component/styles.css +11 -0
  31. package/package.json +36 -0
  32. package/packages/universal-modal/AUTHORS +5 -0
  33. package/packages/universal-modal/CHANGELOG.md +10 -0
  34. package/packages/universal-modal/LICENSE +21 -0
  35. package/packages/universal-modal/NOTICE +6 -0
  36. package/packages/universal-modal/README.md +29 -0
  37. package/packages/universal-modal/package.json +83 -0
  38. package/packages/universal-modal/scripts/copy-styles.mjs +11 -0
  39. package/packages/universal-modal/src/core/constants.ts +50 -0
  40. package/packages/universal-modal/src/core/event-emitter.ts +46 -0
  41. package/packages/universal-modal/src/core/focus-trap.ts +101 -0
  42. package/packages/universal-modal/src/core/index.ts +39 -0
  43. package/packages/universal-modal/src/core/modal-engine.ts +410 -0
  44. package/packages/universal-modal/src/core/modal-stack.ts +68 -0
  45. package/packages/universal-modal/src/core/scroll-lock.ts +78 -0
  46. package/packages/universal-modal/src/core/theme.ts +50 -0
  47. package/packages/universal-modal/src/core/types.ts +196 -0
  48. package/packages/universal-modal/src/core/utils.ts +114 -0
  49. package/packages/universal-modal/src/dom/index.ts +1 -0
  50. package/packages/universal-modal/src/dom/modal-renderer.ts +191 -0
  51. package/packages/universal-modal/src/index.ts +2 -0
  52. package/packages/universal-modal/src/styles/modal.css +368 -0
  53. package/packages/universal-modal/tsconfig.json +24 -0
  54. package/packages/universal-modal/tsup.config.ts +12 -0
  55. package/packages/universal-modal-angular/AUTHORS +5 -0
  56. package/packages/universal-modal-angular/CHANGELOG.md +9 -0
  57. package/packages/universal-modal-angular/LICENSE +21 -0
  58. package/packages/universal-modal-angular/NOTICE +6 -0
  59. package/packages/universal-modal-angular/README.md +12 -0
  60. package/packages/universal-modal-angular/package.json +85 -0
  61. package/packages/universal-modal-angular/src/index.ts +10 -0
  62. package/packages/universal-modal-angular/src/universal-modal.component.ts +275 -0
  63. package/packages/universal-modal-angular/tsconfig.json +20 -0
  64. package/packages/universal-modal-angular/tsup.config.ts +10 -0
  65. package/packages/universal-modal-react/AUTHORS +5 -0
  66. package/packages/universal-modal-react/CHANGELOG.md +9 -0
  67. package/packages/universal-modal-react/LICENSE +21 -0
  68. package/packages/universal-modal-react/NOTICE +6 -0
  69. package/packages/universal-modal-react/README.md +12 -0
  70. package/packages/universal-modal-react/package.json +87 -0
  71. package/packages/universal-modal-react/src/UniversalModal.tsx +340 -0
  72. package/packages/universal-modal-react/src/index.ts +10 -0
  73. package/packages/universal-modal-react/tsconfig.json +19 -0
  74. package/packages/universal-modal-react/tsup.config.ts +13 -0
  75. package/packages/universal-modal-vanilla/AUTHORS +5 -0
  76. package/packages/universal-modal-vanilla/CHANGELOG.md +9 -0
  77. package/packages/universal-modal-vanilla/LICENSE +21 -0
  78. package/packages/universal-modal-vanilla/NOTICE +6 -0
  79. package/packages/universal-modal-vanilla/README.md +12 -0
  80. package/packages/universal-modal-vanilla/package.json +79 -0
  81. package/packages/universal-modal-vanilla/src/create-modal.ts +125 -0
  82. package/packages/universal-modal-vanilla/src/index.ts +14 -0
  83. package/packages/universal-modal-vanilla/tsconfig.json +18 -0
  84. package/packages/universal-modal-vanilla/tsup.config.ts +10 -0
  85. package/packages/universal-modal-vue/AUTHORS +5 -0
  86. package/packages/universal-modal-vue/CHANGELOG.md +9 -0
  87. package/packages/universal-modal-vue/LICENSE +21 -0
  88. package/packages/universal-modal-vue/NOTICE +6 -0
  89. package/packages/universal-modal-vue/README.md +12 -0
  90. package/packages/universal-modal-vue/package.json +83 -0
  91. package/packages/universal-modal-vue/src/UniversalModal.ts +342 -0
  92. package/packages/universal-modal-vue/src/index.ts +10 -0
  93. package/packages/universal-modal-vue/tsconfig.json +18 -0
  94. package/packages/universal-modal-vue/tsup.config.ts +10 -0
  95. package/packages/universal-modal-web-component/AUTHORS +5 -0
  96. package/packages/universal-modal-web-component/CHANGELOG.md +9 -0
  97. package/packages/universal-modal-web-component/LICENSE +21 -0
  98. package/packages/universal-modal-web-component/NOTICE +6 -0
  99. package/packages/universal-modal-web-component/README.md +12 -0
  100. package/packages/universal-modal-web-component/package.json +83 -0
  101. package/packages/universal-modal-web-component/src/index.ts +11 -0
  102. package/packages/universal-modal-web-component/src/universal-modal-element.ts +424 -0
  103. package/packages/universal-modal-web-component/tsconfig.json +18 -0
  104. package/packages/universal-modal-web-component/tsup.config.ts +10 -0
  105. package/scripts/copy-styles.mjs +11 -0
  106. package/scripts/serve-examples.mjs +48 -0
package/AUTHORS ADDED
@@ -0,0 +1,5 @@
1
+ # Authors
2
+
3
+ Subrahmanyam Poluru <mail.polurus@gmail.com>
4
+ https://polurus.com
5
+ https://www.linkedin.com/in/polurus/
package/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in each package `CHANGELOG.md`.
4
+
5
+ ## 1.0.0 — 2026-08-01
6
+
7
+ ### Added
8
+ - Monorepo packages: core + react / vue / angular / vanilla / web-component
9
+ - Brand and button theming via CSS variables and `theme` option
10
+ - MIT license, AUTHORS, NOTICE, SECURITY policy
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Subrahmanyam Poluru
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/NOTICE ADDED
@@ -0,0 +1,6 @@
1
+ @poluru-labs/universal-modal
2
+ Copyright (c) 2026 Subrahmanyam Poluru
3
+
4
+ This product is developed and maintained by Poluru.
5
+ Homepage: https://polurus.com
6
+ Contact: mail.polurus@gmail.com
package/README.md ADDED
@@ -0,0 +1,239 @@
1
+ # @poluru-labs/universal-modal
2
+
3
+ Production-grade **universal modal** monorepo — framework-agnostic core plus separately installable adapters.
4
+
5
+ **Author:** Subrahmanyam Poluru ([polurus.com](https://polurus.com)) · [mail.polurus@gmail.com](mailto:mail.polurus@gmail.com) · [LinkedIn](https://www.linkedin.com/in/polurus/)
6
+ **Org:** [Poluru](https://github.com/poluru-labs) · License: MIT
7
+
8
+ > npm cannot publish or install a package named `@scope/pkg/react` (the `/` is a subpath, not a package).
9
+ > Install the matching package name instead — same idea as `@headlessui/react`.
10
+
11
+ ---
12
+
13
+ ## Install
14
+
15
+ Pick the adapter for your stack (each pulls in the core automatically):
16
+
17
+ ```bash
18
+ npm install @poluru-labs/universal-modal-react
19
+ npm install @poluru-labs/universal-modal-vue
20
+ npm install @poluru-labs/universal-modal-angular
21
+ npm install @poluru-labs/universal-modal-vanilla
22
+ npm install @poluru-labs/universal-modal-web-component
23
+ ```
24
+
25
+ Core only (engine + styles):
26
+
27
+ ```bash
28
+ npm install @poluru-labs/universal-modal
29
+ ```
30
+
31
+ Import styles once:
32
+
33
+ ```ts
34
+ import '@poluru-labs/universal-modal/styles.css';
35
+ ```
36
+
37
+ ### Brand & button colors
38
+
39
+ Override globally with CSS variables:
40
+
41
+ ```css
42
+ :root {
43
+ --um-color-brand: #0d47a1;
44
+ --um-color-brand-fg: #ffffff;
45
+ --um-btn-confirm-bg: var(--um-color-brand);
46
+ --um-btn-confirm-fg: var(--um-color-brand-fg);
47
+ --um-btn-confirm-border: var(--um-color-brand);
48
+ --um-btn-cancel-bg: #fff;
49
+ --um-btn-cancel-fg: #111827;
50
+ --um-btn-cancel-border: #e5e7eb;
51
+ }
52
+ ```
53
+
54
+ Or per modal via `theme`:
55
+
56
+ ```ts
57
+ createModal({
58
+ title: 'Delete?',
59
+ theme: {
60
+ brand: '#0d47a1',
61
+ confirmBg: '#b91c1c',
62
+ confirmFg: '#ffffff',
63
+ },
64
+ });
65
+ ```
66
+
67
+ See [docs/THEMING.md](docs/THEMING.md).
68
+
69
+ ---
70
+
71
+ ## Packages
72
+
73
+ | Package | Import |
74
+ |---|---|
75
+ | `@poluru-labs/universal-modal` | Core engine, types, `ModalRenderer`, CSS |
76
+ | `@poluru-labs/universal-modal-react` | `UniversalModal` React component |
77
+ | `@poluru-labs/universal-modal-vue` | `UniversalModal` Vue 3 component |
78
+ | `@poluru-labs/universal-modal-angular` | `UniversalModalComponent` Angular standalone |
79
+ | `@poluru-labs/universal-modal-vanilla` | `createModal`, `confirmModal` |
80
+ | `@poluru-labs/universal-modal-web-component` | `<universal-modal>` custom element |
81
+
82
+ ---
83
+
84
+ ## Usage
85
+
86
+ ### React
87
+
88
+ ```bash
89
+ npm install @poluru-labs/universal-modal-react
90
+ ```
91
+
92
+ ```tsx
93
+ import { useState } from 'react';
94
+ import { UniversalModal } from '@poluru-labs/universal-modal-react';
95
+ import '@poluru-labs/universal-modal/styles.css';
96
+
97
+ export function Example() {
98
+ const [open, setOpen] = useState(false);
99
+ return (
100
+ <>
101
+ <button onClick={() => setOpen(true)}>Open</button>
102
+ <UniversalModal open={open} onOpenChange={setOpen} title="Hello">
103
+ <p>Content</p>
104
+ </UniversalModal>
105
+ </>
106
+ );
107
+ }
108
+ ```
109
+
110
+ ### Vue 3
111
+
112
+ ```bash
113
+ npm install @poluru-labs/universal-modal-vue
114
+ ```
115
+
116
+ ```vue
117
+ <script setup lang="ts">
118
+ import { ref } from 'vue';
119
+ import { UniversalModal } from '@poluru-labs/universal-modal-vue';
120
+ import '@poluru-labs/universal-modal/styles.css';
121
+ const open = ref(false);
122
+ </script>
123
+
124
+ <template>
125
+ <UniversalModal v-model:open="open" title="Hello">
126
+ <p>Content</p>
127
+ </UniversalModal>
128
+ </template>
129
+ ```
130
+
131
+ ### Angular
132
+
133
+ ```bash
134
+ npm install @poluru-labs/universal-modal-angular
135
+ ```
136
+
137
+ ```ts
138
+ import { UniversalModalComponent } from '@poluru-labs/universal-modal-angular';
139
+ import '@poluru-labs/universal-modal/styles.css';
140
+ ```
141
+
142
+ ### Vanilla JS / TypeScript
143
+
144
+ ```bash
145
+ npm install @poluru-labs/universal-modal-vanilla
146
+ ```
147
+
148
+ ```ts
149
+ import { createModal } from '@poluru-labs/universal-modal-vanilla';
150
+ import '@poluru-labs/universal-modal/styles.css';
151
+
152
+ const modal = createModal({
153
+ title: 'Delete?',
154
+ role: 'alertdialog',
155
+ onConfirm: async () => api.remove(),
156
+ });
157
+ await modal.open();
158
+ ```
159
+
160
+ ### Web Components
161
+
162
+ ```bash
163
+ npm install @poluru-labs/universal-modal-web-component
164
+ ```
165
+
166
+ ```ts
167
+ import '@poluru-labs/universal-modal-web-component';
168
+ import '@poluru-labs/universal-modal/styles.css';
169
+ ```
170
+
171
+ ```html
172
+ <universal-modal id="demo" title="Settings" size="lg">
173
+ <p>Content</p>
174
+ </universal-modal>
175
+ <script>
176
+ document.getElementById('demo').open();
177
+ </script>
178
+ ```
179
+
180
+ ---
181
+
182
+ ## Monorepo layout
183
+
184
+ ```text
185
+ packages/
186
+ universal-modal/ # @poluru-labs/universal-modal
187
+ universal-modal-react/ # @poluru-labs/universal-modal-react
188
+ universal-modal-vue/
189
+ universal-modal-angular/
190
+ universal-modal-vanilla/
191
+ universal-modal-web-component/
192
+ examples/
193
+ docs/
194
+ ```
195
+
196
+ ---
197
+
198
+ ## Develop
199
+
200
+ ```bash
201
+ npm install
202
+ npm run build
203
+ npm run examples
204
+ ```
205
+
206
+ - http://localhost:4173/examples/index.html — gallery hub
207
+ - Vanilla / Web Component / React / Vue / Angular each include multiple samples (confirm, drawer, sheet, fullscreen, persistent, themed, nested)
208
+
209
+ See `docs/API.md`, `docs/THEMING.md`, and `docs/ACCESSIBILITY.md`.
210
+
211
+ ---
212
+
213
+ ## Publish checklist
214
+
215
+ Before `npm run publish:packages`:
216
+
217
+ 1. You are logged in (`npm whoami`) and can publish under `@poluru-labs`
218
+ 2. GitHub repo URLs in each `package.json` match the real remote (or update them)
219
+ 3. `npm run build` succeeds
220
+ 4. Each package includes `LICENSE`, `AUTHORS`, `NOTICE`, `CHANGELOG.md`, `README.md`
221
+ 5. No secrets in the tarball (`npm pack -w @poluru-labs/universal-modal --dry-run`)
222
+
223
+ ```bash
224
+ npm run build
225
+ npm run publish:packages
226
+ ```
227
+
228
+ Publishes core first, then each adapter.
229
+
230
+ ---
231
+
232
+ ## License & legal
233
+
234
+ - **License:** [MIT](LICENSE) © Subrahmanyam Poluru
235
+ - **Authors:** [AUTHORS](AUTHORS)
236
+ - **Notice:** [NOTICE](NOTICE)
237
+ - **Security:** [SECURITY.md](SECURITY.md)
238
+
239
+ Third-party runtime code: none in the published core (framework peers are optional and not bundled). DevDependencies (TypeScript, tsup, React/Vue/Angular for builds) are not shipped in the npm tarball.
package/SECURITY.md ADDED
@@ -0,0 +1,21 @@
1
+ # Security Policy
2
+
3
+ ## Supported versions
4
+
5
+ | Version | Supported |
6
+ |---|---|
7
+ | 1.x | Yes |
8
+
9
+ ## Reporting a vulnerability
10
+
11
+ Please report security issues privately to **mail.polurus@gmail.com**.
12
+
13
+ Include:
14
+ - Package name and version
15
+ - Description of the issue
16
+ - Steps to reproduce (if possible)
17
+ - Impact assessment
18
+
19
+ Do not open a public GitHub issue for security reports until a fix is available or we confirm disclosure is safe.
20
+
21
+ We aim to acknowledge reports within 7 days.
@@ -0,0 +1,43 @@
1
+ # Accessibility notes
2
+
3
+ Author: **Subrahmanyam Poluru** ·
4
+
5
+ ## Semantics
6
+
7
+ - Root dialog uses `role="dialog"` by default
8
+ - Use `role="alertdialog"` for destructive or blocking confirms
9
+ - Always sets `aria-modal="true"`
10
+ - Prefers `aria-labelledby` (title) and `aria-describedby` (description)
11
+ - Falls back to `aria-label` when no visible title is provided
12
+
13
+ ## Keyboard
14
+
15
+ - `Tab` / `Shift+Tab` are trapped inside the **topmost** open modal
16
+ - `Escape` closes the topmost modal (unless `persistent` or `closeOnEsc: false`)
17
+ - Close, Cancel, and Confirm are real `<button>` elements
18
+
19
+ ## Focus management
20
+
21
+ 1. On open: focus moves to `initialFocus`, then `[autofocus]`, then the first focusable control, then the dialog itself
22
+ 2. On close: focus restores to `returnFocus` (or the previously focused element)
23
+ 3. Nested modals: only the top layer’s focus trap is active
24
+
25
+ ## Screen readers
26
+
27
+ - Title and description IDs are stable per modal instance
28
+ - Loading confirm buttons expose `aria-busy="true"`
29
+ - Close control has `aria-label="Close dialog"`
30
+ - Hidden closed roots use `hidden` + `aria-hidden="true"`
31
+
32
+ ## Motion
33
+
34
+ - Honors `prefers-reduced-motion: reduce` (skips enter/exit delays and CSS transitions)
35
+
36
+ ## Authoring checklist
37
+
38
+ - [ ] Provide a title **or** `ariaLabel`
39
+ - [ ] Use `alertdialog` for confirms that interrupt the user’s flow
40
+ - [ ] Ensure at least one focusable control inside the dialog
41
+ - [ ] Pass `returnFocus` to the trigger in Vanilla / imperative usage
42
+ - [ ] Verify stacked modals: ESC closes only the top layer
43
+ - [ ] Confirm focus returns after close with VoiceOver / NVDA
package/docs/API.md ADDED
@@ -0,0 +1,56 @@
1
+ # API Reference
2
+
3
+ Author: **Subrahmanyam Poluru** · mail.polurus@gmail.com · https://polurus.com
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @poluru-labs/universal-modal-react
9
+ npm install @poluru-labs/universal-modal-vue
10
+ npm install @poluru-labs/universal-modal-angular
11
+ npm install @poluru-labs/universal-modal-vanilla
12
+ npm install @poluru-labs/universal-modal-web-component
13
+ ```
14
+
15
+ ## Imports
16
+
17
+ ```ts
18
+ import { ModalEngine, ModalRenderer } from '@poluru-labs/universal-modal';
19
+ import { createModal, confirmModal } from '@poluru-labs/universal-modal-vanilla';
20
+ import { UniversalModal } from '@poluru-labs/universal-modal-react';
21
+ import { UniversalModal } from '@poluru-labs/universal-modal-vue';
22
+ import { UniversalModalComponent } from '@poluru-labs/universal-modal-angular';
23
+ import '@poluru-labs/universal-modal-web-component';
24
+ import '@poluru-labs/universal-modal/styles.css';
25
+ ```
26
+
27
+ ## Options (shared)
28
+
29
+ ```ts
30
+ type ModalOptions = {
31
+ id?: string;
32
+ size?: 'sm' | 'md' | 'lg' | 'xl' | 'fullscreen';
33
+ placement?: 'center' | 'top' | 'bottom' | 'left' | 'right';
34
+ title?: string;
35
+ description?: string;
36
+ persistent?: boolean;
37
+ closeOnEsc?: boolean;
38
+ closeOnBackdrop?: boolean;
39
+ showCloseIcon?: boolean;
40
+ role?: 'dialog' | 'alertdialog';
41
+ confirmText?: string;
42
+ cancelText?: string;
43
+ hideActions?: boolean;
44
+ zIndexBase?: number;
45
+ onConfirm?: () => void | Promise<void>;
46
+ onCancel?: () => void | Promise<void>;
47
+ beforeOpen?: () => boolean | void | Promise<boolean | void>;
48
+ afterOpen?: () => void;
49
+ beforeClose?: (reason: CloseReason) => boolean | void | Promise<boolean | void>;
50
+ afterClose?: (reason: CloseReason) => void;
51
+ };
52
+ ```
53
+
54
+ ## Events
55
+
56
+ `beforeOpen` · `afterOpen` · `beforeClose` · `afterClose` · `confirm` · `cancel` · `openChange` · `loadingChange`
@@ -0,0 +1,65 @@
1
+ # Theming & brand colors
2
+
3
+ Default brand color: `#0d47a1`.
4
+
5
+ ## 1. CSS variables (recommended)
6
+
7
+ Import styles, then override on `:root`, `[data-um-theme]`, or a parent:
8
+
9
+ ```css
10
+ :root {
11
+ --um-color-brand: #0d47a1;
12
+ --um-color-brand-fg: #ffffff;
13
+
14
+ --um-btn-confirm-bg: var(--um-color-brand);
15
+ --um-btn-confirm-fg: var(--um-color-brand-fg);
16
+ --um-btn-confirm-border: var(--um-color-brand);
17
+
18
+ --um-btn-cancel-bg: #ffffff;
19
+ --um-btn-cancel-fg: #111827;
20
+ --um-btn-cancel-border: #e5e7eb;
21
+ }
22
+ ```
23
+
24
+ ### Brand tokens
25
+
26
+ | Variable | Purpose |
27
+ |---|---|
28
+ | `--um-color-brand` | Primary brand color |
29
+ | `--um-color-brand-fg` | Text/icon on brand surfaces |
30
+ | `--um-color-accent` | Alias of brand (legacy) |
31
+ | `--um-color-accent-fg` | Alias of brand foreground |
32
+
33
+ ### Button tokens
34
+
35
+ | Variable | Purpose |
36
+ |---|---|
37
+ | `--um-btn-confirm-bg` | Confirm button background |
38
+ | `--um-btn-confirm-fg` | Confirm button text |
39
+ | `--um-btn-confirm-border` | Confirm button border |
40
+ | `--um-btn-cancel-bg` | Cancel button background |
41
+ | `--um-btn-cancel-fg` | Cancel button text |
42
+ | `--um-btn-cancel-border` | Cancel button border |
43
+
44
+ ## 2. Per-modal `theme` option
45
+
46
+ ```ts
47
+ createModal({
48
+ title: 'Delete?',
49
+ theme: {
50
+ brand: '#0d47a1',
51
+ brandForeground: '#ffffff',
52
+ confirmBg: '#b91c1c',
53
+ confirmFg: '#ffffff',
54
+ confirmBorder: '#b91c1c',
55
+ cancelBg: '#ffffff',
56
+ cancelFg: '#111827',
57
+ cancelBorder: '#e5e7eb',
58
+ cssVars: {
59
+ 'um-radius': '16px',
60
+ },
61
+ },
62
+ });
63
+ ```
64
+
65
+ React / Vue / Angular accept the same `theme` prop/input.
@@ -0,0 +1,137 @@
1
+ <main class="demo-page">
2
+ <header class="demo-header">
3
+ <p class="demo-kicker">Angular</p>
4
+ <h1>Modal samples</h1>
5
+ <p>
6
+ Standalone <code>UniversalModalComponent</code> patterns — drop this into an Angular
7
+ 16+ app with the package import.
8
+ </p>
9
+ </header>
10
+
11
+ <section class="demo-grid">
12
+ <article class="demo-card">
13
+ <h2>1. Archive confirm</h2>
14
+ <p>Alertdialog with async confirm handler.</p>
15
+ <button type="button" (click)="open('archive')">Archive workspace</button>
16
+ </article>
17
+ <article class="demo-card">
18
+ <h2>2. Danger delete</h2>
19
+ <p>Custom red confirm theme.</p>
20
+ <button type="button" (click)="open('danger')">Delete webhook</button>
21
+ </article>
22
+ <article class="demo-card">
23
+ <h2>3. Right drawer</h2>
24
+ <p>Large form panel.</p>
25
+ <button type="button" (click)="open('drawer')">Edit workspace</button>
26
+ </article>
27
+ <article class="demo-card">
28
+ <h2>4. Top notice</h2>
29
+ <p>No footer actions.</p>
30
+ <button type="button" (click)="open('top')">Show notice</button>
31
+ </article>
32
+ <article class="demo-card">
33
+ <h2>5. Fullscreen</h2>
34
+ <p>Checklist / walkthrough layout.</p>
35
+ <button type="button" (click)="open('fullscreen')">Open checklist</button>
36
+ </article>
37
+ <article class="demo-card">
38
+ <h2>6. Persistent</h2>
39
+ <p>Blocks ESC and backdrop.</p>
40
+ <button type="button" (click)="open('persistent')">Accept DPA</button>
41
+ </article>
42
+ <article class="demo-card">
43
+ <h2>7. Brand theme</h2>
44
+ <p>Per-dialog brand override.</p>
45
+ <button type="button" (click)="open('brand')">Open branded</button>
46
+ </article>
47
+ </section>
48
+
49
+ <universal-modal
50
+ [open]="sample === 'archive'"
51
+ title="Archive workspace?"
52
+ description="Members keep read-only access."
53
+ role="alertdialog"
54
+ confirmText="Archive"
55
+ cancelText="Cancel"
56
+ (openChange)="onOpenChange($event)"
57
+ (confirm)="onArchive()"
58
+ >
59
+ <label class="demo-field">
60
+ Workspace
61
+ <input [value]="workspace" (input)="workspace = $any($event.target).value" />
62
+ </label>
63
+ </universal-modal>
64
+
65
+ <universal-modal
66
+ [open]="sample === 'danger'"
67
+ title="Delete webhook?"
68
+ role="alertdialog"
69
+ confirmText="Delete"
70
+ [theme]="dangerTheme"
71
+ (openChange)="onOpenChange($event)"
72
+ >
73
+ <p>Deliveries will stop immediately.</p>
74
+ </universal-modal>
75
+
76
+ <universal-modal
77
+ [open]="sample === 'drawer'"
78
+ title="Workspace settings"
79
+ size="lg"
80
+ placement="right"
81
+ confirmText="Save"
82
+ (openChange)="onOpenChange($event)"
83
+ >
84
+ <div class="demo-stack">
85
+ <label class="demo-field">Name <input [value]="workspace" /></label>
86
+ <label class="demo-field">Slug <input value="poluru-labs" /></label>
87
+ </div>
88
+ </universal-modal>
89
+
90
+ <universal-modal
91
+ [open]="sample === 'top'"
92
+ title="Deploy in progress"
93
+ placement="top"
94
+ [hideActions]="true"
95
+ (openChange)="onOpenChange($event)"
96
+ >
97
+ <p>You can keep browsing while the pipeline finishes.</p>
98
+ </universal-modal>
99
+
100
+ <universal-modal
101
+ [open]="sample === 'fullscreen'"
102
+ title="Launch checklist"
103
+ size="fullscreen"
104
+ confirmText="Mark complete"
105
+ (openChange)="onOpenChange($event)"
106
+ >
107
+ <div class="demo-stack">
108
+ <p>✓ Domain verified</p>
109
+ <p>✓ Billing method added</p>
110
+ <p>✓ First project created</p>
111
+ </div>
112
+ </universal-modal>
113
+
114
+ <universal-modal
115
+ [open]="sample === 'persistent'"
116
+ title="Data processing agreement"
117
+ role="alertdialog"
118
+ [persistent]="true"
119
+ [showCloseIcon]="false"
120
+ confirmText="Accept"
121
+ cancelText="Decline"
122
+ (openChange)="onOpenChange($event)"
123
+ >
124
+ <p>Outside dismiss is disabled until you choose an action.</p>
125
+ </universal-modal>
126
+
127
+ <universal-modal
128
+ [open]="sample === 'brand'"
129
+ title="Branded dialog"
130
+ description="theme input maps to CSS variables."
131
+ [theme]="brandTheme"
132
+ confirmText="Apply"
133
+ (openChange)="onOpenChange($event)"
134
+ >
135
+ <p>Use this for one-off brand treatments.</p>
136
+ </universal-modal>
137
+ </main>
@@ -0,0 +1,53 @@
1
+ import { Component } from '@angular/core';
2
+ import {
3
+ UniversalModalComponent,
4
+ type ModalTheme,
5
+ } from '@poluru-labs/universal-modal-angular';
6
+
7
+ type Sample =
8
+ | 'archive'
9
+ | 'danger'
10
+ | 'drawer'
11
+ | 'top'
12
+ | 'fullscreen'
13
+ | 'persistent'
14
+ | 'brand'
15
+ | null;
16
+
17
+ @Component({
18
+ selector: 'app-root',
19
+ standalone: true,
20
+ imports: [UniversalModalComponent],
21
+ templateUrl: './app.component.html',
22
+ styleUrl: './styles.css',
23
+ })
24
+ export class AppComponent {
25
+ sample: Sample = null;
26
+ workspace = 'Poluru Labs';
27
+
28
+ readonly dangerTheme: ModalTheme = {
29
+ confirmBg: '#b91c1c',
30
+ confirmFg: '#ffffff',
31
+ confirmBorder: '#b91c1c',
32
+ };
33
+
34
+ readonly brandTheme: ModalTheme = {
35
+ brand: '#0d47a1',
36
+ brandForeground: '#ffffff',
37
+ confirmBg: '#0d47a1',
38
+ confirmFg: '#ffffff',
39
+ };
40
+
41
+ open(sample: Exclude<Sample, null>): void {
42
+ this.sample = sample;
43
+ }
44
+
45
+ onOpenChange(open: boolean): void {
46
+ if (!open) this.sample = null;
47
+ }
48
+
49
+ async onArchive(): Promise<void> {
50
+ await new Promise((r) => setTimeout(r, 700));
51
+ console.log('archived', this.workspace);
52
+ }
53
+ }