minora 0.1.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.
@@ -0,0 +1,401 @@
1
+ /* ============================================================
2
+ ELEGANT MINIMALIST UI KIT — Tooltip & Popover Components
3
+ tooltip.css
4
+
5
+ ANATOMY — TOOLTIP
6
+ ─────────────────────────────────────────────────────────────
7
+ Structure (injected to body by JS):
8
+ <div class="tooltip tooltip-top" role="tooltip" style="position:fixed;">
9
+ <span class="tooltip-arrow"></span>
10
+ <span class="tooltip-content">Tooltip text</span>
11
+ </div>
12
+
13
+ Class layers:
14
+ 1. .tooltip → Base container, fixed position
15
+ 2. .tooltip-{position} → Arrow placement (top/bottom/left/right variants)
16
+ 3. .tooltip-light → Light bg variant
17
+ 4. .tooltip-rich → With bold title + body text
18
+ 5. .tooltip-icon → With leading icon
19
+ 6. .tooltip-interactive → With action button, stays on hover
20
+ ─────────────────────────────────────────────────────────────
21
+
22
+ ANATOMY — POPOVER
23
+ ─────────────────────────────────────────────────────────────
24
+ Structure:
25
+ <div class="popover" role="dialog" style="position:fixed;">
26
+ <div class="popover-header">Title + close</div>
27
+ <div class="popover-body">Freeform content</div>
28
+ <div class="popover-footer">Actions</div>
29
+ </div>
30
+ ─────────────────────────────────────────────────────────────
31
+ ============================================================ */
32
+
33
+ /* ═══════════════════════════════════════════════════════════
34
+ TOOLTIP — Base
35
+ ═══════════════════════════════════════════════════════════ */
36
+
37
+ .tooltip {
38
+ position: fixed;
39
+ z-index: var(--z-tooltip);
40
+ max-width: var(--tooltip-max-width);
41
+ font-family: var(--font-sans);
42
+ font-size: var(--tooltip-font-size);
43
+ line-height: var(--leading-tight);
44
+ padding: var(--tooltip-padding);
45
+ background-color: var(--tooltip-bg);
46
+ color: var(--tooltip-color);
47
+ border-radius: var(--tooltip-border-radius);
48
+ box-shadow: var(--tooltip-shadow);
49
+ opacity: 0;
50
+ visibility: hidden;
51
+ pointer-events: none;
52
+ transform: scale(0.95);
53
+ transition: opacity var(--tooltip-out-duration) var(--ease-in-out),
54
+ transform var(--tooltip-out-duration) var(--ease-in-out),
55
+ visibility var(--tooltip-out-duration) var(--ease-in-out);
56
+ word-wrap: break-word;
57
+ }
58
+
59
+ .tooltip.is-visible {
60
+ opacity: 1;
61
+ visibility: visible;
62
+ pointer-events: auto;
63
+ transform: scale(1);
64
+ transition: opacity var(--tooltip-in-duration) var(--ease-out),
65
+ transform var(--tooltip-in-duration) var(--ease-out),
66
+ visibility var(--tooltip-in-duration) var(--ease-out);
67
+ }
68
+
69
+ /* ═══════════════════════════════════════════════════════════
70
+ TOOLTIP — Arrow
71
+ ═══════════════════════════════════════════════════════════ */
72
+
73
+ .tooltip-arrow {
74
+ position: absolute;
75
+ width: var(--tooltip-arrow-size);
76
+ height: var(--tooltip-arrow-size);
77
+ background-color: var(--tooltip-bg);
78
+ transform: rotate(45deg);
79
+ }
80
+
81
+ /* Arrow positions */
82
+ .tooltip-top .tooltip-arrow,
83
+ .tooltip-top-start .tooltip-arrow,
84
+ .tooltip-top-end .tooltip-arrow {
85
+ bottom: calc(var(--tooltip-arrow-size) * -1 + 1px);
86
+ }
87
+
88
+ .tooltip-bottom .tooltip-arrow,
89
+ .tooltip-bottom-start .tooltip-arrow,
90
+ .tooltip-bottom-end .tooltip-arrow {
91
+ top: calc(var(--tooltip-arrow-size) * -1 + 1px);
92
+ }
93
+
94
+ .tooltip-left .tooltip-arrow,
95
+ .tooltip-left-start .tooltip-arrow,
96
+ .tooltip-left-end .tooltip-arrow {
97
+ right: calc(var(--tooltip-arrow-size) * -1 + 1px);
98
+ }
99
+
100
+ .tooltip-right .tooltip-arrow,
101
+ .tooltip-right-start .tooltip-arrow,
102
+ .tooltip-right-end .tooltip-arrow {
103
+ left: calc(var(--tooltip-arrow-size) * -1 + 1px);
104
+ }
105
+
106
+ /* ═══════════════════════════════════════════════════════════
107
+ TOOLTIP — Slide-in directions
108
+ Different translate based on position for smooth entrance
109
+ ═══════════════════════════════════════════════════════════ */
110
+
111
+ .tooltip-top.is-visible,
112
+ .tooltip-top-start.is-visible,
113
+ .tooltip-top-end.is-visible {
114
+ transform: translateY(0) scale(1);
115
+ }
116
+
117
+ .tooltip-top:not(.is-visible),
118
+ .tooltip-top-start:not(.is-visible),
119
+ .tooltip-top-end:not(.is-visible) {
120
+ transform: translateY(4px) scale(0.95);
121
+ }
122
+
123
+ .tooltip-bottom.is-visible,
124
+ .tooltip-bottom-start.is-visible,
125
+ .tooltip-bottom-end.is-visible {
126
+ transform: translateY(0) scale(1);
127
+ }
128
+
129
+ .tooltip-bottom:not(.is-visible),
130
+ .tooltip-bottom-start:not(.is-visible),
131
+ .tooltip-bottom-end:not(.is-visible) {
132
+ transform: translateY(-4px) scale(0.95);
133
+ }
134
+
135
+ .tooltip-left.is-visible,
136
+ .tooltip-left-start.is-visible,
137
+ .tooltip-left-end.is-visible {
138
+ transform: translateX(0) scale(1);
139
+ }
140
+
141
+ .tooltip-left:not(.is-visible),
142
+ .tooltip-left-start:not(.is-visible),
143
+ .tooltip-left-end:not(.is-visible) {
144
+ transform: translateX(4px) scale(0.95);
145
+ }
146
+
147
+ .tooltip-right.is-visible,
148
+ .tooltip-right-start.is-visible,
149
+ .tooltip-right-end.is-visible {
150
+ transform: translateX(0) scale(1);
151
+ }
152
+
153
+ .tooltip-right:not(.is-visible),
154
+ .tooltip-right-start:not(.is-visible),
155
+ .tooltip-right-end:not(.is-visible) {
156
+ transform: translateX(-4px) scale(0.95);
157
+ }
158
+
159
+ /* ═══════════════════════════════════════════════════════════
160
+ TOOLTIP — Variant: Light
161
+ ═══════════════════════════════════════════════════════════ */
162
+
163
+ .tooltip-light {
164
+ background-color: var(--tooltip-light-bg);
165
+ color: var(--tooltip-light-color);
166
+ border: var(--border-width) solid var(--tooltip-light-border);
167
+ }
168
+
169
+ .tooltip-light .tooltip-arrow {
170
+ background-color: var(--tooltip-light-bg);
171
+ }
172
+
173
+ /* ═══════════════════════════════════════════════════════════
174
+ TOOLTIP — Variant: Rich (title + body)
175
+ ═══════════════════════════════════════════════════════════ */
176
+
177
+ .tooltip-rich {
178
+ padding: var(--space-3);
179
+ max-width: var(--tooltip-rich-max-width);
180
+ }
181
+
182
+ .tooltip-rich .tooltip-title {
183
+ font-family: var(--font-sans);
184
+ font-size: var(--text-sm);
185
+ font-weight: var(--font-semibold);
186
+ color: var(--color-neutral-50);
187
+ margin-bottom: var(--space-0-5);
188
+ display: block;
189
+ }
190
+
191
+ .tooltip-rich .tooltip-body {
192
+ font-size: var(--text-xs);
193
+ color: var(--color-neutral-300);
194
+ line-height: var(--leading-snug);
195
+ }
196
+
197
+ .tooltip-light.tooltip-rich .tooltip-title {
198
+ color: var(--color-neutral-800);
199
+ }
200
+
201
+ .tooltip-light.tooltip-rich .tooltip-body {
202
+ color: var(--color-neutral-600);
203
+ }
204
+
205
+ /* ═══════════════════════════════════════════════════════════
206
+ TOOLTIP — Variant: With Icon
207
+ ═══════════════════════════════════════════════════════════ */
208
+
209
+ .tooltip-with-icon {
210
+ display: flex;
211
+ align-items: flex-start;
212
+ gap: var(--space-2);
213
+ }
214
+
215
+ .tooltip-icon-img {
216
+ flex-shrink: 0;
217
+ width: var(--space-4);
218
+ height: var(--space-4);
219
+ color: var(--color-neutral-400);
220
+ }
221
+
222
+ /* ═══════════════════════════════════════════════════════════
223
+ TOOLTIP — Variant: Interactive
224
+ Stays visible when mouse enters tooltip content
225
+ ═══════════════════════════════════════════════════════════ */
226
+
227
+ .tooltip-interactive {
228
+ pointer-events: auto;
229
+ }
230
+
231
+ .tooltip-interactive .tooltip-actions {
232
+ display: flex;
233
+ align-items: center;
234
+ gap: var(--space-2);
235
+ margin-top: var(--space-2-5);
236
+ padding-top: var(--space-2);
237
+ border-top: var(--border-width) solid rgba(255, 255, 255, 0.1);
238
+ }
239
+
240
+ .tooltip-light.tooltip-interactive .tooltip-actions {
241
+ border-top-color: var(--color-neutral-200);
242
+ }
243
+
244
+ .tooltip-action-btn {
245
+ font-family: var(--font-sans);
246
+ font-size: var(--text-xs);
247
+ font-weight: var(--font-medium);
248
+ padding: var(--space-0-5) var(--space-2);
249
+ border-radius: var(--radius-sm);
250
+ border: none;
251
+ cursor: pointer;
252
+ background-color: rgba(255, 255, 255, 0.15);
253
+ color: var(--color-neutral-50);
254
+ transition: background-color var(--duration-fast) var(--ease-in-out);
255
+ }
256
+
257
+ .tooltip-action-btn:hover {
258
+ background-color: rgba(255, 255, 255, 0.25);
259
+ }
260
+
261
+ .tooltip-light .tooltip-action-btn {
262
+ background-color: var(--color-neutral-100);
263
+ color: var(--color-neutral-700);
264
+ }
265
+
266
+ .tooltip-light .tooltip-action-btn:hover {
267
+ background-color: var(--color-neutral-200);
268
+ }
269
+
270
+ /* ═══════════════════════════════════════════════════════════
271
+ TRIGGER ELEMENTS — Elements with tooltips
272
+ ═══════════════════════════════════════════════════════════ */
273
+
274
+ [data-tooltip] {
275
+ position: relative;
276
+ }
277
+
278
+ /* ═══════════════════════════════════════════════════════════
279
+ POPOVER — Container
280
+ ═══════════════════════════════════════════════════════════ */
281
+
282
+ .popover {
283
+ position: fixed;
284
+ z-index: var(--z-dropdown);
285
+ min-width: var(--popover-min-width);
286
+ max-width: var(--popover-max-width);
287
+ max-height: calc(100vh - var(--space-8));
288
+ background-color: var(--popover-bg);
289
+ color: var(--popover-color);
290
+ border: var(--border-width) solid var(--popover-border);
291
+ border-radius: var(--popover-border-radius);
292
+ box-shadow: var(--popover-shadow);
293
+ overflow: hidden;
294
+ display: flex;
295
+ flex-direction: column;
296
+ opacity: 0;
297
+ visibility: hidden;
298
+ transform: scale(0.96) translateY(-4px);
299
+ transition: opacity var(--tooltip-in-duration) var(--ease-out),
300
+ transform var(--tooltip-in-duration) var(--ease-out),
301
+ visibility var(--tooltip-in-duration) var(--ease-out);
302
+ }
303
+
304
+ .popover.is-visible {
305
+ opacity: 1;
306
+ visibility: visible;
307
+ transform: scale(1) translateY(0);
308
+ }
309
+
310
+ .popover.is-hidden {
311
+ opacity: 0;
312
+ visibility: hidden;
313
+ transform: scale(0.96) translateY(-4px);
314
+ transition: opacity var(--tooltip-out-duration) var(--ease-in-out),
315
+ transform var(--tooltip-out-duration) var(--ease-in-out),
316
+ visibility var(--tooltip-out-duration) var(--ease-in-out);
317
+ }
318
+
319
+ /* ═══════════════════════════════════════════════════════════
320
+ POPOVER — Header
321
+ ═══════════════════════════════════════════════════════════ */
322
+
323
+ .popover-header {
324
+ display: flex;
325
+ align-items: center;
326
+ justify-content: space-between;
327
+ gap: var(--space-4);
328
+ padding: var(--space-4);
329
+ border-bottom: var(--border-width) solid var(--color-neutral-100);
330
+ flex-shrink: 0;
331
+ }
332
+
333
+ .popover-title {
334
+ font-family: var(--font-sans);
335
+ font-size: var(--text-sm);
336
+ font-weight: var(--font-semibold);
337
+ color: var(--color-neutral-900);
338
+ margin: 0;
339
+ }
340
+
341
+ .popover-close {
342
+ display: inline-flex;
343
+ align-items: center;
344
+ justify-content: center;
345
+ width: var(--space-6);
346
+ height: var(--space-6);
347
+ flex-shrink: 0;
348
+ border: none;
349
+ border-radius: var(--radius-sm);
350
+ background: transparent;
351
+ color: var(--color-neutral-400);
352
+ cursor: pointer;
353
+ padding: 0;
354
+ transition: color var(--duration-fast) var(--ease-in-out),
355
+ background-color var(--duration-fast) var(--ease-in-out);
356
+ }
357
+
358
+ .popover-close:hover {
359
+ color: var(--color-neutral-700);
360
+ background-color: var(--color-neutral-100);
361
+ }
362
+
363
+ .popover-close svg {
364
+ width: var(--space-3-5);
365
+ height: var(--space-3-5);
366
+ }
367
+
368
+ /* ═══════════════════════════════════════════════════════════
369
+ POPOVER — Body
370
+ ═══════════════════════════════════════════════════════════ */
371
+
372
+ .popover-body {
373
+ padding: var(--space-4);
374
+ overflow-y: auto;
375
+ flex: 1;
376
+ }
377
+
378
+ .popover-body p {
379
+ font-size: var(--text-sm);
380
+ color: var(--color-neutral-600);
381
+ line-height: var(--leading-relaxed);
382
+ margin: 0 0 var(--space-3);
383
+ }
384
+
385
+ .popover-body p:last-child {
386
+ margin-bottom: 0;
387
+ }
388
+
389
+ /* ═══════════════════════════════════════════════════════════
390
+ POPOVER — Footer
391
+ ═══════════════════════════════════════════════════════════ */
392
+
393
+ .popover-footer {
394
+ display: flex;
395
+ align-items: center;
396
+ justify-content: flex-end;
397
+ gap: var(--space-3);
398
+ padding: var(--space-3) var(--space-4);
399
+ border-top: var(--border-width) solid var(--color-neutral-100);
400
+ flex-shrink: 0;
401
+ }
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Minora — Modal Manager
3
+ * ──────────────────────
4
+ * Full-featured modal system with focus trap, body scroll lock,
5
+ * nested stacking, and backdrop/escape close.
6
+ *
7
+ * Usage:
8
+ * ModalManager.open('modal-id');
9
+ * ModalManager.close('modal-id');
10
+ */
11
+ (function() {
12
+ 'use strict';
13
+
14
+ var ModalManager = (function() {
15
+ var openModals = [];
16
+ var previouslyFocused = [];
17
+ var scrollBarWidth = 0;
18
+
19
+ function getScrollBarWidth() {
20
+ var outer = document.createElement('div');
21
+ outer.style.cssText = 'position:absolute;top:-9999px;width:50px;height:50px;overflow:hidden;';
22
+ document.body.appendChild(outer);
23
+ var wNoScroll = outer.offsetWidth;
24
+ outer.style.overflow = 'scroll';
25
+ var inner = document.createElement('div');
26
+ inner.style.cssText = 'width:100%;height:50px;';
27
+ outer.appendChild(inner);
28
+ var wScroll = inner.offsetWidth;
29
+ document.body.removeChild(outer);
30
+ return wNoScroll - wScroll;
31
+ }
32
+
33
+ function lockBodyScroll() {
34
+ if (openModals.length === 0) {
35
+ scrollBarWidth = getScrollBarWidth();
36
+ document.body.style.paddingRight = scrollBarWidth + 'px';
37
+ document.body.classList.add('is-modal-open');
38
+ }
39
+ }
40
+
41
+ function unlockBodyScroll() {
42
+ if (openModals.length === 0) {
43
+ document.body.style.paddingRight = '';
44
+ document.body.classList.remove('is-modal-open');
45
+ }
46
+ }
47
+
48
+ function getFocusable(modal) {
49
+ var selectors = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
50
+ return Array.from(modal.querySelectorAll(selectors)).filter(function(el) {
51
+ return el.offsetParent !== null;
52
+ });
53
+ }
54
+
55
+ function trapFocus(e, overlay) {
56
+ if (e.key !== 'Tab') return;
57
+ var focusable = getFocusable(overlay);
58
+ if (focusable.length === 0) return;
59
+
60
+ var first = focusable[0];
61
+ var last = focusable[focusable.length - 1];
62
+
63
+ if (e.shiftKey) {
64
+ if (document.activeElement === first) { e.preventDefault(); last.focus(); }
65
+ } else {
66
+ if (document.activeElement === last) { e.preventDefault(); first.focus(); }
67
+ }
68
+ }
69
+
70
+ function open(id) {
71
+ var overlay = document.getElementById(id);
72
+ if (!overlay) return;
73
+
74
+ previouslyFocused.push(document.activeElement);
75
+ lockBodyScroll();
76
+ openModals.push(overlay);
77
+
78
+ overlay.classList.add('is-open');
79
+ overlay.setAttribute('aria-hidden', 'false');
80
+
81
+ overlay._focusHandler = function(e) { trapFocus(e, overlay); };
82
+ overlay.addEventListener('keydown', overlay._focusHandler);
83
+
84
+ overlay._backdropHandler = function(e) {
85
+ if (e.target === overlay) close(id);
86
+ };
87
+ overlay.addEventListener('click', overlay._backdropHandler);
88
+
89
+ overlay._escapeHandler = function(e) {
90
+ if (e.key === 'Escape' && openModals[openModals.length - 1] === overlay) close(id);
91
+ };
92
+ document.addEventListener('keydown', overlay._escapeHandler);
93
+
94
+ requestAnimationFrame(function() {
95
+ var focusable = getFocusable(overlay);
96
+ if (focusable.length > 0) focusable[0].focus();
97
+ });
98
+ }
99
+
100
+ function close(id) {
101
+ var overlay = document.getElementById(id);
102
+ if (!overlay) return;
103
+
104
+ overlay.classList.remove('is-open');
105
+ overlay.setAttribute('aria-hidden', 'true');
106
+
107
+ if (overlay._focusHandler) overlay.removeEventListener('keydown', overlay._focusHandler);
108
+ if (overlay._backdropHandler) overlay.removeEventListener('click', overlay._backdropHandler);
109
+ if (overlay._escapeHandler) document.removeEventListener('keydown', overlay._escapeHandler);
110
+
111
+ openModals = openModals.filter(function(m) { return m !== overlay; });
112
+ unlockBodyScroll();
113
+
114
+ var prev = previouslyFocused.pop();
115
+ if (prev && prev.focus) setTimeout(function() { prev.focus(); }, 50);
116
+ }
117
+
118
+ return { open: open, close: close };
119
+ })();
120
+
121
+ window.ModalManager = ModalManager;
122
+
123
+ // Initialize all overlays as hidden
124
+ document.querySelectorAll('.modal-overlay').forEach(function(el) {
125
+ el.setAttribute('aria-hidden', 'true');
126
+ });
127
+ })();