mnfst 0.5.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +11 -0
- package/README.md +58 -0
- package/dist/manifest.accordion.css +81 -0
- package/dist/manifest.appwrite.auth.js +6247 -0
- package/dist/manifest.appwrite.data.js +1586 -0
- package/dist/manifest.appwrite.presence.js +1845 -0
- package/dist/manifest.avatar.css +113 -0
- package/dist/manifest.button.css +79 -0
- package/dist/manifest.checkbox.css +58 -0
- package/dist/manifest.code.css +453 -0
- package/dist/manifest.code.js +958 -0
- package/dist/manifest.code.min.css +1 -0
- package/dist/manifest.components.js +737 -0
- package/dist/manifest.css +3124 -0
- package/dist/manifest.data.js +11413 -0
- package/dist/manifest.dialog.css +130 -0
- package/dist/manifest.divider.css +77 -0
- package/dist/manifest.dropdown.css +278 -0
- package/dist/manifest.dropdowns.js +378 -0
- package/dist/manifest.form.css +169 -0
- package/dist/manifest.icons.js +161 -0
- package/dist/manifest.input.css +129 -0
- package/dist/manifest.js +302 -0
- package/dist/manifest.localization.js +571 -0
- package/dist/manifest.markdown.js +738 -0
- package/dist/manifest.min.css +1 -0
- package/dist/manifest.radio.css +38 -0
- package/dist/manifest.resize.css +233 -0
- package/dist/manifest.resize.js +442 -0
- package/dist/manifest.router.js +1207 -0
- package/dist/manifest.sidebar.css +102 -0
- package/dist/manifest.slides.css +80 -0
- package/dist/manifest.slides.js +173 -0
- package/dist/manifest.switch.css +44 -0
- package/dist/manifest.table.css +74 -0
- package/dist/manifest.tabs.js +273 -0
- package/dist/manifest.tailwind.js +578 -0
- package/dist/manifest.theme.css +119 -0
- package/dist/manifest.themes.js +109 -0
- package/dist/manifest.toast.css +92 -0
- package/dist/manifest.toasts.js +285 -0
- package/dist/manifest.tooltip.css +156 -0
- package/dist/manifest.tooltips.js +331 -0
- package/dist/manifest.typography.css +341 -0
- package/dist/manifest.utilities.css +399 -0
- package/dist/manifest.utilities.js +3197 -0
- package/package.json +63 -0
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
/* Manifest Dropdowns */
|
|
2
|
+
|
|
3
|
+
// Initialize plugin when either DOM is ready or Alpine is ready
|
|
4
|
+
function initializeDropdownPlugin() {
|
|
5
|
+
// Ensure Alpine.js context exists for directives to work
|
|
6
|
+
function ensureAlpineContext() {
|
|
7
|
+
const body = document.body;
|
|
8
|
+
if (!body.hasAttribute('x-data')) {
|
|
9
|
+
body.setAttribute('x-data', '{}');
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Helper to register directives
|
|
14
|
+
function registerDirective(name, handler) {
|
|
15
|
+
Alpine.directive(name, handler);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Check if a menu element is nested (triggered from within another menu)
|
|
19
|
+
const isNestedMenu = (menu) => {
|
|
20
|
+
// Find all buttons/elements that target this menu
|
|
21
|
+
const menuId = menu.id;
|
|
22
|
+
const triggers = document.querySelectorAll(`[popovertarget="${menuId}"], [x-dropdown="${menuId}"], [x-dropdown*="${menuId}"]`);
|
|
23
|
+
|
|
24
|
+
// Check if any trigger is inside another popover menu
|
|
25
|
+
for (const trigger of triggers) {
|
|
26
|
+
let parent = trigger.parentElement;
|
|
27
|
+
while (parent) {
|
|
28
|
+
if (parent.tagName === 'MENU' && parent.hasAttribute('popover')) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
parent = parent.parentElement;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Ensure Alpine.js context exists
|
|
38
|
+
ensureAlpineContext();
|
|
39
|
+
|
|
40
|
+
// Register dropdown directive
|
|
41
|
+
registerDirective('dropdown', (el, { modifiers, expression }, { effect, evaluateLater }) => {
|
|
42
|
+
let menu;
|
|
43
|
+
|
|
44
|
+
// Shared hover state for all dropdown types
|
|
45
|
+
let hoverTimeout;
|
|
46
|
+
let autoCloseTimeout;
|
|
47
|
+
let startAutoCloseTimer;
|
|
48
|
+
|
|
49
|
+
effect(() => {
|
|
50
|
+
|
|
51
|
+
// Defer processing to ensure Alpine is fully ready
|
|
52
|
+
setTimeout(() => {
|
|
53
|
+
if (!window.Alpine) {
|
|
54
|
+
console.warn('[Manifest] Alpine not available for dropdown processing');
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Generate a unique anchor code for positioning
|
|
59
|
+
const anchorCode = Math.random().toString(36).substr(2, 9);
|
|
60
|
+
|
|
61
|
+
// Evaluate the expression to get the actual menu ID
|
|
62
|
+
let dropdownId;
|
|
63
|
+
if (expression) {
|
|
64
|
+
// Check if expression contains template literals or is a static string
|
|
65
|
+
if (expression.includes('${') || expression.includes('`')) {
|
|
66
|
+
// Use evaluateLater for dynamic expressions
|
|
67
|
+
const evaluator = evaluateLater(expression);
|
|
68
|
+
evaluator(value => {
|
|
69
|
+
dropdownId = value;
|
|
70
|
+
});
|
|
71
|
+
} else {
|
|
72
|
+
// Static string - use as-is
|
|
73
|
+
dropdownId = expression;
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
dropdownId = `dropdown-${anchorCode}`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Check if expression refers to a template ID
|
|
80
|
+
if (dropdownId && document.getElementById(dropdownId)?.tagName === 'TEMPLATE') {
|
|
81
|
+
// Clone template content and generate unique ID
|
|
82
|
+
const template = document.getElementById(dropdownId);
|
|
83
|
+
menu = template.content.cloneNode(true).firstElementChild;
|
|
84
|
+
const uniqueDropdownId = `dropdown-${anchorCode}`;
|
|
85
|
+
menu.setAttribute('id', uniqueDropdownId);
|
|
86
|
+
document.body.appendChild(menu);
|
|
87
|
+
el.setAttribute('popovertarget', uniqueDropdownId);
|
|
88
|
+
|
|
89
|
+
// Initialize Alpine on the cloned menu
|
|
90
|
+
Alpine.initTree(menu);
|
|
91
|
+
} else {
|
|
92
|
+
// Original behavior for static dropdowns
|
|
93
|
+
menu = document.getElementById(dropdownId);
|
|
94
|
+
if (!menu) {
|
|
95
|
+
// Check if this might be a component-based dropdown
|
|
96
|
+
if (window.ManifestComponentsRegistry && window.ManifestComponentsLoader) {
|
|
97
|
+
// Try to find the menu in components
|
|
98
|
+
const componentName = dropdownId; // Assume the dropdownId is the component name
|
|
99
|
+
const registry = window.ManifestComponentsRegistry;
|
|
100
|
+
|
|
101
|
+
if (registry.registered.has(componentName)) {
|
|
102
|
+
// Component exists, wait for it to be loaded
|
|
103
|
+
const waitForComponent = async () => {
|
|
104
|
+
const loader = window.ManifestComponentsLoader;
|
|
105
|
+
const content = await loader.loadComponent(componentName);
|
|
106
|
+
if (content) {
|
|
107
|
+
// Create a temporary container to parse the component
|
|
108
|
+
const tempDiv = document.createElement('div');
|
|
109
|
+
tempDiv.innerHTML = content.trim();
|
|
110
|
+
const menuElement = tempDiv.querySelector(`#${dropdownId}`);
|
|
111
|
+
|
|
112
|
+
if (menuElement) {
|
|
113
|
+
// Clone the menu and append to body
|
|
114
|
+
menu = menuElement.cloneNode(true);
|
|
115
|
+
menu.setAttribute('id', dropdownId);
|
|
116
|
+
document.body.appendChild(menu);
|
|
117
|
+
el.setAttribute('popovertarget', dropdownId);
|
|
118
|
+
|
|
119
|
+
// Initialize Alpine on the menu
|
|
120
|
+
Alpine.initTree(menu);
|
|
121
|
+
|
|
122
|
+
// Set up the dropdown after menu is ready
|
|
123
|
+
setupDropdown();
|
|
124
|
+
} else {
|
|
125
|
+
console.warn(`[Manifest] Menu with id "${dropdownId}" not found in component "${componentName}"`);
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
console.warn(`[Manifest] Failed to load component "${componentName}" for dropdown`);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// Wait for components to be ready, then try to load
|
|
133
|
+
if (window.__induxComponentsInitialized) {
|
|
134
|
+
waitForComponent();
|
|
135
|
+
} else {
|
|
136
|
+
window.addEventListener('indux:components-ready', waitForComponent);
|
|
137
|
+
}
|
|
138
|
+
return; // Exit early, setup will happen in waitForComponent
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
console.warn(`[Manifest] Dropdown menu with id "${dropdownId}" not found`);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
el.setAttribute('popovertarget', dropdownId);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Set up the dropdown
|
|
149
|
+
setupDropdown();
|
|
150
|
+
|
|
151
|
+
function setupDropdown() {
|
|
152
|
+
if (!menu) return;
|
|
153
|
+
|
|
154
|
+
// Set up popover
|
|
155
|
+
menu.setAttribute('popover', '');
|
|
156
|
+
|
|
157
|
+
// Set up anchor positioning
|
|
158
|
+
const anchorName = `--dropdown-${anchorCode}`;
|
|
159
|
+
el.style.setProperty('anchor-name', anchorName);
|
|
160
|
+
menu.style.setProperty('position-anchor', anchorName);
|
|
161
|
+
|
|
162
|
+
// Set up hover functionality after menu is ready
|
|
163
|
+
if (modifiers.includes('hover')) {
|
|
164
|
+
const handleShowPopover = () => {
|
|
165
|
+
if (menu && !menu.matches(':popover-open')) {
|
|
166
|
+
clearTimeout(hoverTimeout);
|
|
167
|
+
clearTimeout(autoCloseTimeout);
|
|
168
|
+
|
|
169
|
+
menu.showPopover();
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// Enhanced auto-close when mouse leaves both trigger and menu
|
|
174
|
+
startAutoCloseTimer = () => {
|
|
175
|
+
clearTimeout(autoCloseTimeout);
|
|
176
|
+
autoCloseTimeout = setTimeout(() => {
|
|
177
|
+
if (menu?.matches(':popover-open')) {
|
|
178
|
+
const isOverButton = el.matches(':hover');
|
|
179
|
+
const isOverMenu = menu.matches(':hover');
|
|
180
|
+
|
|
181
|
+
if (!isOverButton && !isOverMenu) {
|
|
182
|
+
menu.hidePopover();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}, 300); // Small delay to prevent accidental closes
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
el.addEventListener('mouseenter', handleShowPopover);
|
|
189
|
+
el.addEventListener('mouseleave', startAutoCloseTimer);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
// Add keyboard navigation handling
|
|
195
|
+
menu.addEventListener('keydown', (e) => {
|
|
196
|
+
// Get all navigable elements (traditional focusable + li elements)
|
|
197
|
+
const allElements = menu.querySelectorAll(
|
|
198
|
+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"]), li'
|
|
199
|
+
);
|
|
200
|
+
const currentIndex = Array.from(allElements).indexOf(e.target);
|
|
201
|
+
|
|
202
|
+
if (e.key === 'ArrowDown') {
|
|
203
|
+
e.preventDefault();
|
|
204
|
+
const nextIndex = (currentIndex + 1) % allElements.length;
|
|
205
|
+
const nextElement = allElements[nextIndex];
|
|
206
|
+
if (nextElement.tagName === 'LI') {
|
|
207
|
+
nextElement.setAttribute('tabindex', '0');
|
|
208
|
+
}
|
|
209
|
+
nextElement.focus();
|
|
210
|
+
} else if (e.key === 'ArrowUp') {
|
|
211
|
+
e.preventDefault();
|
|
212
|
+
const prevIndex = (currentIndex - 1 + allElements.length) % allElements.length;
|
|
213
|
+
const prevElement = allElements[prevIndex];
|
|
214
|
+
if (prevElement.tagName === 'LI') {
|
|
215
|
+
prevElement.setAttribute('tabindex', '0');
|
|
216
|
+
}
|
|
217
|
+
prevElement.focus();
|
|
218
|
+
} else if (e.key === 'Tab') {
|
|
219
|
+
// Get only traditional focusable elements for tab navigation
|
|
220
|
+
const focusable = menu.querySelectorAll(
|
|
221
|
+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
// If we're on the last focusable element and tabbing forward
|
|
225
|
+
if (!e.shiftKey && e.target === focusable[focusable.length - 1]) {
|
|
226
|
+
e.preventDefault();
|
|
227
|
+
menu.hidePopover();
|
|
228
|
+
// Focus the next focusable element after the dropdown trigger
|
|
229
|
+
const allFocusable = document.querySelectorAll(
|
|
230
|
+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
231
|
+
);
|
|
232
|
+
const triggerIndex = Array.from(allFocusable).indexOf(el);
|
|
233
|
+
const nextElement = allFocusable[triggerIndex + 1];
|
|
234
|
+
if (nextElement) nextElement.focus();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// If we're on the first element and tabbing backward
|
|
238
|
+
if (e.shiftKey && e.target === focusable[0]) {
|
|
239
|
+
menu.hidePopover();
|
|
240
|
+
}
|
|
241
|
+
} else if (e.key === 'Escape') {
|
|
242
|
+
menu.hidePopover();
|
|
243
|
+
el.focus();
|
|
244
|
+
} else if (e.key === 'Enter' || e.key === ' ') {
|
|
245
|
+
// Allow Enter/Space to activate li elements or follow links
|
|
246
|
+
if (e.target.tagName === 'LI') {
|
|
247
|
+
const link = e.target.querySelector('a');
|
|
248
|
+
if (link) {
|
|
249
|
+
e.preventDefault();
|
|
250
|
+
link.click();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// Make li elements focusable when menu opens
|
|
257
|
+
menu.addEventListener('toggle', (e) => {
|
|
258
|
+
if (e.newState === 'open') {
|
|
259
|
+
// Set up li elements for keyboard navigation
|
|
260
|
+
const liElements = menu.querySelectorAll('li');
|
|
261
|
+
liElements.forEach((li, index) => {
|
|
262
|
+
if (!li.hasAttribute('tabindex')) {
|
|
263
|
+
li.setAttribute('tabindex', '-1');
|
|
264
|
+
}
|
|
265
|
+
// Focus first li element if no other focusable elements
|
|
266
|
+
if (index === 0 && !menu.querySelector('button, [href], input, select, textarea, [tabindex="0"]')) {
|
|
267
|
+
li.setAttribute('tabindex', '0');
|
|
268
|
+
li.focus();
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
// Add hover functionality for menu
|
|
275
|
+
if (modifiers.includes('hover')) {
|
|
276
|
+
// Simple approach: any mouse activity in the menu area cancels close timer
|
|
277
|
+
menu.addEventListener('mouseenter', () => {
|
|
278
|
+
clearTimeout(autoCloseTimeout);
|
|
279
|
+
clearTimeout(hoverTimeout);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
menu.addEventListener('mouseleave', () => {
|
|
283
|
+
// Always start timer when leaving menu bounds
|
|
284
|
+
if (startAutoCloseTimer) {
|
|
285
|
+
startAutoCloseTimer();
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// Add event listeners to all interactive elements inside menu to cancel timers
|
|
290
|
+
const cancelCloseTimer = () => {
|
|
291
|
+
clearTimeout(autoCloseTimeout);
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
// Set up listeners on existing menu items
|
|
295
|
+
const setupMenuItemListeners = () => {
|
|
296
|
+
const menuItems = menu.querySelectorAll('li, button, a, [role="menuitem"]');
|
|
297
|
+
menuItems.forEach(item => {
|
|
298
|
+
item.addEventListener('mouseenter', cancelCloseTimer);
|
|
299
|
+
});
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
// Setup listeners after a brief delay to ensure menu is rendered
|
|
303
|
+
setTimeout(setupMenuItemListeners, 10);
|
|
304
|
+
}
|
|
305
|
+
} // End of setupDropdown function
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Track initialization to prevent duplicates
|
|
312
|
+
let dropdownPluginInitialized = false;
|
|
313
|
+
|
|
314
|
+
function ensureDropdownPluginInitialized() {
|
|
315
|
+
if (dropdownPluginInitialized) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (!window.Alpine || typeof window.Alpine.directive !== 'function') {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
dropdownPluginInitialized = true;
|
|
323
|
+
initializeDropdownPlugin();
|
|
324
|
+
|
|
325
|
+
// If elements with x-dropdown already exist, process them
|
|
326
|
+
if (window.Alpine && typeof window.Alpine.initTree === 'function') {
|
|
327
|
+
const existingDropdownElements = document.querySelectorAll('[x-dropdown]');
|
|
328
|
+
existingDropdownElements.forEach(el => {
|
|
329
|
+
if (!el.__x) {
|
|
330
|
+
window.Alpine.initTree(el);
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Expose on window for loader to call if needed
|
|
337
|
+
window.ensureDropdownPluginInitialized = ensureDropdownPluginInitialized;
|
|
338
|
+
|
|
339
|
+
// Handle both DOMContentLoaded and alpine:init
|
|
340
|
+
if (document.readyState === 'loading') {
|
|
341
|
+
document.addEventListener('DOMContentLoaded', ensureDropdownPluginInitialized);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
document.addEventListener('alpine:init', ensureDropdownPluginInitialized);
|
|
345
|
+
|
|
346
|
+
// If Alpine is already initialized when this script loads, initialize immediately
|
|
347
|
+
if (window.Alpine && typeof window.Alpine.directive === 'function') {
|
|
348
|
+
setTimeout(ensureDropdownPluginInitialized, 0);
|
|
349
|
+
} else if (document.readyState === 'complete') {
|
|
350
|
+
// If document is already loaded but Alpine isn't ready yet, wait for it
|
|
351
|
+
const checkAlpine = setInterval(() => {
|
|
352
|
+
if (window.Alpine && typeof window.Alpine.directive === 'function') {
|
|
353
|
+
clearInterval(checkAlpine);
|
|
354
|
+
ensureDropdownPluginInitialized();
|
|
355
|
+
}
|
|
356
|
+
}, 10);
|
|
357
|
+
setTimeout(() => clearInterval(checkAlpine), 5000);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Handle dialog interactions - close dropdowns when dialogs open
|
|
361
|
+
document.addEventListener('click', (event) => {
|
|
362
|
+
const button = event.target.closest('button[popovertarget]');
|
|
363
|
+
if (!button) return;
|
|
364
|
+
|
|
365
|
+
const targetId = button.getAttribute('popovertarget');
|
|
366
|
+
const target = document.getElementById(targetId);
|
|
367
|
+
|
|
368
|
+
if (target && target.tagName === 'DIALOG' && target.hasAttribute('popover')) {
|
|
369
|
+
// Close dropdowns BEFORE the dialog opens to avoid conflicts
|
|
370
|
+
const openDropdowns = document.querySelectorAll('menu[popover]:popover-open');
|
|
371
|
+
|
|
372
|
+
openDropdowns.forEach(dropdown => {
|
|
373
|
+
if (!target.contains(dropdown)) {
|
|
374
|
+
dropdown.hidePopover();
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
});
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/* Manifest Forms */
|
|
2
|
+
|
|
3
|
+
@layer components {
|
|
4
|
+
|
|
5
|
+
/* Group wrapper */
|
|
6
|
+
:where([role=group]:has(button, input, select)):not(.unstyle) {
|
|
7
|
+
display: flex;
|
|
8
|
+
flex-flow: row nowrap;
|
|
9
|
+
align-items: center;
|
|
10
|
+
gap: 0;
|
|
11
|
+
width: fit-content;
|
|
12
|
+
max-width: 100%;
|
|
13
|
+
|
|
14
|
+
&>* {
|
|
15
|
+
flex-basis: auto;
|
|
16
|
+
flex-shrink: 0;
|
|
17
|
+
|
|
18
|
+
&:focus {
|
|
19
|
+
z-index: 1
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
&>*:first-child {
|
|
24
|
+
border-start-end-radius: 0;
|
|
25
|
+
border-end-end-radius: 0
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
&>*:not(:first-child):not(:last-child) {
|
|
29
|
+
border-radius: 0
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
&>*:last-child {
|
|
33
|
+
border-start-start-radius: 0;
|
|
34
|
+
border-end-start-radius: 0
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
&.even>* {
|
|
38
|
+
flex-shrink: initial;
|
|
39
|
+
width: 100%
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
& input {
|
|
43
|
+
width: fit-content
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
:where(form):not(.unstyle) {
|
|
48
|
+
display: flex;
|
|
49
|
+
flex-direction: column;
|
|
50
|
+
gap: calc(var(--spacing) * 4);
|
|
51
|
+
width: 100%
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* Fieldset wrapper for checkboxes and radios */
|
|
55
|
+
:where(fieldset):not(.unstyle) {
|
|
56
|
+
display: flex;
|
|
57
|
+
flex-direction: column;
|
|
58
|
+
gap: 0.375ch calc(var(--spacing) * 2);
|
|
59
|
+
width: 100%;
|
|
60
|
+
|
|
61
|
+
&:has([type=radio], [type=checkbox]) {
|
|
62
|
+
gap: calc(var(--spacing) * 2);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
:where(fieldset:has(legend)):not(.unstyle) {
|
|
67
|
+
padding: 1ch 1.5ch 1.5ch 1.5ch;
|
|
68
|
+
border-radius: var(--radius, 0.5rem);
|
|
69
|
+
border-color: var(--color-line, oklch(48.3% 0.006422 17.4 / 0.15));
|
|
70
|
+
border-style: solid;
|
|
71
|
+
border-width: 1px;
|
|
72
|
+
|
|
73
|
+
& :where(legend) {
|
|
74
|
+
padding: 0 1.5ch;
|
|
75
|
+
font-size: 0.875rem;
|
|
76
|
+
color: var(--color-content-subtle, oklch(67.4% 0.0318 251.27));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* Label wrapper for radios and checkboxes */
|
|
81
|
+
:where(label, .label):has([type=radio], [type=checkbox]):not(.unstyle) {
|
|
82
|
+
display: flex;
|
|
83
|
+
flex-flow: row;
|
|
84
|
+
align-items: center;
|
|
85
|
+
gap: 1ch;
|
|
86
|
+
outline: 0 none;
|
|
87
|
+
cursor: pointer;
|
|
88
|
+
|
|
89
|
+
&:focus-within {
|
|
90
|
+
box-shadow: 0 0 0 0
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* Label wrapper */
|
|
95
|
+
:where(label:not(:has(.label)), .label):has(button, [role=button], [type=button], [type=submit], select, input:not([role=button], [type=checkbox], [type=radio], [type=file], [type=search]), textarea):not(:has(data)):not(.unstyle) {
|
|
96
|
+
display: flex;
|
|
97
|
+
flex-direction: column;
|
|
98
|
+
gap: 0.2ch;
|
|
99
|
+
width: 100%;
|
|
100
|
+
text-indent: calc(var(--radius) / 2);
|
|
101
|
+
cursor: pointer;
|
|
102
|
+
|
|
103
|
+
/* Prevent text indentation of bare label text nodes from being inherited */
|
|
104
|
+
:where(*) {
|
|
105
|
+
text-indent: 0
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/* Indent label text in spans */
|
|
109
|
+
:where(span:first-of-type) {
|
|
110
|
+
padding-inline-start: calc(var(--radius) / 2)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
:where(button, [role=button], [type=button], [type=submit], select, input:not([role=button], [type=checkbox], [type=radio], [type=file], [type=search]), textarea) {
|
|
114
|
+
width: 100%;
|
|
115
|
+
max-width: 100%
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* Space out stacked labels for search and file inputs */
|
|
119
|
+
:has([type=search], [type=file]) :where([type=search], [type=file]) {
|
|
120
|
+
margin-top: 0.2ch
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* Inline label wrapper */
|
|
125
|
+
label:has(data):not(.unstyle) {
|
|
126
|
+
display: flex;
|
|
127
|
+
flex-direction: row;
|
|
128
|
+
justify-content: space-between;
|
|
129
|
+
align-items: center;
|
|
130
|
+
gap: 1rem;
|
|
131
|
+
width: 100%;
|
|
132
|
+
|
|
133
|
+
&:focus-within {
|
|
134
|
+
box-shadow: 0 0 0 0
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/* Size buttons and inputs */
|
|
138
|
+
& :where(.label, button, input:not([type=checkbox], [type=radio]), select, textarea) {
|
|
139
|
+
width: calc(var(--spacing-field-height) * 8);
|
|
140
|
+
max-width: 50%;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/* Focus state for .label wrapper for search and file inputs */
|
|
144
|
+
& .label:focus-within {
|
|
145
|
+
box-shadow: 0 0 0 2px color-mix(in oklch, var(--color-content-stark) 35%, transparent)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* Align textarea label to top */
|
|
149
|
+
&:has(textarea) {
|
|
150
|
+
align-items: start;
|
|
151
|
+
|
|
152
|
+
:where(data) {
|
|
153
|
+
padding-top: calc(var(--spacing))
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/* Label wrappers for search and file inputs using .label utility */
|
|
159
|
+
label:has(.label):not(.unstyle) {
|
|
160
|
+
justify-content: space-between;
|
|
161
|
+
background-color: transparent;
|
|
162
|
+
cursor: default;
|
|
163
|
+
|
|
164
|
+
:where([type=search]) {
|
|
165
|
+
width: 100%;
|
|
166
|
+
max-width: 100%
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|