@coherent.js/core 1.0.0-beta.3 → 1.0.0-beta.6

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,1080 @@
1
+ /**
2
+ * Coherent.js Strict Element Types
3
+ * Per-element attribute validation for HTML elements
4
+ *
5
+ * This module provides strict TypeScript types for HTML elements with:
6
+ * - Per-element attribute validation (e.g., 'checked' only on input)
7
+ * - Strict children types (no boolean children)
8
+ * - Void element handling (no children on img, input, br, etc.)
9
+ * - Data attribute support via template literal types
10
+ *
11
+ * @version 1.0.0-beta.1
12
+ */
13
+
14
+ // ============================================================================
15
+ // Strict Children Type
16
+ // ============================================================================
17
+
18
+ /**
19
+ * Valid child types for Coherent elements.
20
+ * Note: boolean is NOT allowed - use null/undefined for conditional rendering.
21
+ */
22
+ export type CoherentChild =
23
+ | string
24
+ | number
25
+ | StrictCoherentElement
26
+ | StrictCoherentElement[]
27
+ | null
28
+ | undefined;
29
+
30
+ // ============================================================================
31
+ // Global HTML Attributes
32
+ // ============================================================================
33
+
34
+ /**
35
+ * ARIA role values for accessibility
36
+ */
37
+ export type AriaRole =
38
+ | 'alert'
39
+ | 'alertdialog'
40
+ | 'application'
41
+ | 'article'
42
+ | 'banner'
43
+ | 'button'
44
+ | 'cell'
45
+ | 'checkbox'
46
+ | 'columnheader'
47
+ | 'combobox'
48
+ | 'complementary'
49
+ | 'contentinfo'
50
+ | 'definition'
51
+ | 'dialog'
52
+ | 'directory'
53
+ | 'document'
54
+ | 'feed'
55
+ | 'figure'
56
+ | 'form'
57
+ | 'grid'
58
+ | 'gridcell'
59
+ | 'group'
60
+ | 'heading'
61
+ | 'img'
62
+ | 'link'
63
+ | 'list'
64
+ | 'listbox'
65
+ | 'listitem'
66
+ | 'log'
67
+ | 'main'
68
+ | 'marquee'
69
+ | 'math'
70
+ | 'menu'
71
+ | 'menubar'
72
+ | 'menuitem'
73
+ | 'menuitemcheckbox'
74
+ | 'menuitemradio'
75
+ | 'navigation'
76
+ | 'none'
77
+ | 'note'
78
+ | 'option'
79
+ | 'presentation'
80
+ | 'progressbar'
81
+ | 'radio'
82
+ | 'radiogroup'
83
+ | 'region'
84
+ | 'row'
85
+ | 'rowgroup'
86
+ | 'rowheader'
87
+ | 'scrollbar'
88
+ | 'search'
89
+ | 'searchbox'
90
+ | 'separator'
91
+ | 'slider'
92
+ | 'spinbutton'
93
+ | 'status'
94
+ | 'switch'
95
+ | 'tab'
96
+ | 'table'
97
+ | 'tablist'
98
+ | 'tabpanel'
99
+ | 'term'
100
+ | 'textbox'
101
+ | 'timer'
102
+ | 'toolbar'
103
+ | 'tooltip'
104
+ | 'tree'
105
+ | 'treegrid'
106
+ | 'treeitem'
107
+ | string; // Allow custom roles
108
+
109
+ /**
110
+ * Global HTML attributes shared by all elements.
111
+ * Based on lib.dom.d.ts patterns.
112
+ */
113
+ export interface GlobalHTMLAttributes {
114
+ // Core attributes
115
+ id?: string;
116
+ className?: string;
117
+ class?: string; // alias for className
118
+ style?: string | Record<string, string | number>;
119
+ title?: string;
120
+ lang?: string;
121
+ dir?: 'ltr' | 'rtl' | 'auto';
122
+ tabIndex?: number;
123
+ hidden?: boolean;
124
+ draggable?: boolean | 'true' | 'false';
125
+ contentEditable?: boolean | 'true' | 'false' | 'inherit';
126
+ spellCheck?: boolean | 'true' | 'false';
127
+ translate?: 'yes' | 'no';
128
+ accessKey?: string;
129
+ autoCapitalize?: 'off' | 'none' | 'on' | 'sentences' | 'words' | 'characters';
130
+ enterKeyHint?: 'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send';
131
+ inputMode?: 'none' | 'text' | 'decimal' | 'numeric' | 'tel' | 'search' | 'email' | 'url';
132
+ slot?: string;
133
+ is?: string;
134
+
135
+ // Key prop for reconciliation (extracted, not rendered)
136
+ key?: string | number;
137
+
138
+ // Data attributes (template literal type)
139
+ [key: `data-${string}`]: string | number | boolean | undefined;
140
+
141
+ // ARIA attributes
142
+ role?: AriaRole;
143
+ 'aria-label'?: string;
144
+ 'aria-labelledby'?: string;
145
+ 'aria-describedby'?: string;
146
+ 'aria-hidden'?: boolean | 'true' | 'false';
147
+ 'aria-disabled'?: boolean | 'true' | 'false';
148
+ 'aria-expanded'?: boolean | 'true' | 'false';
149
+ 'aria-selected'?: boolean | 'true' | 'false';
150
+ 'aria-checked'?: boolean | 'true' | 'false' | 'mixed';
151
+ 'aria-pressed'?: boolean | 'true' | 'false' | 'mixed';
152
+ 'aria-current'?: boolean | 'true' | 'false' | 'page' | 'step' | 'location' | 'date' | 'time';
153
+ 'aria-live'?: 'off' | 'assertive' | 'polite';
154
+ 'aria-atomic'?: boolean | 'true' | 'false';
155
+ 'aria-busy'?: boolean | 'true' | 'false';
156
+ 'aria-controls'?: string;
157
+ 'aria-haspopup'?: boolean | 'true' | 'false' | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog';
158
+ 'aria-invalid'?: boolean | 'true' | 'false' | 'grammar' | 'spelling';
159
+ 'aria-modal'?: boolean | 'true' | 'false';
160
+ 'aria-multiline'?: boolean | 'true' | 'false';
161
+ 'aria-multiselectable'?: boolean | 'true' | 'false';
162
+ 'aria-orientation'?: 'horizontal' | 'vertical';
163
+ 'aria-owns'?: string;
164
+ 'aria-placeholder'?: string;
165
+ 'aria-readonly'?: boolean | 'true' | 'false';
166
+ 'aria-required'?: boolean | 'true' | 'false';
167
+ 'aria-roledescription'?: string;
168
+ 'aria-sort'?: 'none' | 'ascending' | 'descending' | 'other';
169
+ 'aria-valuemax'?: number;
170
+ 'aria-valuemin'?: number;
171
+ 'aria-valuenow'?: number;
172
+ 'aria-valuetext'?: string;
173
+ }
174
+
175
+ // ============================================================================
176
+ // Global Event Handlers
177
+ // ============================================================================
178
+
179
+ /**
180
+ * Event handler types matching runtime behavior.
181
+ * Handlers can be either inline strings (for SSR) or functions (for hydration).
182
+ */
183
+ export interface GlobalEventHandlers {
184
+ // Mouse events
185
+ onClick?: string | ((event: MouseEvent) => void);
186
+ onDblClick?: string | ((event: MouseEvent) => void);
187
+ onMouseDown?: string | ((event: MouseEvent) => void);
188
+ onMouseUp?: string | ((event: MouseEvent) => void);
189
+ onMouseEnter?: string | ((event: MouseEvent) => void);
190
+ onMouseLeave?: string | ((event: MouseEvent) => void);
191
+ onMouseMove?: string | ((event: MouseEvent) => void);
192
+ onMouseOver?: string | ((event: MouseEvent) => void);
193
+ onMouseOut?: string | ((event: MouseEvent) => void);
194
+ onContextMenu?: string | ((event: MouseEvent) => void);
195
+
196
+ // Keyboard events
197
+ onKeyDown?: string | ((event: KeyboardEvent) => void);
198
+ onKeyUp?: string | ((event: KeyboardEvent) => void);
199
+ onKeyPress?: string | ((event: KeyboardEvent) => void);
200
+
201
+ // Focus events
202
+ onFocus?: string | ((event: FocusEvent) => void);
203
+ onBlur?: string | ((event: FocusEvent) => void);
204
+ onFocusIn?: string | ((event: FocusEvent) => void);
205
+ onFocusOut?: string | ((event: FocusEvent) => void);
206
+
207
+ // Form events
208
+ onChange?: string | ((event: Event) => void);
209
+ onInput?: string | ((event: Event) => void);
210
+ onSubmit?: string | ((event: SubmitEvent) => void);
211
+ onReset?: string | ((event: Event) => void);
212
+ onInvalid?: string | ((event: Event) => void);
213
+
214
+ // Drag events
215
+ onDrag?: string | ((event: DragEvent) => void);
216
+ onDragEnd?: string | ((event: DragEvent) => void);
217
+ onDragEnter?: string | ((event: DragEvent) => void);
218
+ onDragLeave?: string | ((event: DragEvent) => void);
219
+ onDragOver?: string | ((event: DragEvent) => void);
220
+ onDragStart?: string | ((event: DragEvent) => void);
221
+ onDrop?: string | ((event: DragEvent) => void);
222
+
223
+ // Clipboard events
224
+ onCopy?: string | ((event: ClipboardEvent) => void);
225
+ onCut?: string | ((event: ClipboardEvent) => void);
226
+ onPaste?: string | ((event: ClipboardEvent) => void);
227
+
228
+ // Touch events
229
+ onTouchStart?: string | ((event: TouchEvent) => void);
230
+ onTouchMove?: string | ((event: TouchEvent) => void);
231
+ onTouchEnd?: string | ((event: TouchEvent) => void);
232
+ onTouchCancel?: string | ((event: TouchEvent) => void);
233
+
234
+ // Wheel events
235
+ onWheel?: string | ((event: WheelEvent) => void);
236
+ onScroll?: string | ((event: Event) => void);
237
+
238
+ // Animation events
239
+ onAnimationStart?: string | ((event: AnimationEvent) => void);
240
+ onAnimationEnd?: string | ((event: AnimationEvent) => void);
241
+ onAnimationIteration?: string | ((event: AnimationEvent) => void);
242
+
243
+ // Transition events
244
+ onTransitionStart?: string | ((event: TransitionEvent) => void);
245
+ onTransitionEnd?: string | ((event: TransitionEvent) => void);
246
+ onTransitionCancel?: string | ((event: TransitionEvent) => void);
247
+ onTransitionRun?: string | ((event: TransitionEvent) => void);
248
+ }
249
+
250
+ // ============================================================================
251
+ // Coherent-specific Properties
252
+ // ============================================================================
253
+
254
+ /**
255
+ * Coherent.js-specific element properties.
256
+ */
257
+ export interface CoherentElementBase {
258
+ /** Text content (escaped during render) */
259
+ text?: string | number;
260
+ /** Raw HTML content (dangerous - not escaped) */
261
+ html?: string;
262
+ /** Child elements */
263
+ children?: CoherentChild | CoherentChild[];
264
+ }
265
+
266
+ /**
267
+ * Base attributes combining global HTML attributes, event handlers, and Coherent properties.
268
+ */
269
+ export type BaseElementAttributes = GlobalHTMLAttributes & GlobalEventHandlers & CoherentElementBase;
270
+
271
+ // ============================================================================
272
+ // Element-specific Attribute Interfaces
273
+ // ============================================================================
274
+
275
+ /**
276
+ * Input element types
277
+ */
278
+ export type InputType =
279
+ | 'text'
280
+ | 'email'
281
+ | 'password'
282
+ | 'number'
283
+ | 'tel'
284
+ | 'url'
285
+ | 'search'
286
+ | 'date'
287
+ | 'time'
288
+ | 'datetime-local'
289
+ | 'month'
290
+ | 'week'
291
+ | 'color'
292
+ | 'file'
293
+ | 'hidden'
294
+ | 'checkbox'
295
+ | 'radio'
296
+ | 'range'
297
+ | 'submit'
298
+ | 'reset'
299
+ | 'button'
300
+ | 'image';
301
+
302
+ /**
303
+ * Input element attributes
304
+ */
305
+ export interface InputAttributes extends GlobalHTMLAttributes, GlobalEventHandlers {
306
+ // Coherent-specific (no children for void element, only text for label)
307
+ text?: string | number;
308
+ html?: string;
309
+ // Note: children intentionally omitted for void element
310
+
311
+ // Input-specific attributes
312
+ type?: InputType;
313
+ value?: string | number | readonly string[];
314
+ checked?: boolean;
315
+ disabled?: boolean;
316
+ required?: boolean;
317
+ placeholder?: string;
318
+ name?: string;
319
+ min?: string | number;
320
+ max?: string | number;
321
+ step?: string | number;
322
+ pattern?: string;
323
+ readOnly?: boolean;
324
+ readonly?: boolean; // alias
325
+ autoComplete?: string;
326
+ autocomplete?: string; // alias
327
+ autoFocus?: boolean;
328
+ autofocus?: boolean; // alias
329
+ multiple?: boolean;
330
+ accept?: string;
331
+ maxLength?: number;
332
+ minLength?: number;
333
+ size?: number;
334
+ list?: string;
335
+ form?: string;
336
+ formAction?: string;
337
+ formEncType?: string;
338
+ formMethod?: string;
339
+ formNoValidate?: boolean;
340
+ formTarget?: string;
341
+ capture?: boolean | 'user' | 'environment';
342
+ width?: number | string;
343
+ height?: number | string;
344
+ src?: string;
345
+ alt?: string;
346
+ }
347
+
348
+ /**
349
+ * Button element attributes
350
+ */
351
+ export interface ButtonAttributes extends BaseElementAttributes {
352
+ type?: 'button' | 'submit' | 'reset';
353
+ disabled?: boolean;
354
+ form?: string;
355
+ formAction?: string;
356
+ formEncType?: string;
357
+ formMethod?: string;
358
+ formTarget?: string;
359
+ formNoValidate?: boolean;
360
+ name?: string;
361
+ value?: string;
362
+ popoverTarget?: string;
363
+ popoverTargetAction?: 'hide' | 'show' | 'toggle';
364
+ }
365
+
366
+ /**
367
+ * Anchor element attributes
368
+ */
369
+ export interface AnchorAttributes extends BaseElementAttributes {
370
+ href?: string;
371
+ target?: '_self' | '_blank' | '_parent' | '_top' | string;
372
+ rel?: string;
373
+ download?: string | boolean;
374
+ hrefLang?: string;
375
+ hreflang?: string; // alias
376
+ type?: string;
377
+ referrerPolicy?: ReferrerPolicy;
378
+ ping?: string;
379
+ }
380
+
381
+ /**
382
+ * Image element attributes (void element - no children)
383
+ */
384
+ export interface ImgAttributes extends GlobalHTMLAttributes, GlobalEventHandlers {
385
+ // Coherent-specific (no children for void element)
386
+ text?: string | number;
387
+ html?: string;
388
+ // Note: children intentionally omitted for void element
389
+
390
+ src?: string;
391
+ alt?: string;
392
+ width?: number | string;
393
+ height?: number | string;
394
+ loading?: 'lazy' | 'eager';
395
+ decoding?: 'sync' | 'async' | 'auto';
396
+ crossOrigin?: 'anonymous' | 'use-credentials' | '';
397
+ referrerPolicy?: ReferrerPolicy;
398
+ srcSet?: string;
399
+ srcset?: string; // alias
400
+ sizes?: string;
401
+ useMap?: string;
402
+ isMap?: boolean;
403
+ fetchPriority?: 'high' | 'low' | 'auto';
404
+
405
+ // Event handlers
406
+ onLoad?: string | ((event: Event) => void);
407
+ onError?: string | ((event: Event) => void);
408
+ }
409
+
410
+ /**
411
+ * Form element attributes
412
+ */
413
+ export interface FormAttributes extends BaseElementAttributes {
414
+ action?: string;
415
+ method?: 'get' | 'post' | 'dialog';
416
+ encType?: string;
417
+ enctype?: string; // alias
418
+ target?: '_self' | '_blank' | '_parent' | '_top' | string;
419
+ noValidate?: boolean;
420
+ novalidate?: boolean; // alias
421
+ autoComplete?: 'on' | 'off';
422
+ autocomplete?: 'on' | 'off'; // alias
423
+ acceptCharset?: string;
424
+ name?: string;
425
+ rel?: string;
426
+ }
427
+
428
+ /**
429
+ * Select element attributes
430
+ */
431
+ export interface SelectAttributes extends BaseElementAttributes {
432
+ name?: string;
433
+ multiple?: boolean;
434
+ disabled?: boolean;
435
+ required?: boolean;
436
+ size?: number;
437
+ autoFocus?: boolean;
438
+ autofocus?: boolean; // alias
439
+ form?: string;
440
+ value?: string | number | readonly string[];
441
+ }
442
+
443
+ /**
444
+ * Textarea element attributes
445
+ */
446
+ export interface TextareaAttributes extends BaseElementAttributes {
447
+ name?: string;
448
+ rows?: number;
449
+ cols?: number;
450
+ disabled?: boolean;
451
+ readOnly?: boolean;
452
+ readonly?: boolean; // alias
453
+ required?: boolean;
454
+ placeholder?: string;
455
+ maxLength?: number;
456
+ minLength?: number;
457
+ wrap?: 'soft' | 'hard' | 'off';
458
+ autoFocus?: boolean;
459
+ autofocus?: boolean; // alias
460
+ autoComplete?: string;
461
+ autocomplete?: string; // alias
462
+ form?: string;
463
+ value?: string;
464
+ }
465
+
466
+ /**
467
+ * Label element attributes
468
+ */
469
+ export interface LabelAttributes extends BaseElementAttributes {
470
+ htmlFor?: string;
471
+ for?: string; // alias
472
+ form?: string;
473
+ }
474
+
475
+ /**
476
+ * Option element attributes
477
+ */
478
+ export interface OptionAttributes extends BaseElementAttributes {
479
+ value?: string | number;
480
+ disabled?: boolean;
481
+ selected?: boolean;
482
+ label?: string;
483
+ }
484
+
485
+ /**
486
+ * Optgroup element attributes
487
+ */
488
+ export interface OptgroupAttributes extends BaseElementAttributes {
489
+ disabled?: boolean;
490
+ label?: string;
491
+ }
492
+
493
+ /**
494
+ * Table element attributes
495
+ */
496
+ export interface TableAttributes extends BaseElementAttributes {
497
+ border?: number | string;
498
+ cellPadding?: number | string;
499
+ cellSpacing?: number | string;
500
+ width?: number | string;
501
+ summary?: string;
502
+ }
503
+
504
+ /**
505
+ * Table cell attributes (td, th)
506
+ */
507
+ export interface TableCellAttributes extends BaseElementAttributes {
508
+ colSpan?: number;
509
+ colspan?: number; // alias
510
+ rowSpan?: number;
511
+ rowspan?: number; // alias
512
+ headers?: string;
513
+ scope?: 'row' | 'col' | 'rowgroup' | 'colgroup';
514
+ abbr?: string;
515
+ }
516
+
517
+ /**
518
+ * Iframe element attributes
519
+ */
520
+ export interface IframeAttributes extends BaseElementAttributes {
521
+ src?: string;
522
+ srcDoc?: string;
523
+ srcdoc?: string; // alias
524
+ name?: string;
525
+ width?: number | string;
526
+ height?: number | string;
527
+ sandbox?: string;
528
+ allow?: string;
529
+ allowFullScreen?: boolean;
530
+ allowfullscreen?: boolean; // alias
531
+ loading?: 'lazy' | 'eager';
532
+ referrerPolicy?: ReferrerPolicy;
533
+
534
+ // Event handlers
535
+ onLoad?: string | ((event: Event) => void);
536
+ onError?: string | ((event: Event) => void);
537
+ }
538
+
539
+ /**
540
+ * Video element attributes
541
+ */
542
+ export interface VideoAttributes extends BaseElementAttributes {
543
+ src?: string;
544
+ poster?: string;
545
+ width?: number | string;
546
+ height?: number | string;
547
+ autoPlay?: boolean;
548
+ autoplay?: boolean; // alias
549
+ controls?: boolean;
550
+ loop?: boolean;
551
+ muted?: boolean;
552
+ playsInline?: boolean;
553
+ playsinline?: boolean; // alias
554
+ preload?: 'none' | 'metadata' | 'auto' | '';
555
+ crossOrigin?: 'anonymous' | 'use-credentials' | '';
556
+
557
+ // Event handlers
558
+ onPlay?: string | ((event: Event) => void);
559
+ onPause?: string | ((event: Event) => void);
560
+ onEnded?: string | ((event: Event) => void);
561
+ onLoadedMetadata?: string | ((event: Event) => void);
562
+ onTimeUpdate?: string | ((event: Event) => void);
563
+ onVolumeChange?: string | ((event: Event) => void);
564
+ onError?: string | ((event: Event) => void);
565
+ }
566
+
567
+ /**
568
+ * Audio element attributes
569
+ */
570
+ export interface AudioAttributes extends BaseElementAttributes {
571
+ src?: string;
572
+ autoPlay?: boolean;
573
+ autoplay?: boolean; // alias
574
+ controls?: boolean;
575
+ loop?: boolean;
576
+ muted?: boolean;
577
+ preload?: 'none' | 'metadata' | 'auto' | '';
578
+ crossOrigin?: 'anonymous' | 'use-credentials' | '';
579
+
580
+ // Event handlers
581
+ onPlay?: string | ((event: Event) => void);
582
+ onPause?: string | ((event: Event) => void);
583
+ onEnded?: string | ((event: Event) => void);
584
+ onLoadedMetadata?: string | ((event: Event) => void);
585
+ onTimeUpdate?: string | ((event: Event) => void);
586
+ onVolumeChange?: string | ((event: Event) => void);
587
+ onError?: string | ((event: Event) => void);
588
+ }
589
+
590
+ /**
591
+ * Source element attributes (void element)
592
+ */
593
+ export interface SourceAttributes extends GlobalHTMLAttributes {
594
+ src?: string;
595
+ srcSet?: string;
596
+ srcset?: string; // alias
597
+ type?: string;
598
+ media?: string;
599
+ sizes?: string;
600
+ width?: number;
601
+ height?: number;
602
+ }
603
+
604
+ /**
605
+ * Track element attributes (void element)
606
+ */
607
+ export interface TrackAttributes extends GlobalHTMLAttributes {
608
+ kind?: 'subtitles' | 'captions' | 'descriptions' | 'chapters' | 'metadata';
609
+ src?: string;
610
+ srcLang?: string;
611
+ srclang?: string; // alias
612
+ label?: string;
613
+ default?: boolean;
614
+ }
615
+
616
+ /**
617
+ * Canvas element attributes
618
+ */
619
+ export interface CanvasAttributes extends BaseElementAttributes {
620
+ width?: number | string;
621
+ height?: number | string;
622
+ }
623
+
624
+ /**
625
+ * Link element attributes (void element)
626
+ */
627
+ export interface LinkAttributes extends GlobalHTMLAttributes {
628
+ href?: string;
629
+ rel?: string;
630
+ type?: string;
631
+ media?: string;
632
+ as?: string;
633
+ crossOrigin?: 'anonymous' | 'use-credentials' | '';
634
+ integrity?: string;
635
+ referrerPolicy?: ReferrerPolicy;
636
+ sizes?: string;
637
+ disabled?: boolean;
638
+ hrefLang?: string;
639
+ imageSrcSet?: string;
640
+ imageSizes?: string;
641
+ fetchPriority?: 'high' | 'low' | 'auto';
642
+ }
643
+
644
+ /**
645
+ * Meta element attributes (void element)
646
+ */
647
+ export interface MetaAttributes extends GlobalHTMLAttributes {
648
+ name?: string;
649
+ content?: string;
650
+ httpEquiv?: string;
651
+ 'http-equiv'?: string; // alias
652
+ charset?: string;
653
+ property?: string;
654
+ media?: string;
655
+ }
656
+
657
+ /**
658
+ * Script element attributes
659
+ */
660
+ export interface ScriptAttributes extends BaseElementAttributes {
661
+ src?: string;
662
+ type?: string;
663
+ async?: boolean;
664
+ defer?: boolean;
665
+ crossOrigin?: 'anonymous' | 'use-credentials' | '';
666
+ integrity?: string;
667
+ noModule?: boolean;
668
+ nomodule?: boolean; // alias
669
+ referrerPolicy?: ReferrerPolicy;
670
+ blocking?: string;
671
+ fetchPriority?: 'high' | 'low' | 'auto';
672
+
673
+ // Event handlers
674
+ onLoad?: string | ((event: Event) => void);
675
+ onError?: string | ((event: Event) => void);
676
+ }
677
+
678
+ /**
679
+ * Style element attributes
680
+ */
681
+ export interface StyleAttributes extends BaseElementAttributes {
682
+ type?: string;
683
+ media?: string;
684
+ nonce?: string;
685
+ blocking?: string;
686
+ }
687
+
688
+ /**
689
+ * Progress element attributes
690
+ */
691
+ export interface ProgressAttributes extends BaseElementAttributes {
692
+ value?: number;
693
+ max?: number;
694
+ }
695
+
696
+ /**
697
+ * Meter element attributes
698
+ */
699
+ export interface MeterAttributes extends BaseElementAttributes {
700
+ value?: number;
701
+ min?: number;
702
+ max?: number;
703
+ low?: number;
704
+ high?: number;
705
+ optimum?: number;
706
+ }
707
+
708
+ /**
709
+ * Details element attributes
710
+ */
711
+ export interface DetailsAttributes extends BaseElementAttributes {
712
+ open?: boolean;
713
+ onToggle?: string | ((event: Event) => void);
714
+ }
715
+
716
+ /**
717
+ * Dialog element attributes
718
+ */
719
+ export interface DialogAttributes extends BaseElementAttributes {
720
+ open?: boolean;
721
+ onClose?: string | ((event: Event) => void);
722
+ onCancel?: string | ((event: Event) => void);
723
+ }
724
+
725
+ /**
726
+ * Time element attributes
727
+ */
728
+ export interface TimeAttributes extends BaseElementAttributes {
729
+ dateTime?: string;
730
+ datetime?: string; // alias
731
+ }
732
+
733
+ /**
734
+ * Output element attributes
735
+ */
736
+ export interface OutputAttributes extends BaseElementAttributes {
737
+ htmlFor?: string;
738
+ for?: string; // alias
739
+ form?: string;
740
+ name?: string;
741
+ }
742
+
743
+ /**
744
+ * Fieldset element attributes
745
+ */
746
+ export interface FieldsetAttributes extends BaseElementAttributes {
747
+ disabled?: boolean;
748
+ form?: string;
749
+ name?: string;
750
+ }
751
+
752
+ /**
753
+ * Legend element attributes
754
+ */
755
+ export interface LegendAttributes extends BaseElementAttributes {}
756
+
757
+ /**
758
+ * Datalist element attributes
759
+ */
760
+ export interface DatalistAttributes extends BaseElementAttributes {}
761
+
762
+ /**
763
+ * Col element attributes (void element)
764
+ */
765
+ export interface ColAttributes extends GlobalHTMLAttributes {
766
+ span?: number;
767
+ }
768
+
769
+ /**
770
+ * Colgroup element attributes
771
+ */
772
+ export interface ColgroupAttributes extends BaseElementAttributes {
773
+ span?: number;
774
+ }
775
+
776
+ /**
777
+ * Area element attributes (void element)
778
+ */
779
+ export interface AreaAttributes extends GlobalHTMLAttributes, GlobalEventHandlers {
780
+ alt?: string;
781
+ coords?: string;
782
+ download?: string | boolean;
783
+ href?: string;
784
+ hrefLang?: string;
785
+ ping?: string;
786
+ referrerPolicy?: ReferrerPolicy;
787
+ rel?: string;
788
+ shape?: 'rect' | 'circle' | 'poly' | 'default';
789
+ target?: '_self' | '_blank' | '_parent' | '_top' | string;
790
+ }
791
+
792
+ /**
793
+ * Map element attributes
794
+ */
795
+ export interface MapAttributes extends BaseElementAttributes {
796
+ name?: string;
797
+ }
798
+
799
+ /**
800
+ * Embed element attributes (void element)
801
+ */
802
+ export interface EmbedAttributes extends GlobalHTMLAttributes, GlobalEventHandlers {
803
+ src?: string;
804
+ type?: string;
805
+ width?: number | string;
806
+ height?: number | string;
807
+ }
808
+
809
+ /**
810
+ * Object element attributes
811
+ */
812
+ export interface ObjectAttributes extends BaseElementAttributes {
813
+ data?: string;
814
+ type?: string;
815
+ name?: string;
816
+ useMap?: string;
817
+ form?: string;
818
+ width?: number | string;
819
+ height?: number | string;
820
+ }
821
+
822
+ /**
823
+ * Param element attributes (void element - deprecated but still supported)
824
+ */
825
+ export interface ParamAttributes extends GlobalHTMLAttributes {
826
+ name?: string;
827
+ value?: string;
828
+ }
829
+
830
+ /**
831
+ * Blockquote element attributes
832
+ */
833
+ export interface BlockquoteAttributes extends BaseElementAttributes {
834
+ cite?: string;
835
+ }
836
+
837
+ /**
838
+ * Q element attributes
839
+ */
840
+ export interface QAttributes extends BaseElementAttributes {
841
+ cite?: string;
842
+ }
843
+
844
+ /**
845
+ * Ins/Del element attributes
846
+ */
847
+ export interface InsDelAttributes extends BaseElementAttributes {
848
+ cite?: string;
849
+ dateTime?: string;
850
+ datetime?: string; // alias
851
+ }
852
+
853
+ /**
854
+ * Referrer policy type
855
+ */
856
+ export type ReferrerPolicy =
857
+ | 'no-referrer'
858
+ | 'no-referrer-when-downgrade'
859
+ | 'origin'
860
+ | 'origin-when-cross-origin'
861
+ | 'same-origin'
862
+ | 'strict-origin'
863
+ | 'strict-origin-when-cross-origin'
864
+ | 'unsafe-url'
865
+ | '';
866
+
867
+ // ============================================================================
868
+ // Void Element Tag Names
869
+ // ============================================================================
870
+
871
+ /**
872
+ * HTML void elements that cannot have children.
873
+ */
874
+ export type VoidElementTagNames =
875
+ | 'area'
876
+ | 'base'
877
+ | 'br'
878
+ | 'col'
879
+ | 'embed'
880
+ | 'hr'
881
+ | 'img'
882
+ | 'input'
883
+ | 'link'
884
+ | 'meta'
885
+ | 'param'
886
+ | 'source'
887
+ | 'track'
888
+ | 'wbr';
889
+
890
+ // ============================================================================
891
+ // HTML Element Attribute Map
892
+ // ============================================================================
893
+
894
+ /**
895
+ * Maps HTML tag names to their specific attribute interfaces.
896
+ */
897
+ export interface HTMLElementAttributeMap {
898
+ // Interactive elements
899
+ a: AnchorAttributes;
900
+ button: ButtonAttributes;
901
+ input: InputAttributes;
902
+ select: SelectAttributes;
903
+ textarea: TextareaAttributes;
904
+ label: LabelAttributes;
905
+ option: OptionAttributes;
906
+ optgroup: OptgroupAttributes;
907
+ output: OutputAttributes;
908
+ datalist: DatalistAttributes;
909
+
910
+ // Form elements
911
+ form: FormAttributes;
912
+ fieldset: FieldsetAttributes;
913
+ legend: LegendAttributes;
914
+
915
+ // Media elements
916
+ img: ImgAttributes;
917
+ video: VideoAttributes;
918
+ audio: AudioAttributes;
919
+ source: SourceAttributes;
920
+ track: TrackAttributes;
921
+ canvas: CanvasAttributes;
922
+ picture: BaseElementAttributes;
923
+
924
+ // Embedded content
925
+ iframe: IframeAttributes;
926
+ embed: EmbedAttributes;
927
+ object: ObjectAttributes;
928
+ param: ParamAttributes;
929
+ map: MapAttributes;
930
+ area: AreaAttributes;
931
+
932
+ // Document metadata
933
+ link: LinkAttributes;
934
+ meta: MetaAttributes;
935
+ base: GlobalHTMLAttributes;
936
+ style: StyleAttributes;
937
+ script: ScriptAttributes;
938
+ noscript: BaseElementAttributes;
939
+
940
+ // Table elements
941
+ table: TableAttributes;
942
+ caption: BaseElementAttributes;
943
+ thead: BaseElementAttributes;
944
+ tbody: BaseElementAttributes;
945
+ tfoot: BaseElementAttributes;
946
+ tr: BaseElementAttributes;
947
+ td: TableCellAttributes;
948
+ th: TableCellAttributes;
949
+ col: ColAttributes;
950
+ colgroup: ColgroupAttributes;
951
+
952
+ // Progress/Meter
953
+ progress: ProgressAttributes;
954
+ meter: MeterAttributes;
955
+
956
+ // Interactive elements
957
+ details: DetailsAttributes;
958
+ summary: BaseElementAttributes;
959
+ dialog: DialogAttributes;
960
+
961
+ // Text elements
962
+ time: TimeAttributes;
963
+ blockquote: BlockquoteAttributes;
964
+ q: QAttributes;
965
+ ins: InsDelAttributes;
966
+ del: InsDelAttributes;
967
+
968
+ // Void elements with only global attributes
969
+ br: GlobalHTMLAttributes;
970
+ hr: GlobalHTMLAttributes;
971
+ wbr: GlobalHTMLAttributes;
972
+
973
+ // Structural elements (all use base attributes)
974
+ div: BaseElementAttributes;
975
+ span: BaseElementAttributes;
976
+ p: BaseElementAttributes;
977
+ section: BaseElementAttributes;
978
+ article: BaseElementAttributes;
979
+ aside: BaseElementAttributes;
980
+ header: BaseElementAttributes;
981
+ footer: BaseElementAttributes;
982
+ main: BaseElementAttributes;
983
+ nav: BaseElementAttributes;
984
+ figure: BaseElementAttributes;
985
+ figcaption: BaseElementAttributes;
986
+ address: BaseElementAttributes;
987
+ hgroup: BaseElementAttributes;
988
+ search: BaseElementAttributes;
989
+
990
+ // Heading elements
991
+ h1: BaseElementAttributes;
992
+ h2: BaseElementAttributes;
993
+ h3: BaseElementAttributes;
994
+ h4: BaseElementAttributes;
995
+ h5: BaseElementAttributes;
996
+ h6: BaseElementAttributes;
997
+
998
+ // List elements
999
+ ul: BaseElementAttributes;
1000
+ ol: BaseElementAttributes;
1001
+ li: BaseElementAttributes;
1002
+ dl: BaseElementAttributes;
1003
+ dt: BaseElementAttributes;
1004
+ dd: BaseElementAttributes;
1005
+ menu: BaseElementAttributes;
1006
+
1007
+ // Text formatting elements
1008
+ em: BaseElementAttributes;
1009
+ strong: BaseElementAttributes;
1010
+ small: BaseElementAttributes;
1011
+ s: BaseElementAttributes;
1012
+ cite: BaseElementAttributes;
1013
+ dfn: BaseElementAttributes;
1014
+ abbr: BaseElementAttributes;
1015
+ ruby: BaseElementAttributes;
1016
+ rt: BaseElementAttributes;
1017
+ rp: BaseElementAttributes;
1018
+ data: BaseElementAttributes;
1019
+ code: BaseElementAttributes;
1020
+ var: BaseElementAttributes;
1021
+ samp: BaseElementAttributes;
1022
+ kbd: BaseElementAttributes;
1023
+ sub: BaseElementAttributes;
1024
+ sup: BaseElementAttributes;
1025
+ i: BaseElementAttributes;
1026
+ b: BaseElementAttributes;
1027
+ u: BaseElementAttributes;
1028
+ mark: BaseElementAttributes;
1029
+ bdi: BaseElementAttributes;
1030
+ bdo: BaseElementAttributes;
1031
+ pre: BaseElementAttributes;
1032
+
1033
+ // Document structure (typically not used in components)
1034
+ html: BaseElementAttributes;
1035
+ head: BaseElementAttributes;
1036
+ body: BaseElementAttributes;
1037
+ title: BaseElementAttributes;
1038
+
1039
+ // Other elements
1040
+ template: BaseElementAttributes;
1041
+ slot: BaseElementAttributes;
1042
+ }
1043
+
1044
+ // ============================================================================
1045
+ // Strict Coherent Element Type
1046
+ // ============================================================================
1047
+
1048
+ /**
1049
+ * Strict element type with per-element attribute validation.
1050
+ *
1051
+ * Use this type for strict type checking:
1052
+ * - Catches attribute typos (classname vs className)
1053
+ * - Catches element-specific attribute misuse (checked on div)
1054
+ * - Prevents children on void elements
1055
+ *
1056
+ * For permissive typing (backward compatibility), use CoherentElement from index.d.ts.
1057
+ *
1058
+ * @example
1059
+ * ```typescript
1060
+ * // Valid - input can have checked
1061
+ * const checkbox: StrictCoherentElement = {
1062
+ * input: { type: 'checkbox', checked: true }
1063
+ * };
1064
+ *
1065
+ * // Error - div cannot have checked
1066
+ * const invalid: StrictCoherentElement = {
1067
+ * div: { checked: true } // Type error!
1068
+ * };
1069
+ *
1070
+ * // Error - img cannot have children
1071
+ * const invalidImg: StrictCoherentElement = {
1072
+ * img: { src: 'foo.png', children: [{ span: {} }] } // Type error!
1073
+ * };
1074
+ * ```
1075
+ */
1076
+ export type StrictCoherentElement = {
1077
+ [K in keyof HTMLElementAttributeMap]?: K extends VoidElementTagNames
1078
+ ? Omit<HTMLElementAttributeMap[K], 'children'>
1079
+ : HTMLElementAttributeMap[K];
1080
+ };