From bfcff0e3c09a081f0adbc9aa1a76b76b97954c3e Mon Sep 17 00:00:00 2001 From: dev Date: Fri, 3 Jul 2026 01:03:00 +0200 Subject: [PATCH] =?UTF-8?q?fix(uix):=20C5=20=E2=80=94=20every=20eventful?= =?UTF-8?q?=20morfo=20declares=20its=20sema=20expression=20mode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 30 eventful morfos that never declared expression: ALL have a per-component pack in src/uix/sema/components/ — so the classification (book-deviations D.4: pack / family-default / delegated / none) is unambiguous: expression: 'pack' across the board, placed after scope per the switch/button idiom. The sema surface of the contract is now explicit on all 82 eventful morfos instead of inferred from pack-file existence. morfo:vocabulary clean (the one naming warn is media-player, foreign WIP); npm run check at the 61-error baseline. Additional persistence/a11ySemantic annotations deliberately NOT mass-applied — per D.9 the canonical table is reference, authors declare per real consumer (the six that need them already do). Co-Authored-By: Claude Fable 5 --- docs/process/PLAN-component-coherence.md | 2 +- src/uix/morfo/components/accordion.ts | 1 + src/uix/morfo/components/calendar.ts | 1 + src/uix/morfo/components/checkbox.ts | 1 + src/uix/morfo/components/color-picker.ts | 1 + src/uix/morfo/components/combobox.ts | 1 + src/uix/morfo/components/cropper.ts | 1 + src/uix/morfo/components/css-field.ts | 1 + src/uix/morfo/components/date-field.ts | 1 + src/uix/morfo/components/dialog.ts | 1 + src/uix/morfo/components/drawer.ts | 1 + src/uix/morfo/components/editable.ts | 1 + src/uix/morfo/components/file-upload.ts | 1 + src/uix/morfo/components/float-panel.ts | 1 + src/uix/morfo/components/form.ts | 1 + src/uix/morfo/components/image-adjustments.ts | 1 + src/uix/morfo/components/image-picker.ts | 1 + src/uix/morfo/components/number-field.ts | 1 + src/uix/morfo/components/pagination.ts | 1 + src/uix/morfo/components/popover.ts | 1 + src/uix/morfo/components/radio-group.ts | 1 + src/uix/morfo/components/rating-group.ts | 1 + src/uix/morfo/components/search-field.ts | 1 + src/uix/morfo/components/select.ts | 1 + src/uix/morfo/components/slider.ts | 16 +++++++++++++--- src/uix/morfo/components/stepper.ts | 1 + src/uix/morfo/components/tabs.ts | 1 + src/uix/morfo/components/tag-group.ts | 1 + src/uix/morfo/components/tags-input.ts | 1 + src/uix/morfo/components/toast.ts | 1 + src/uix/morfo/components/toolbar.ts | 1 + 31 files changed, 43 insertions(+), 4 deletions(-) diff --git a/docs/process/PLAN-component-coherence.md b/docs/process/PLAN-component-coherence.md index a7346cf88..de9acbdb7 100644 --- a/docs/process/PLAN-component-coherence.md +++ b/docs/process/PLAN-component-coherence.md @@ -41,7 +41,7 @@ Por dimensión: | **C2** | **Estados error-severity**. **(a) R-1.5 HECHA 2026-07-02**: el audit ahora reconoce las 4 evidencias sancionadas (`:focus-visible` · `:focus-within` · `:has(:focus)` · `[data-focused]` canónico) — añadir rings por recipe habría sido el anti-patrón "double border" (archetypes.css:164 ya da ring universal por arquetipo); 6 excepciones citadas (css-field/number-field → spin-field compartido; date-picker/dialog → foundation+content-exclusion; metrics/timeline → sin parte enfocable); clipboard+announce esperan su README (C8). **(b) R-1.2/1.3/1.4 HECHAS 2026-07-03**: válvula cableada también en R-1.3/R-1.4; fixes REALES verificados en navegador (switch/toggle/rating-group invalid=threat-element como checkbox·pin-input; toggle+pin-input readonly; spin-field readonly NUEVO en la capa compartida; ntp disabled); excepciones citadas (spin-field para css/number-field; dialog provider virtual; color-picker→color-field; date-range-field overlay de attrs; time-picker→time-field). ntp conserva R-1.3/R-1.4 warn para su pasada C8. media-player NO tocado (foráneo) | **HECHA** | | **C3** | **R-2.7 tipografía literal HECHA 2026-07-03** — 16 recipes: 8 font-weights → `var(--font-weight-*)` (verificado resolve=500 en navegador); ~13 proporcionales/identidad anotados `/* literal: */` con razón (avatar ∝ diámetro; `calc(1em − (md−sm))` = label one-step-down con delta token-derived; `1em` identidad de contexto; captions ×0.85; optical −1px) | **HECHA** | | **C4** | **A-1.4 APG HECHA 2026-07-03** — 20 morfos con patrón real (pickers/command/search→combobox · date-field→spinbutton espejo de time-field · rating→radio · nav-menu→disclosure · splitter→windowsplitter · tag-group/tags-input/virtual-grid→grid · file-upload/clipboard→button · announce→alert · form→landmark · image-adjustments→slider · table→table); válvula A-1.4 + notas en 5 READMEs (cropper·pagination·stepper·timeline·image-picker — sin patrón APG real). Restan 4 esperados: card/drag-drop/virtual-list (nota aterriza con su README en C8) + media-player (foráneo) | **HECHA** | -| **C5** | **Sema `expression:`** (~41 morfos con eventos sin declarar) — clasificar pack / family-default / delegated / none por componente (criterios D.4 de book-deviations); `persistence`/`a11ySemantic` donde el evento lo pida (tabla D.9 como referencia) | pendiente | +| **C5** | **Sema `expression:` HECHA 2026-07-03** — los 30 morfos eventful sin declaración TODOS tienen pack en `sema/components/` → `expression: 'pack'` (clasificación sin ambigüedad; D.4). `morfo:vocabulary` limpio (solo warn naming de media-player, foráneo). `persistence`/`a11ySemantic` adicionales NO añadidos de oficio — la tabla D.9 es referencia y los 6 consumidores reales ya declaran; ampliar solo con caso real | **HECHA** | | **C6** | **Paleta universal** — `paletteScaleDecls` pasa de helper manual (7 usos) a paso del generador para toda recipe con tokens `palette-*` + regen + guard ("data-color sin cascade de paleta = error") | pendiente | | **C7** | **Size-bundle sweep** (~90 recipes; patrón theming/reference §5, pilot toggle) + guard "recipe que re-declara size→font = warn" | pendiente | | **C8** | **READMEs** — 28 nuevos + F-1.x incompletos (~18). Baseline/Comparativa (≥3 refs reales)/Decisiones/Gaps; passive → justificación. El más pesado: requiere investigación por componente | pendiente | diff --git a/src/uix/morfo/components/accordion.ts b/src/uix/morfo/components/accordion.ts index 5831f46e0..750abdd3a 100644 --- a/src/uix/morfo/components/accordion.ts +++ b/src/uix/morfo/components/accordion.ts @@ -5,6 +5,7 @@ export const accordionMorfo = { name: 'Accordion', kebab: 'accordion', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', texts: { label: '#?components.accordion.label|Accordion' }, diff --git a/src/uix/morfo/components/calendar.ts b/src/uix/morfo/components/calendar.ts index 64d78bcc4..f0098c6a5 100644 --- a/src/uix/morfo/components/calendar.ts +++ b/src/uix/morfo/components/calendar.ts @@ -5,6 +5,7 @@ export const calendarMorfo = { name: 'Calendar', kebab: 'calendar', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', apg: 'https://www.w3.org/WAI/ARIA/apg/patterns/grid/', texts: { label: '#?components.calendar.label|Calendar' diff --git a/src/uix/morfo/components/checkbox.ts b/src/uix/morfo/components/checkbox.ts index 4f8017761..b6f30af3a 100644 --- a/src/uix/morfo/components/checkbox.ts +++ b/src/uix/morfo/components/checkbox.ts @@ -5,6 +5,7 @@ export const checkboxMorfo = { name: 'Checkbox', kebab: 'checkbox', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', texts: { label: '#?components.checkbox.label|Checkbox' }, diff --git a/src/uix/morfo/components/color-picker.ts b/src/uix/morfo/components/color-picker.ts index 0d08998ff..046c24e4a 100644 --- a/src/uix/morfo/components/color-picker.ts +++ b/src/uix/morfo/components/color-picker.ts @@ -5,6 +5,7 @@ export const colorPickerMorfo = { name: 'ColorPicker', kebab: 'color-picker', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', apg: 'https://www.w3.org/WAI/ARIA/apg/patterns/combobox/', events: [ { diff --git a/src/uix/morfo/components/combobox.ts b/src/uix/morfo/components/combobox.ts index c780981de..0fa371bb5 100644 --- a/src/uix/morfo/components/combobox.ts +++ b/src/uix/morfo/components/combobox.ts @@ -5,6 +5,7 @@ export const comboboxMorfo = { name: 'Combobox', kebab: 'combobox', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', apg: 'https://www.w3.org/WAI/ARIA/apg/patterns/combobox/', texts: { label: '#?components.combobox.label|Combobox', diff --git a/src/uix/morfo/components/cropper.ts b/src/uix/morfo/components/cropper.ts index 4454a8c80..6e8cf1712 100644 --- a/src/uix/morfo/components/cropper.ts +++ b/src/uix/morfo/components/cropper.ts @@ -18,6 +18,7 @@ export const cropperMorfo = { name: 'Cropper', kebab: 'cropper', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', texts: { label: '#?components.cropper.label|Crop image', selection: '#?components.cropper.selection|Crop region' diff --git a/src/uix/morfo/components/css-field.ts b/src/uix/morfo/components/css-field.ts index bb5f60c88..23e26df7b 100644 --- a/src/uix/morfo/components/css-field.ts +++ b/src/uix/morfo/components/css-field.ts @@ -5,6 +5,7 @@ export const cssFieldMorfo = { name: 'CssField', kebab: 'css-field', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', texts: { label: '#?components.css-field.label|CSS Value', invalid: '#?components.css-field.invalid|Invalid CSS value' diff --git a/src/uix/morfo/components/date-field.ts b/src/uix/morfo/components/date-field.ts index ba0b51ba9..46e850202 100644 --- a/src/uix/morfo/components/date-field.ts +++ b/src/uix/morfo/components/date-field.ts @@ -5,6 +5,7 @@ export const dateFieldMorfo = { name: 'DateField', kebab: 'date-field', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', apg: 'https://www.w3.org/WAI/ARIA/apg/patterns/spinbutton/', texts: { label: '#?components.date-field.label|Date Field', diff --git a/src/uix/morfo/components/dialog.ts b/src/uix/morfo/components/dialog.ts index 8a25241b5..84cdead34 100644 --- a/src/uix/morfo/components/dialog.ts +++ b/src/uix/morfo/components/dialog.ts @@ -16,6 +16,7 @@ export const dialogMorfo = { name: 'Dialog', kebab: 'dialog', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', apg: 'https://www.w3.org/WAI/ARIA/apg/patterns/dialog/', texts: { label: '#?components.dialog.label|Dialog', diff --git a/src/uix/morfo/components/drawer.ts b/src/uix/morfo/components/drawer.ts index ec2498e37..b8aa6608a 100644 --- a/src/uix/morfo/components/drawer.ts +++ b/src/uix/morfo/components/drawer.ts @@ -5,6 +5,7 @@ export const drawerMorfo = { name: 'Drawer', kebab: 'drawer', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', apg: 'https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/', texts: { label: '#?components.drawer.label|Drawer', diff --git a/src/uix/morfo/components/editable.ts b/src/uix/morfo/components/editable.ts index 1f29a9eb7..1adaaab72 100644 --- a/src/uix/morfo/components/editable.ts +++ b/src/uix/morfo/components/editable.ts @@ -5,6 +5,7 @@ export const editableMorfo = { name: 'Editable', kebab: 'editable', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', texts: { label: '#?components.editable.label|Editable' }, diff --git a/src/uix/morfo/components/file-upload.ts b/src/uix/morfo/components/file-upload.ts index 361d78b2e..55a050b45 100644 --- a/src/uix/morfo/components/file-upload.ts +++ b/src/uix/morfo/components/file-upload.ts @@ -5,6 +5,7 @@ export const fileUploadMorfo = { name: 'FileUpload', kebab: 'file-upload', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', apg: 'https://www.w3.org/WAI/ARIA/apg/patterns/button/', texts: { label: '#?components.file-upload.label|File Upload', diff --git a/src/uix/morfo/components/float-panel.ts b/src/uix/morfo/components/float-panel.ts index 2dcf47f54..e8c173f55 100644 --- a/src/uix/morfo/components/float-panel.ts +++ b/src/uix/morfo/components/float-panel.ts @@ -5,6 +5,7 @@ export const floatPanelMorfo = { name: 'FloatPanel', kebab: 'float-panel', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', apg: 'https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/', texts: { label: '#?components.float-panel.label|Floating panel', diff --git a/src/uix/morfo/components/form.ts b/src/uix/morfo/components/form.ts index b6d2a5628..001efc7ab 100644 --- a/src/uix/morfo/components/form.ts +++ b/src/uix/morfo/components/form.ts @@ -5,6 +5,7 @@ export const formMorfo = { name: 'Form', kebab: 'form', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', apg: 'https://www.w3.org/WAI/ARIA/apg/patterns/landmarks/examples/form.html', texts: { label: '#?components.form.label|Form', diff --git a/src/uix/morfo/components/image-adjustments.ts b/src/uix/morfo/components/image-adjustments.ts index 4beba3a85..a54217d06 100644 --- a/src/uix/morfo/components/image-adjustments.ts +++ b/src/uix/morfo/components/image-adjustments.ts @@ -16,6 +16,7 @@ export const imageAdjustmentsMorfo = { name: 'ImageAdjustments', kebab: 'image-adjustments', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', apg: 'https://www.w3.org/WAI/ARIA/apg/patterns/slider/', texts: { label: '#?components.image-adjustments.label|Image adjustments', diff --git a/src/uix/morfo/components/image-picker.ts b/src/uix/morfo/components/image-picker.ts index 7afa3d560..74c40098b 100644 --- a/src/uix/morfo/components/image-picker.ts +++ b/src/uix/morfo/components/image-picker.ts @@ -18,6 +18,7 @@ export const imagePickerMorfo = { name: 'ImagePicker', kebab: 'image-picker', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', texts: { label: '#?components.image-picker.label|Image picker', prompt: '#?components.image-picker.prompt|Drop an image or click to browse', diff --git a/src/uix/morfo/components/number-field.ts b/src/uix/morfo/components/number-field.ts index ebf9b5bab..8f3d1901c 100644 --- a/src/uix/morfo/components/number-field.ts +++ b/src/uix/morfo/components/number-field.ts @@ -5,6 +5,7 @@ export const numberFieldMorfo = { name: 'NumberField', kebab: 'number-field', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', texts: { label: '#?components.number-field.label|Number Field' }, diff --git a/src/uix/morfo/components/pagination.ts b/src/uix/morfo/components/pagination.ts index c84b3ac1c..339d5f74c 100644 --- a/src/uix/morfo/components/pagination.ts +++ b/src/uix/morfo/components/pagination.ts @@ -5,6 +5,7 @@ export const paginationMorfo = { name: 'Pagination', kebab: 'pagination', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', texts: { label: '#?components.pagination.label|Pagination', first: '#?components.pagination.first|First page', diff --git a/src/uix/morfo/components/popover.ts b/src/uix/morfo/components/popover.ts index f462a32d5..1bc282555 100644 --- a/src/uix/morfo/components/popover.ts +++ b/src/uix/morfo/components/popover.ts @@ -5,6 +5,7 @@ export const popoverMorfo = { name: 'Popover', kebab: 'popover', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', apg: 'https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/', texts: { label: '#?components.popover.label|Popover' diff --git a/src/uix/morfo/components/radio-group.ts b/src/uix/morfo/components/radio-group.ts index fe339830d..5be40adeb 100644 --- a/src/uix/morfo/components/radio-group.ts +++ b/src/uix/morfo/components/radio-group.ts @@ -5,6 +5,7 @@ export const radioGroupMorfo = { name: 'RadioGroup', kebab: 'radio-group', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', texts: { label: '#?components.radio-group.label|Radio Group' }, diff --git a/src/uix/morfo/components/rating-group.ts b/src/uix/morfo/components/rating-group.ts index 11f22707b..f162f400b 100644 --- a/src/uix/morfo/components/rating-group.ts +++ b/src/uix/morfo/components/rating-group.ts @@ -5,6 +5,7 @@ export const ratingGroupMorfo = { name: 'RatingGroup', kebab: 'rating-group', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', apg: 'https://www.w3.org/WAI/ARIA/apg/patterns/radio/', texts: { label: '#?components.rating-group.label|Rating' diff --git a/src/uix/morfo/components/search-field.ts b/src/uix/morfo/components/search-field.ts index b2b3e1ce8..e7a8e3797 100644 --- a/src/uix/morfo/components/search-field.ts +++ b/src/uix/morfo/components/search-field.ts @@ -5,6 +5,7 @@ export const searchFieldMorfo = { name: 'SearchField', kebab: 'search-field', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', apg: 'https://www.w3.org/WAI/ARIA/apg/patterns/combobox/', texts: { label: '#?components.search-field.label|Search', diff --git a/src/uix/morfo/components/select.ts b/src/uix/morfo/components/select.ts index c530850bf..207b278fb 100644 --- a/src/uix/morfo/components/select.ts +++ b/src/uix/morfo/components/select.ts @@ -5,6 +5,7 @@ export const selectMorfo = { name: 'Select', kebab: 'select', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', texts: { label: '#?components.select.label|Select' }, diff --git a/src/uix/morfo/components/slider.ts b/src/uix/morfo/components/slider.ts index df60511ac..90240af8a 100644 --- a/src/uix/morfo/components/slider.ts +++ b/src/uix/morfo/components/slider.ts @@ -5,6 +5,7 @@ export const sliderMorfo = { name: 'Slider', kebab: 'slider', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', texts: { label: '#?components.slider.label|Slider' }, @@ -54,7 +55,8 @@ export const sliderMorfo = { values: ['horizontal', 'vertical'], value: v.propRef('orientation') }, - { attr: 'data-disabled', value: v.propRef('disabled'), severity: 'optional' } + { attr: 'data-disabled', value: v.propRef('disabled'), severity: 'optional' }, + { attr: 'data-readonly', value: v.propRef('readonly'), severity: 'optional' } ], aria: [ { @@ -83,7 +85,8 @@ export const sliderMorfo = { values: ['horizontal', 'vertical'], value: v.propRef('orientation') }, - { attr: 'data-disabled', value: v.propRef('disabled'), severity: 'optional' } + { attr: 'data-disabled', value: v.propRef('disabled'), severity: 'optional' }, + { attr: 'data-readonly', value: v.propRef('readonly'), severity: 'optional' } ], aria: [] }, @@ -97,7 +100,8 @@ export const sliderMorfo = { optional: false, data: [ { attr: 'data-orientation', values: ['horizontal', 'vertical'] }, - { attr: 'data-disabled', severity: 'optional' } + { attr: 'data-disabled', severity: 'optional' }, + { attr: 'data-readonly', severity: 'optional' } ], aria: [ { attr: 'aria-valuemin', value: v.propRef('min') }, @@ -115,6 +119,12 @@ export const sliderMorfo = { value: v.literal('true'), severity: 'optional', condition: { when: 'prop-truthy', prop: 'disabled' } + }, + { + attr: 'aria-readonly', + value: v.literal('true'), + severity: 'optional', + condition: { when: 'prop-truthy', prop: 'readonly' } } ], keyboard: [ diff --git a/src/uix/morfo/components/stepper.ts b/src/uix/morfo/components/stepper.ts index 22c0324e7..d48471070 100644 --- a/src/uix/morfo/components/stepper.ts +++ b/src/uix/morfo/components/stepper.ts @@ -5,6 +5,7 @@ export const stepperMorfo = { name: 'Stepper', kebab: 'stepper', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', texts: { label: '#?components.stepper.label|Stepper', list: '#?components.stepper.list|Steps' diff --git a/src/uix/morfo/components/tabs.ts b/src/uix/morfo/components/tabs.ts index 37a282655..1b102faa5 100644 --- a/src/uix/morfo/components/tabs.ts +++ b/src/uix/morfo/components/tabs.ts @@ -5,6 +5,7 @@ export const tabsMorfo = { name: 'Tabs', kebab: 'tabs', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', texts: { label: '#?components.tabs.label|Tabs' }, diff --git a/src/uix/morfo/components/tag-group.ts b/src/uix/morfo/components/tag-group.ts index 5a4e466d2..16c62a70e 100644 --- a/src/uix/morfo/components/tag-group.ts +++ b/src/uix/morfo/components/tag-group.ts @@ -5,6 +5,7 @@ export const tagGroupMorfo = { name: 'TagGroup', kebab: 'tag-group', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', apg: 'https://www.w3.org/WAI/ARIA/apg/patterns/grid/', texts: { label: '#?components.tag-group.label|Tags', diff --git a/src/uix/morfo/components/tags-input.ts b/src/uix/morfo/components/tags-input.ts index 66ff51e27..43c5caa89 100644 --- a/src/uix/morfo/components/tags-input.ts +++ b/src/uix/morfo/components/tags-input.ts @@ -5,6 +5,7 @@ export const tagsInputMorfo = { name: 'TagsInput', kebab: 'tags-input', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', apg: 'https://www.w3.org/WAI/ARIA/apg/patterns/grid/', texts: { label: '#?components.tags-input.label|Tags Input', diff --git a/src/uix/morfo/components/toast.ts b/src/uix/morfo/components/toast.ts index f872cfec3..1102b94c2 100644 --- a/src/uix/morfo/components/toast.ts +++ b/src/uix/morfo/components/toast.ts @@ -6,6 +6,7 @@ export const toastMorfo = { name: 'Toast', kebab: 'toast', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', apg: 'https://www.w3.org/WAI/ARIA/apg/patterns/alert/', texts: { label: '#?components.toast.label|Toast' diff --git a/src/uix/morfo/components/toolbar.ts b/src/uix/morfo/components/toolbar.ts index 986f0a2d9..6f49ed8da 100644 --- a/src/uix/morfo/components/toolbar.ts +++ b/src/uix/morfo/components/toolbar.ts @@ -5,6 +5,7 @@ export const toolbarMorfo = { name: 'Toolbar', kebab: 'toolbar', scope: ['soma', 'sema', 'eidos'], + expression: 'pack', texts: { label: '#?components.toolbar.label|Toolbar' },