User wants Chakra-style behavior: the chip in the demo should drive
both the visible segments in the input AND the calendar view
(year-only grid, month-only grid, day calendar). The previous
'readonly segments' chip was a Bits-style input lock, not a picker
kind switch — wrong contract.
This commit lays the scaffolding for the Chakra model, in four
incremental landings (A → D). It's the FIRST landing.
What changes:
- DatePickerKind type ('date' | 'month' | 'year') exported from soma.
- DatePickerProvider opts gain a `kind` slot; the root .svelte
defaults to 'date' and threads it through readableActive.
- exports.ts surfaces the new type alongside DatePickerMode.
- The root <div data-date-picker> emits data-kind for downstream
CSS / picker parts to consume in subsequent commits.
- Test fixture extended with the new opt.
Demo (date-picker single):
- Chip control renamed from 'readonly segments' to 'kind' with values
date / month / year. A hint shows the current input format
(YYYY / MM/YYYY / MM/DD/YYYY).
- New `visibleDateParts` derived set drives a `filterByKind` helper
applied to the segment snippet, so the input renders the right
subset on first selection. Literals (separators) between dropped
parts are removed; leading/trailing literals are trimmed.
- Snippet code refs updated: closeButton && readonlySegments line
replaced with kind !== 'date' && ` kind="${kind}"`.
What this commit DOES NOT do (next commits):
- B: render the year-grid in the calendar popover when data-kind=year.
- C: render the month-grid when data-kind=month.
- D: propagate to date-range-picker + wire the soma date-field to
drop segments based on kind (currently the filter lives in the
demo snippet — works for single-picker but isn't a contract for
third-party consumers).
Verification: 0 type errors. Browser-confirmed: clicking 'year' chip
collapses the input to a single '2026' segment and stamps
data-kind='year' on the picker root. Calendar still shows day-grid
(that's commit B's scope). 67/67 component:audit PASS.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User report: 'no veo que haga nada nuevo en la demo' — the new
canonical Footer/Clear/Cancel/Close parts existed but were invisible
on first load because every *Button toggle defaulted to false and the
popovers opened closed. The user reloaded, saw the same baseline as
before, and concluded nothing had landed.
Fix:
- date-picker demo: open=true, closeOnDateSelect=false (so the user
can pick a date without the popover collapsing while exploring),
clearButton=true, cancelButton=true, closeButton=true. Result:
on first load the calendar is visible with 'Borrar fecha · Cancelar
· Cerrar' below it, in the active locale.
- date-range-picker demo: same — open=true, clear/cancel/close all
default to true. The comment claiming the popover should stay
closed because 'segments are for keyboard entry' is replaced; the
demo's purpose is to surface the API, and the segments are still
reachable by Tab-focus even when the popover is open.
These changes are demo-only — the actual provider defaults stay
inline/false. Production consumers must opt in.
0 type errors. Browser verified: Footer renders with all three
i18n-driven labels at the bottom of the calendar.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User report: setting startReadonlySegments/endReadonlySegments in the
date-range-picker demo didn't change anything visible, and the
date-picker demo had no equivalent control.
Root cause #1 (visible): the readonly segment styling was
background: var(--color-neutral-track) — primitive-neutral-3 — too
close to the surface background in both themes. Mechanically the lock
was working (data-readonly stamped, keyboard updates blocked) but the
visual delta was invisible.
Fix: add segment-readonly-color + segment-readonly-decoration tokens
to the recipe. Read-only segments now render with
- background: var(--color-surface-overlay) (clearer step from default)
- color: var(--color-content-secondary) (slightly faded)
- text-decoration: underline dotted var(--color-content-muted)
- cursor: default
both in date-field.css and the date-range-picker per-component
override.
Root cause #2 (consistency): date-picker demo lacked the segment-lock
chip group that the range demo has. Added segmentLock state +
readonly-segments derived array (year / monthYear / date presets) +
chip control + snippet integration. Now both pickers expose the prop
identically.
Verification (browser): clicking the 'monthYear' chip in date-picker
demo stamps data-readonly on the month + year segments; the dotted
underline + lighter color are visible. Range demo: clicking 'year'
under 'start segments' affects only the start year segment, end stays
editable.
0 type errors, 67/67 component:audit PASS.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The footer affordances (Clear / Cancel / Close) lived inline in each
demo as picker-actions.svelte. Promoted to canonical Eidos surface so
the API is consistent and the contract is auditable.
morfo:
- date-picker + date-range-picker: added Footer (archetype 'footer'),
Clear/Cancel/Close (archetype 'trigger', kind 'public', optional).
Each part declares aria-label via idlangref and data-action="…".
- types.ts: added 'footer' to ARCHETYPE_VOCABULARY array + the
MorfoArchetype union (the type had it, the runtime list did not).
- lang catalogs: added clear / cancel / close idlangref entries to
date-picker.ts and date-range-picker.ts.
eidos:
- Created date-picker-footer/clear/cancel/close.svelte and the range
mirrors. Each part:
- Pulls the provider from context via DatePickerProvider.require()
(analogous DateRangePickerProvider.require() for the range).
- Renders nothing when the corresponding *Button opt is false; the
Close part stays visible whenever mode === 'modal' (modal pickers
need a way out — outside-click and Escape are blocked).
- Emits data-{component}-{part} + data-action so the recipe selector
matches the morfo declaration.
- aria-label resolves via uix.langs.ts('#?components.X.{action}|…').
- onclick calls provider.clear() / cancel() / commit() then forwards
any consumer-supplied onclick.
- index.ts barrels: registered Footer/Clear/Cancel + replaced
Close (was popover-close.svelte) with the new picker-aware Close.
Soma popover-close still drives the old aria; date-picker's Close
adds the modal-conditional visibility + commit semantics.
- *.css: folded the inline picker-actions styles into the recipes —
data-{name}-footer flex row + data-{name}-{clear,cancel,close}
buttons (clear/cancel margin-inline-end: auto so close sits flush
right). prefers-reduced-motion already covered.
soma:
- date-picker exports.ts: surfaces DatePickerProvider + DatePickerMode
+ datePickerAttrs so the Eidos parts can consume them (mirrors what
date-range-picker already exposed). No new behavior — just plumbing.
demo:
- date-picker + date-range-picker demos: replaced
<PickerActions /> with <DatePicker.Footer>
<DatePicker.Clear /><DatePicker.Cancel /><DatePicker.Close />
</DatePicker.Footer>. The two picker-actions.svelte files are
deleted.
Verification: 0 type errors, morfo:check PASS (Footer parts not
required in DOM; conditional visibility honored), component:audit
67/67 PASS.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replicates the date-range-picker pattern on the single date-picker:
soma:
- DatePickerMode type ('inline' | 'modal') exported.
- Provider gains mode/clearButton/cancelButton/closeButton opts plus
commit() / cancel() / clear() action helpers; cancel() reverts the
value snapshot taken via watch() on the OPEN edge of opts.open.
- handleDateSelect now early-returns when mode === 'modal' so modal
pickers don't auto-close on selection.
- exports.ts surfaces DatePickerProvider + DatePickerMode for demo /
footer wiring (mirrors date-range-picker's barrel).
- Test fixture extended with the new opts (closeOnDateSelect stays
true by default; mode='inline', all buttons false).
morfo:
- scope=['soma','sema'], apg=dialog-modal.
- 6 events: open / close-commit / close-cancel / close-dismiss /
close-dismiss-outside / commit-clear. prewrite stamps data-last-action
with the causal exit reason so Sema can tint the exit animation.
- Calendar part declares the state machine (open/closed,
data-last-action, data-starting-style, data-ending-style) and the
modal-keyboard surface (Escape / Tab / Shift+Tab).
demo:
- New picker-actions.svelte mirrors the range demo: pulls provider via
context, renders Clear / Cancel / Close conditionally, forces Close in
modal mode. CSS is local to the file (uses --color-primary-* +
--color-surface-overlay tokens, no raw colors).
- +page.svelte adds mode radio chip group + 3 footer-button switches and
passes mode/clearButton/cancelButton/closeButton through to the Eidos
wrapper (which spreads to soma).
The Eidos wrapper needs no change — it already spreads everything via
...rest, so the new soma opts reach Soma without further wiring.
Verification: 0 type errors, 3/3 soma tests pass, morfo:check PASS,
component:audit 67/67 PASS.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The clear affordance is the standard search-input pattern: an X glyph
inside the input, with the localized "Clear" / "Borrar" string moved
to `aria-label`. Showing the word "Borrar" inline and then a hover
button on top read as two redundant affordances.
Replaced the visible text with `<X size="1em" decorative />` from the
icon set. The aria-label still pulls from
`'#?components.search-field.clear|Clear'` so screen readers get the
localized affordance name.
Code snippets in the API panel updated to match the new pattern
(`<SearchField.ClearTrigger aria-label="Clear"><X ... /></...>`)
instead of the text-only variant.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The demo hardcoded the visible Clear button label inline:
<SearchField.ClearTrigger>Clear</SearchField.ClearTrigger>
So the toolbar's locale toggle (ES/EN) only swapped the aria-label
via the morfo's translationRef, not the visible text. With locale=ES
the panel showed a Spanish UI everywhere except the literal "Clear".
Replace with `uix.langs.ts('#?components.search-field.clear|Clear')`
so the visible button picks up the active catalog entry. Catalog
strings shortened to just `Borrar` / `Clear` (one-word action label
that fits the inline-button affordance — the previous "Borrar
búsqueda" was too long for the visible chrome and wrapped in two
lines). The full phrase still reads naturally for aria-label.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The demo seeded `cover.png` with `new Uint8Array(size)` — empty
bytes. `URL.createObjectURL` then handed a blob the browser couldn't
decode as PNG, so the preview rendered the user-agent broken-image
icon next to the row.
Fix: when the demo file type starts with `image/`, prepend a valid
1x1 transparent PNG header before the size-padding bytes. The
browser decodes a 1px image cleanly and the preview slot stays
empty-looking (which is the intended visual) instead of showing a
broken-image affordance. atob lives inside the `typeof File`
runtime check so SSR doesn't choke on it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User feedback batch from incidencia 2026-05-20:
1. Modal vs inline mode
- New `mode: 'inline' | 'modal'` prop on the soma `DateRangePicker.Provider`.
- Modal wires the popover `modal: true` → outside-click and Escape are
ignored; user must commit via the footer Close button (or revert via
Cancel).
- Provider exposes helpers `clear()`, `cancel()`, `commit()` for the
footer. `cancel()` restores the value snapshot taken on the OPEN edge
(captured via a `watch` on `opts.open` true-edge transition).
2. Footer buttons as boolean props
- `clearButton`, `cancelButton`, `closeButton` props on the picker.
The footer renders only if at least one is true. In `mode='modal'`
the closeButton is forced on (the user always needs an exit).
- `picker-actions.svelte` in the demo reads the picker context via
`DateRangePickerProvider.require()` and renders the enabled buttons
against `provider.clear/cancel/commit`.
3. Range field shape (revert to Chakra-style two boxes)
- Removed the `data-date-range-field-group` wrapper from the demo so
the start and end inputs are rendered as two separate boxed fields
with the icon embedded in the end box, matching Chakra's layout.
- The recipe CSS rules for `data-date-range-field-group` stay
available as an opt-in for consumers who prefer the unified pill.
4. Demo defaults
- `open` starts at `false` so the picker exercises the real
open/close flow when the user clicks the trigger — the segments are
for direct keyboard entry, the popover is for visual exploration.
- Mode toggle (inline / modal) + footer button switches surfaced as
controls in the demo.
5. Plumbing
- DateRangePickerOpts gains `mode`, `clearButton`, `cancelButton`,
`closeButton` (StateProps for mode, ActiveProps for the booleans).
- `DateRangePickerProvider` and `DateRangePickerMode` are now re-
exported from the soma barrel for consumers that wire footer
actions in the calendar tree.
- Test factory updated to seed the new opts.
Verified in browser: trigger opens; click-outside in modal mode is
ignored; Close commits & closes; Cancel reverts to snapshot & closes;
Clear empties the range & keeps open. Heading "May – June 2026" (year
collapse) and centered per-calendar titles still working from the
previous commit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes incidencia point B (range field shouldn't show two separate
boxes). New CSS contract: `[data-date-range-field-group]` on the
wrapper element collapses the chrome around its descendants:
- The group owns the border, background, focus ring, invalid state,
variant/ghost treatment, and disabled opacity.
- Each `[data-date-range-field-input]` inside loses its own border,
background, and padding; it just contributes its segments to the
shared row.
- The "to" separator (.range-separator or `[data-range-separator]`)
picks up the field's literal tone.
- The embedded trigger from the previous commit sits flush at the
trailing edge of the group, since `data-date-field-input` already
detects the embedded case.
Demo: add `data-date-range-field-group` to the `.range-field-row`
wrapper. Visually now reads as one input pill: `05 / 20 / 2026 to
06 / 03 / 2026 📅`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes incidencia points D + E:
- Heading collapses the shared year when both visible months are the
same year: "May – June 2026" instead of "May 2026 – June 2026".
Cross-year still renders both ends explicitly ("June 2025 – July
2026"). Implemented in `RangeCalendarProvider.headingValue`.
- date-range-picker demo: drop the per-month title that previously
rendered BELOW each grid (duplicate of the main heading). Instead,
render one centered title per visible calendar INSIDE the header
bar between the prev/next buttons. The grid below now shows just
the weeks. A `range-header-titles` grid container splits the
available header space equally across `month-count` titles, so
each label sits centered over its calendar grid.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The calendar icon now lives INSIDE the date input as a flush trailing
affordance, not as a separate sibling button. The change is markup +
recipe CSS only — no API change.
- Demo (date-picker): drop the `.picker-control` grid wrapper; put
`<DatePicker.Trigger />` as the last child of the Input snippet,
after the segments. Codeblock snippets updated to match.
- Demo (date-range-picker): keep the dual-input + separator layout
but move `<DateRangePicker.Trigger />` into the END input, since
the trigger represents the trailing affordance of the whole range.
Both code snippets updated.
- Recipe CSS (both): add nested rules that detect the embedded case
(`[data-date-field-input] > [data-date-picker-trigger]`,
`[data-date-range-field-input] > [data-date-range-picker-trigger]`).
Embedded trigger drops its own border/background, matches the
segment height, sits flush at the trailing edge with
`margin-inline-start: auto`, and uses the segment focus ring on
hover/focus instead of the standalone field ring.
The standalone (sibling) treatment stays available for consumers who
prefer the icon as a separate control.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two new Eidos components shipped end-to-end (wrapper + recipe + demo +
Soma provider hardening) plus a checklist-driven audit pipeline that
scores all 67 morfo components against doctrinal completion criteria.
New components:
- date-picker: full popover-anchored picker over date-field + calendar,
with calendar/content/trigger parts and demo route.
- date-range-picker: standalone wrapper with own calendar/grid/segment
surface, demo route, and recipe CSS.
- Both wrappers follow Option C disciplined (root + parts attached via
explicit assignment, no Object.assign).
Supporting Soma changes:
- range-calendar provider tightened (211 LOC of behavior, 167 LOC of
tests), README brought up to component doctrine.
- date-field, date-picker, date-range-field, date-range-picker Soma
providers + READMEs updated for new wrappers.
- popover provider/close gain props needed by the picker wrappers.
Morfo updates:
- date-picker / date-range-picker / range-calendar morfos refined for
the new APIs (parts, events, ARIA).
Audit infrastructure (new):
- src/uix/COMPONENT_COMPLETION_CHECKLIST.md — 81 doctrinal rules across
morfo / eidos wrapper / recipe CSS / demo / README / cross-layer
scripts. Each rule keyed to active_architecture.md, GUIA_IMPLEMENTACION
and DEMO_AUTHORING_GUIDE.
- scripts/component-audit.ts + `npm run component:audit` — regex parser
over all 67 components, emits tmp/component-audit.md with summary
scoreboard + per-component findings. Validates against canonical
SEMA_FAMILIES / SEMA_VERBS / ARCHETYPE_VOCABULARY / INTENTS.
- Initial baseline: 1 PASS, 64 NEEDS-WORK, 2 BROKEN (tooltip,
date-range-picker). Top systemic gaps: translations.label (49),
README Gaps/Comparativa/Baseline sections (87 combined), keyboard
/event ratio under-declaration (15), apg URL absent (19).
Misc:
- src/uix/kimi-audit-eidos.md — supplementary audit notes.
- .gitignore: ignore .codex-* agent scratch artifacts at repo root.
- continue.md + READMEs updated through the migration.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>