astra
alpha-0.1-background
alpha-0.1-dir-prefs
alpha-0.1-sec-dom
menubar-v4-safe
active-uix
morfo-runtime
morfo-driven-soma
semantuix
glm-5
main
sium-v1.0
${ noResults }
72 Commits (87e3d68f866e9cb59c0dff99915a6734f8cfe99f)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
30e9517a9a |
feat(combobox): SelectedTags inline chips for multi mode
New parts `<Combobox.SelectedTags>`, `<Combobox.SelectedTag>` and `<Combobox.SelectedTagRemove>` render the current selection as removable chips inside `<Combobox.Control>` when `type='multiple'` (Material UI Autocomplete pattern): - Morfo: 3 new public parts with kebab names + texts entries. - Langs: es/en entries `selected-tags` / `selected-tag-remove`. - Soma: `ComboboxSelectedTagsProvider` exposes `items` derived from the provider's label registry; `ComboboxSelectedTagProvider` surfaces `data-value` / `data-label`; `ComboboxSelectedTagRemoveProvider` resolves its parent tag via context and calls `provider.removeValue` on click. Provider gains `getSelectedItems()` and `removeValue()` helpers; `selectedTagsEmpty` runtime prop drives `[data-empty]`. - Soma item registry: stop unregistering labels on Item cleanup so chips keep proper labels after the listbox closes/lazy-unmounts. - Eidos: default snippet loops items and renders a chip + X icon per entry; consumer can override the snippet for custom rendering. - CSS: chip styling, wrap-friendly Control with breathing padding when chips are present, ClearButton pinned right via auto margin, input flex basis reduced to 4rem so it shares a row with chips. - Demo: renders `<Combobox.SelectedTags />` inside Control when `mode === 'multiple'`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
26daa9e22e |
feat(search-field demo): full keyboard nav for results popover
Follows the WAI-ARIA combobox pattern via aria-activedescendant — the input keeps DOM focus, highlight moves virtually: - ArrowDown / ArrowUp: open popover if closed, cycle highlight (wrapping at edges). - Home / End: jump to first / last (when open). - Enter: commit the highlighted item, close popover. - Escape: close popover without clearing value; Soma's clearOnEscape fires only when popover is already closed. - pointermove on a result moves the highlight to it (mouse mirrors keyboard). - highlighted item auto-scrolls into view; index resets when result set shrinks past it or after commit/clear. Wires aria-controls / aria-expanded / aria-activedescendant / aria-autocomplete on `<SearchField.Input>` and ids + data-highlighted on each option. Header meta pill updated to 7 keys. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
99a1f05cd7 |
fix(search-field demo): close popover after pick without reopening on refocus
`commitPick()` set `value`, closed the popover, then refocused the input — but the `onfocusin` on `<Popover.Anchor>` reopened it. Guard the anchor's open handler with a `justCommitted` flag cleared on the next microtask so the refocus that follows a pick is ignored. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
82349f15b4 |
feat(search-field): floating results popover in demo + hide native clear
- Demo `/uix/components/search-field` wraps the SearchField in `<Popover.Anchor>`; results render in `<Popover.Content>` (intrinsic width, align="start") wrapped in `<PickerShell.Body>` so the panel inherits the canonical picker chrome. Items are real `<button>`s with `onclick` — committing a selection sets `value`, closes the popover, and restores focus to the input. - Forward `bind:ref` through the eidos `<SearchField.Input>` wrapper so consumers can refocus the input from the demo's pick handler. - Hide the browser's native `::-webkit-search-cancel-button` / `::-webkit-search-decoration` / `::-ms-clear` so only the canonical `<SearchField.ClearTrigger>` X is visible. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
afb09c2254 |
feat(picker-shell): extend to Root + Header + Body + Footer layout
Promote PickerShell from action-row-only to a canonical 4-zone shell (Root → Header / Body / Footer) shared by every composite picker so date/time/color popovers get the same visual contract for free. - Add `<PickerShell.Root>` flex-column wrapper (emits data-picker-shell, data-mode) plus `<PickerShell.Header>` and `<PickerShell.Body>` zones matching the existing Footer's spacing + border tokens. - Auto-wrap all 5 *-content.svelte popovers (date / date-range / time / time-range / color) in `<PickerShell.Root>` so consumers only compose Body + Footer. - Refresh all 5 demos to use `<PickerShell.Body>` around picker content; date-picker demo gains a `showShellHeader` toggle showcasing the new Header zone. - CSS scales section gap + header spacing across xs/sm/md/lg picker sizes via [data-popover-content][data-picker-size] scopes, in sync with the existing Footer scale. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
8aa6086976 |
docs(search-field demo): add live filterable results panel
The demo only had the SearchField input by itself with a trace strip; nothing actually demonstrated the search outcome. Added a 34-item dataset of UIX components (name + group) and a live results grid that filters by name or group substring as the user types. - Empty input renders 34/34 results. - Typing "form" filters to 17 components from the Forms group. - "drawer" filters to 1 (Drawer). - Empty results state renders a graceful "No matches for ..." message. - Counter shows `results N / total` so the demo doubles as a quick feedback loop for the debounceMs control (count + meta animate together when the debounced onValueChange finally fires). CSS lives inline in the demo (`.search-field-results*` selectors) since this is demo content rather than reusable component visuals. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
9fbf8e48b5 |
feat(number-field, search-field, combobox): audit-driven improvements
Three components closing the 6-component audit batch.
number-field
- N1 RTL scrubber: invert horizontal delta sign when dir='rtl' so
dragging right decrements (visually backward) in RTL layouts. Vertical
unaffected. Added 2 RTL scrub tests; existing scrub test pinned to ltr.
- N2 stepper height: Increment/Decrement now use --_number-field-height
for inline-size/block-size/min-inline-size so they always match the
input row at every size token (xs/sm/md/lg/xl).
- N3 input focus ring: explicit [data-number-field-input]:focus-visible
with --_number-field-accent-border box-shadow, alongside the existing
Provider-level ring.
search-field
- S1 icon tokens: added --search-field-icon-size-{xs..xl} + icon-color +
icon-margin-inline-end + a Morfo Icon part + soma/eidos wrappers.
Demo uses <SearchField.Icon><Search /></SearchField.Icon>.
- S2 debounceMs prop: soma wraps onValueChange in a timers.schedule with
the provided delay. clear() cancels, submit() flushes synchronously.
- S3 loading slot: new LoadingIndicator part (morfo + soma + eidos) +
data-loading on Provider + CSS spinner via @keyframes search-field-spin
honoring prefers-reduced-motion.
combobox
- C1 ClearButton part: new morfo trigger + soma provider + eidos wrapper
with X icon. Hidden via [data-empty] (opacity + pointer-events:none).
Click clears value + inputValue and refocuses input.
- C2 allowsCustomValue: when true and no highlighted item, Enter commits
the current inputValue as the value. Single mode closes after commit;
multi appends + clears input + stays open.
- C4 Backspace removes last tag in multi mode when input is empty.
- C5 Sticky group headers: CSS position:sticky with content-overlay bg.
Plus: fixed search-field demo's brittle parts[1] index reference to a
named lookup (parts.find(p => p.kebab === 'input')).
Verified: svelte-check --threshold error → 0 errors / 0 warnings /
4584 files. All component tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
7bc17485dc |
feat(editable): invalid + required + form + Control + better hover/disabled + defaultValue
Audit-driven improvements (7 items) to Editable:
- E1 invalid + required props
- morfo: Provider gains data-required/data-invalid + aria-required/aria-invalid;
Input emits data-invalid + aria-invalid.
- soma: opts include `invalid` and `required`; runtime registration + provider
props derivation emits the data-attrs.
- eidos css: risk-border on Input + Control when [data-invalid].
- E2 APG: added `apg: 'https://www.w3.org/WAI/ARIA/apg/patterns/textbox/'` to
morfo — Editable is essentially a textbox swap.
- E3 form integration: new HiddenInput part (morfo + soma + eidos). The Provider
wrapper auto-renders `<EditableHiddenInput>` when `name` is set, so consumers
don't have to compose it manually. Submits value as native form data.
- E4 Control re-export: Eidos namespace now exposes `Editable.Control`,
matching the soma surface.
- E7 submit hover: replaced `filter: brightness(1.04)` with
`--_editable-accent-solid-hover` per color variant. Consistent with the
other triggers' bg+border transition pattern.
- E9 disabled state: added `cursor: not-allowed` + `color: content-disabled`
on Preview/Input/triggers, alongside the existing opacity. Clearer
feedback than opacity alone.
- E10 defaultValue: new prop seeds `value` once via untrack when `value`
is uncontrolled. Same pattern as the recent select.defaultValue.
Demo at /uix/components/editable: switches for invalid/required/disabled,
a Form integration section with native form submit + FormData panel,
and a defaultValue uncontrolled example.
Verified: svelte-check --threshold error → 0 errors / 0 warnings / 4578
files. Soma tests: 3/3 pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
75136e6de5 |
fix(select) + feat(field): match-anchor width, label persistence, OptionalIndicator, auto-label
select bugs
- The popover content didn't match the trigger width even though
matchAnchorWidth was already implemented in the eidos layer. Root
cause was in soma/layers/floating/floating.svelte.ts: `transformedStyle`
read `this.opts.style` directly without `.current`, so consumer styles
never merged into the floating wrapper. Fixed by reading
`this.opts.style.current` and also returning the value when it's an
object (previously fell through to undefined). All popover-based
components (select, combobox, date/time pickers, color picker) now get
their `style` prop applied.
- The trigger value text reverted from the rendered label (e.g. "Apple")
to the raw value string ("apple") whenever the popover closed. Items
unmount when Content's Presence collapses, and they were unregistering
their label on cleanup — leaving the label registry empty between
opens. Drop the unregister on cleanup; keep registrations alive across
popover open/close cycles. Re-mounts overwrite the entry.
field improvements (F1 + F2 + F3)
- F1: add an explicit `[data-field][data-variant='surface']` CSS block
so the surface variant (declared in the type union) has its own rule
instead of silently falling back to the default style.
- F2: new `OptionalIndicator` part (morfo + soma wrapper + eidos
wrapper). Renders only when `required === false`. Default text comes
from `texts['optional-indicator']` ("(optional)"). Styled muted + 0.85em.
- F3: `<Field.Label />` without children now auto-renders the
localised label from `texts.label`. Reduces boilerplate when consumers
rely on the field name for its label.
- Demo at /uix/components/field gained composition examples for the new
parts.
Verified: svelte-check --threshold error → 0 errors / 0 warnings / 4576
files. In-browser: select trigger and popover widths both 336 px;
closed and open values both show labels (e.g. "Apple, Pear, Orange").
Field demo shows 2 required `*`, 1 `(optional)`, and an empty
`<Field.Label />` falling back to the literal "Field".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
ffdb480403 |
feat(select): invalid state + ItemDescription + defaultValue + visual fixes
Audit-driven improvements to Select: - morfo APG URL fixed (combobox → listbox; Select is a collapsible-listbox per ARIA APG, not a text-input combobox). - `invalid` prop added to soma + morfo data-invalid/aria-invalid on Provider and Trigger. Eidos applies risk-border + tinted focus shadow when invalid. - `defaultValue` prop added (string | string[]) for uncontrolled init. Normalized to string[] inside the soma wrapper via untrack so the reference is intentionally non-reactive. - `ItemDescription` part: new morfo part (kebab item-description, archetype description), new soma wrapper + new eidos wrapper. CSS gives muted color and 0.85em font-size; when the parent item is checked, the description inherits accent color. - Value truncation: ensure `min-inline-size: 0` + `max-inline-size: 100%` on the trigger value so long labels ellipsis instead of overflowing. - Highlighted vs checked distinction: the highlighted-only state now uses a lighter hover background, and an item that is both highlighted and checked gets an additional inset focus ring on top of the accent fill. Demo at /uix/components/select adds an `invalid` switch, and three of the six items now ship with a description (Apple / Pear / Carrot). Verified in browser: - triggerInvalid + aria-invalid = true after toggling switch. - Description on non-checked item: rgb(123,123,123) (content-muted). - Description on checked item: accent purple (inherits). - svelte-check --threshold error → 0 errors / 0 warnings / 4574 files. Deferred: SL3 readonly (rare use case), SL4 virtual scrolling (own task). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
fd3a0b00c9 |
feat(calendar, date-picker, date-range-picker): add xs size + fix duplicate footer
xs propagation - Add [data-size='xs'] blocks to calendar.css, date-picker.css, date-range-picker.css (root + inline range calendar). - Extend CalendarSize union with 'xs'; DatePicker / DateRangePicker inherit it through their type re-exports. - date-picker-content + date-range-picker-content clamp xs to sm when forwarding to PopoverContent (PopoverSize starts at sm; the calendar inside still receives xs via data-size). - size chip groups in calendar / date-picker / date-range-picker demos now list xs / sm / md / lg. date-picker demo: fix duplicate footer - The `calendarBody` snippet had a hardcoded <DatePicker.Footer> that rendered alongside the controllable `pickerFooter` snippet, producing two footer rows when showFooter=true. Removed the hardcoded one. Verified in browser: - date-picker size='xs' -> data-date-field-input height 26px (control-height-xs), padding 8px (space-2), font-size 12px. - date-picker popover now shows a single footer. - svelte-check --threshold error -> 0 errors / 0 warnings / 4572 files. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
f4707bd0c9 |
feat(month-grid, year-grid): add xs size
Both grids now accept size='xs' alongside sm/md/lg. - recipe: add calendar-padding-xs / control-size-xs / day-size-xs / font-size-xs tokens (space-2, control-height-xxs=22px, font-size-xs). Regenerated generated/base.css. - eidos css: add [data-month-grid][data-size='xs'] and the year-grid equivalent before the existing sm block. - types: MonthGridSize / YearGridSize extend Size with 'xs'. - demos: size chip group now lists xs / sm / md / lg. Verified in browser at /uix/components/month-grid with size='xs': cell block-size = 22px, padding = 8px (space-2), font-size = 12px. svelte-check --threshold error -> 0 errors / 0 warnings / 4572 files. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
f7831563a1 |
feat(month-grid, year-grid): standalone primitives parallel to date-picker views
DatePicker.MonthView / YearView require a DatePickerProvider context and
can't be used standalone. This commit introduces MonthGrid and YearGrid
as independent primitives that accept value / placeholder / minValue /
maxValue as props and work anywhere.
morfo
- New morfo/components/month-grid.ts and year-grid.ts.
- scope: ['soma','sema'], apg: grid pattern.
- 7 parts each: Provider (role=application) / Header / Heading /
PrevButton / NextButton / Grid (role=grid) / Cell (role=gridcell).
- Events: commit-set (cell click) + nav-step (prev/next).
- Cell data-attrs: data-value, data-selected, data-current, data-disabled,
data-focused.
- Keyboard map covers arrows / Home / End / PageUp/Down / Enter / Space.
soma
- New soma/components/{month-grid,year-grid}/ with provider class +
langs + types + exports + 7 thin svelte wrappers each.
- MonthGridProvider: 12-cell grid keyed to placeholder.year; localized
month names via Intl.DateTimeFormat with configurable monthFormat
('long'|'short'|'narrow'). Prev/Next step ±1 year.
- YearGridProvider: decadeSize-cell grid (default 12); page anchored at
placeholder.year - (placeholder.year % decadeSize). Prev/Next step ±
decadeSize.
- Both expose Cell sub-provider with snippetProps {selected, disabled,
current, value, label} + roving tabindex + RTL-aware keyboard nav.
eidos
- New eidos/components/{month-grid,year-grid}/ — namespace-style index
with size / variant / color visual props + dedicated CSS reusing the
--calendar-* token family.
- Each grid renders Header (Prev/Heading/Next) + 3-col Grid.
demo
- New /uix/components/month-grid and /uix/components/year-grid demo
pages with the standard live/api/morfo/sema/recipe/a11y tabs,
interactive controls for every public prop, soma+eidos snippets that
reactively rebuild from controls, and sema event play buttons.
Verified: svelte-check --threshold error -> 0 errors / 0 warnings /
4572 files.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
dd22cfc2bc |
feat(time-range-picker): eidos wrapper + canonical picker-shell parity
Builds the Eidos time-range-picker layer end-to-end and brings color-picker's morfo into parity with the other pickers. time-range-picker - soma: `mode` prop, `pickerShellContext` registration with commit/cancel/clear and `valueOnOpen` snapshot for revert; Provider now renders a div with data-state/disabled/readonly/required + aria mirrors. - soma: new `Clock` part (mirrors TimePicker.Clock with role="dialog" and data-state). - morfo: scope ['soma','sema'] + apg dialog-modal; promoted Provider to public div with states/data-state; added Clock part; declared the 6 canonical picker events (open / close-commit / close-cancel / close-dismiss / close-dismiss-outside / commit-clear); slider parts declare data-endpoint + data-disabled. - eidos: full namespace (Trigger / Content / Clock / HourSlider / MinuteSlider / SecondSlider / DayPeriodToggle + re-exported PickerShell Footer / Clear / Cancel / Close). Sliders compose generic SliderProvider (N-8) and ship with optional rulers above hour / below minute+second. - eidos css: 2-column endpoint grid in the clock; per-endpoint range tint; AM/PM toggle styling; reuses time-field tokens. - demo: interactive testbed at /uix/components/time-range-picker mirroring the time-picker demo — every soma + eidos prop has a live control, closeOnRangeComplete + start/endReadonlySegments toggles, footer composition switches, sema play buttons, soma+eidos snippets. - nav: added entry between Time picker and Color field. color-picker (audit fixes) - morfo: Provider declares states ['open','closed'] + data-state attr; added the same 6 canonical picker events targeting the `content` part (handle-pick / handle-drag / commit-set preserved). - soma: runtime now feeds open state + disabled/readonly/required/invalid props to the morfo runtime so data-state actually emits. Verified: svelte-check --threshold error → 0 errors / 0 warnings / 4524 files. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
8515f5e673 |
fix(color-picker): ValueSwatch now reactively reflects the current color
User report: 'la caja del color seleccionado en el pickercolor no se
actualiza, compruébalo.'
Root cause
----------
The Eidos recipe had an `[data-color-picker-value-swatch]::after`
overlay that painted `var(--cp-trigger-color, currentColor)` on top of
the swatch — a CSS variable set ONLY on the trigger button by the
Eidos wrapper. The popover content is portaled outside the trigger's
subtree, so the var never propagated into the ValueSwatch element.
The overlay therefore stayed at its fallback (`currentColor`) and
covered the soma-set inline `background-color`, hiding the live colour.
Fix
---
1. **Soma `ColorPickerValueSwatchProvider.backgroundStyle`** now emits
BOTH `background-color: <hex>` and `--cp-current-color: <hex>` so:
- Consumers without the eidos recipe still see the colour (inline
background-color, as before).
- Eidos recipes / external CSS can read `--cp-current-color` as a
reactive CSS variable on the element itself.
2. **Eidos CSS** drops the `::after` overlay. Instead the checker
(transparency pattern) lives on a `::before` pseudo with
`z-index: -1` inside an isolated stacking context, so the soma's
inline `background-color` paints ON TOP and the checker shows
through only where alpha < 1. No more colour stomping.
Checks: 67/67 component-audit PASS, svelte-check 0 errors / 0 warnings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
da19728cb6 |
feat(color-picker): area drag emits handle-pick/handle-drag/commit-set + refresh demo snippets
User report: 'el selector del cuadro de colores, no tiene semántica, es
un drag, etc como el slider, no?'
You're right — the 2D area is a drag interaction with the same shape
as a slider. It was wiring pointer events to soma state but staying
silent to sema; nothing for sounds/haptics/transient styles to hook
onto. Now it speaks the same vocabulary as the slider.
Morfo additions (color-picker)
------------------------------
- `handle-pick` (family: handle, verb: pick, sequence: coincident,
target: area) — fires once at pointer-down.
- `handle-drag` (family: handle, verb: drag, sequence: coincident,
target: area) — fires on each move (pointer or keyboard step).
- `commit-set` already existed — now also emitted on pointer-up and
on each keyboard step (slider parity).
Soma changes (color-picker-provider)
------------------------------------
- ColorPickerAreaProvider:
- `onpointerdown` → trigger `handle-pick` then continue to drag.
- `handlePointerMove` → trigger `handle-drag` after writing the value.
- `onpointerup` → trigger `commit-set` then `commitChange()` for
the onValueChangeEnd callback.
- ColorPickerAreaThumbProvider.onkeydown:
- Each arrow / Home / End / PageUp / PageDown step now triggers
`handle-drag` + `commit-set` + commitChange(). Matches the slider's
keyboard semantics (one step = one drag + one commit).
Demo snippet refresh
--------------------
The `somaSnippet` / `eidosSnippet` in `/uix/components/color-picker`
were stuck on the pre-refactor API (`<ColorPicker.Area>` with explicit
Background+Thumb children, no `Control`, no `ValueSwatch`, no
PickerShell footer). Updated both to mirror the current demo body:
- Eidos: `Label / Control[Trigger + ValueText + HiddenInput] / Portal /
Content[Area / ChannelSlider hue / alpha / ValueSwatch +
ChannelInput + EyeDropper / SwatchGroup / Footer[Clear/Cancel/
Close]]`.
- Soma: same shape but uses `PickerShell.Footer / Clear / Cancel /
Close` explicitly (and the explicit Track/Thumb composition on
channel sliders for the curious).
- Both snippets honor the live `mode / inline / closeOnSelect /
enableAlpha / showFooter / showClear / showCancel / showClose`
flags from the chip controls.
Checks: 67/67 component-audit PASS, 40/40 morfo:check PASS,
svelte-check 0 errors / 0 warnings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
3e5be857c4 |
fix(color-picker): single-row preset palette of 10 swatches that share container width
User report: 'la paleta dejala en 10 y más pequeños para que solo ocupen una linea, se deben de ajustar a lo ancho'. Demo ---- - Preset palette trimmed from 11 to 10 colors. CSS — swatch-group rewritten to single-row flex layout ------------------------------------------------------ - `display: flex; flex-wrap: nowrap; inline-size: 100%; min-inline-size: 0` so all swatches share the available width. - Each `<SwatchTrigger>` becomes `flex: 1 1 0` + `min-inline-size: 0` + `aspect-ratio: 1` + `inline-size/block-size: auto`, with the recipe token (`--color-picker-swatch-size`) acting as `max-inline- size` instead of a fixed size. Net effect: swatches shrink/grow together to fit any popover width and any number of presets, while capping at 1.5rem so they don't blow up on huge containers. Math at default md popover (~19.5rem content width, ~14px padding each side, 10 swatches × ~23px + 9 × 6px gap ≈ ~284px usable). Each swatch lands at ~23px and the row never wraps. Checks: 67/67 component-audit PASS, svelte-check 0 errors / 0 warnings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
aaf35fdbb0 |
fix(color-picker/demo): compact value row — swatch + channel input + eyedropper inline
User feedback: 'se pueden realinear los diferentes elementos para que no sea tan alto, el pick from screen en la misma linea del color seleccionado, o el input del valor al lado del color, etc.' Collapses three previously-stacked rows into one: Before: [preview swatch + value text] ← row 1 [channel input segments + format] ← row 2 [eyedropper button + label] ← row 3 (right-aligned) After: [swatch] [channel input ........] [eyedropper] ← single row The ValueText is dropped because the channel input segments already show the hex/rgb/hsl value digit-by-digit (more useful than a static string). The eyedropper becomes a compact icon button at the end of the row — `aria-label="Pick color from screen"` still announces it. Net effect: ~50px shorter popover. Files ----- - web/routes/uix/components/color-picker/+page.svelte — `.cp-value-row` flex layout with swatch (1.75rem) + ChannelField (flex: 1 1 auto) + EyeDropper. ChannelField input forced `flex-wrap: nowrap` so segments stay inline. Old `.cp-preview-row` / `.cp-eyedropper-row` styles removed. Checks: 67/67 component-audit PASS, svelte-check 0 errors / 0 warnings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
709d8a108e |
fix(color-picker): fix slider gradients + add live preview + label eyedropper
User report: 'los sliders son deficitarios, no muestra la banda de color ni de transferencia, faltan textos, el color seleccionado aparte del valor ascii, un texto que indique el gotero, etc.' Three concrete issues addressed. 1. Slider gradients invisible ----------------------------- Root cause: the soma channel-slider div carries BOTH `data-slider` (from SliderProvider.props) and `data-color-picker-channel-slider` (from mergeProps). My CSS targeted `[data-color-picker-channel-slider] [data-slider]::before` — i.e. as a *descendant*. They're the same element, so the selector never matched and the rainbow / alpha gradient stayed hidden. Fix: - New selector `[data-color-picker-channel-slider][data-slider]::before` paints the gradient on the slider's own track strip. - Transparency checker moved to the wrapper's `::after` (with `z-index: -1` + `isolation: isolate`) so it sits behind the gradient — visible only where the alpha gradient ramps to transparent. - Adds a subtle border on the track so the strip is visible against the popover surface. 2. Live preview alongside the hex/rgb/hsl string ------------------------------------------------ The popover used to show only the segmented ChannelInput (digits) and a small swatch dot on the trigger. Now the popover has a `.cp-preview- row` with `<ColorPicker.ValueSwatch>` (2rem) + `<ColorPicker.ValueText>` + uppercase format tag, so the user always sees the actual color visually alongside its numeric representation. 3. Eyedropper label ------------------- Added a textual `Pick from screen` label next to the icon button, plus `aria-label` on the EyeDropper. The button is no longer a mystery glyph. 4. Channel slider row labels ---------------------------- Each ChannelSlider now sits in a `.cp-slider-row` with a leading uppercase label (Hue / Alpha) so the user knows what each track drives without hovering or reading aria. 5. Section header for swatches ------------------------------ 'Preset colors' header above the swatch grid for visual hierarchy inside the popover. Files ----- - src/uix/eidos/components/color-picker/color-picker.css — slider gradient routing + checker-on-::after layering. - web/routes/uix/components/color-picker/+page.svelte — preview row, slider row labels, eyedropper label, section headers + local scoped CSS in <style>. Checks: 67/67 component-audit PASS, 40/40 morfo:check PASS, svelte-check 0 errors / 0 warnings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
714ee3735c |
feat(picker-shell): extract shared Footer/Clear/Cancel/Close + apply to color-picker
Introduces PickerShell — a soma context + eidos parts shared by every
composite picker (date, time, color, future range pickers). Aligns with
norm N-8 (reuse components across complex composites) so the
Footer/Clear/Cancel/Close behavior lives in ONE place instead of being
duplicated per picker family.
New files
---------
- src/uix/soma/components/picker-shell/
picker-shell-handle.svelte.ts → pickerShellContext +
PickerShellHandle interface
(getMode / commit / cancel / clear).
exports.ts, index.ts → public surface.
- src/uix/eidos/components/picker-shell/
picker-shell-footer.svelte → [data-picker-footer][data-mode]
picker-shell-clear.svelte → [data-picker-clear][data-action]
picker-shell-cancel.svelte → [data-picker-cancel][data-action]
picker-shell-close.svelte → [data-picker-close][data-action]
picker-shell.css → canonical action-row recipe.
index.ts, types.ts → PickerShell namespace + prop types.
- Registered in src/uix/eidos/index.css.
Color-picker now consumes the shell
-----------------------------------
- ColorPickerProvider gets `mode: 'inline' | 'modal'` opt + a
`valueOnOpen` snapshot via watch(open) so cancel() can revert.
- New `commit()` / `cancel()` / `clear()` methods with shell-picker
semantics. The previous `commit()` (fires onValueChangeEnd) was
renamed `commitChange()` since the picker-shell `commit()` means
"save + close". All internal callers (area-thumb pointerup,
swatch click, eyedropper open, channel-slider drop) updated.
- Constructor registers the handle:
`pickerShellContext.set(this.pickerShellHandle)`.
- PopoverProvider receives `modal: mode === 'modal'` so outside-click
+ Escape get blocked when modal.
- Eidos `ColorPicker.Footer / .Clear / .Cancel / .Close` re-export
the shared parts. The old `ColorPicker.Close = PopoverClose` was
renamed `ColorPicker.PopoverClose`; the canonical `Close` is now
the picker-shell save action.
Demo
----
- New `mode` chip (inline | modal).
- Footer composition switches (render footer / clear / cancel / close).
- Renders <ColorPicker.Footer> with shell parts inside <Content>.
Visual fixes
------------
- Horizontal overflow on the popover content: added `min-inline-size: 0
/ max-inline-size: 100% / box-sizing: border-box / overflow-x: hidden`
on `[data-popover-content][data-color-picker-content]` and on its
direct children. Inner ChannelInput allowed to wrap segments instead
of pushing the popover wider.
- ColorField segments inside the popover gain `flex: 0 1 auto`.
Norms
-----
- N-8 case study appended to PENDIENTES.md documenting the
PickerShell extraction alongside the channel-slider refactor.
Checks
------
- 67/67 component-audit PASS
- 40/40 morfo:check PASS
- svelte-check 0 errors / 0 warnings
Follow-up (task #81): migrate date-picker / time-picker Footer to use
PickerShell too — drop the duplicate `date-picker-footer/clear/
cancel/close.svelte` and `time-picker-footer/clear/cancel/close.svelte`
files; update CSS selectors to `[data-picker-footer/...]`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
2731ee567b |
feat(eidos): add ColorPicker wrapper + demo
Composite popover: 2D saturation/brightness area, hue + alpha channel
sliders, preset swatches, eye-dropper, and a segmented channel input
composed from ColorField. Builds on the existing soma color-picker
(23 parts) — eidos layer is mostly thin pass-throughs adding visual
chrome.
What landed
-----------
- New `src/uix/eidos/components/color-picker/`:
- Root visual (Provider) + visual-context (size/variant/color)
- Trigger with a default swatch dot tinted from
`--cp-trigger-color` (the air pattern from morfo-runtime)
- Content (popover wrapper), Area (+ Background + Thumb),
TransparencyGrid
- ChannelSlider (+ Track + Thumb) — generic per-channel slider;
default-composes Track+Thumb when no children
- SwatchGroup + SwatchTrigger + Swatch + SwatchIndicator
- EyeDropper (Pipette icon by default)
- ChannelInput + ChannelSegment + FormatSelect (composed from
ColorField parts; same provider injected automatically)
- Label, Control, ValueSwatch, ValueText, HiddenInput
- Recipe section `color-picker` in `recipes/base.ts` (regenerated
`generated/base.css`) — trigger, content, area, channel slider,
swatches, eye-dropper tokens.
- CSS reuses color-field tokens for the segmented input portion and
adds picker-specific visuals. Transparency checker on trigger swatch
+ value swatch + alpha slider track via conic-gradient pattern.
- Morfo updates: scope `['soma','sema']`, `apg: dialog-modal`, added
`commit-set` event so audit classifies it as interactive.
- Soma exports: ColorPickerProvider class so eidos Trigger can read
the current value for the swatch tint.
- Demo at `/uix/components/color-picker` with the standard 6 tabs;
live stage exercises area drag + channel sliders + preset swatches
+ eye-dropper + format switch.
- README documents the surface, comparativa vs air/Ark/Chakra, and
flags the picker contract (mode/Footer/commit/cancel) as gap to
align with date/time-picker in a follow-up.
Checks
------
- 67/67 component-audit PASS
- 40/40 morfo:check PASS (new color-picker demo validates against
morfo DOM)
- svelte-check 0 errors / 0 warnings
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
2f147897d9 |
feat(morfo/time-picker): declare HourSlider/MinuteSlider/SecondSlider as parts + ruler opt-out prop
User pointed out that TimePicker exposes <TimePicker.HourSlider> / .MinuteSlider / .SecondSlider in its public API but those weren't declared in the time-picker morfo — the contract was implicit (data- slider attrs from the slider morfo). Now made explicit. Morfo additions (all optional, archetype: 'group'): - HourSlider → data-time-picker-hour-slider - MinuteSlider → data-time-picker-minute-slider - SecondSlider → data-time-picker-second-slider Eidos slider wrappers stamp the corresponding data-attr on the outer wrapper div so morfo:check finds the parts in the DOM. The inner slider DOM (data-slider, data-slider-thumb, data-slider-range) keeps its own contract via the slider morfo — composition, not duplication. Also: - Hour ticks now end-inclusive: 24h shows 0/3/6/9/12/15/18/21/23 (was 0..21, missing the upper cap). 12h shows 1/3/6/9/12. - Minute / Second scale now ends with 59 so the upper bound is shown. - New optional `ruler` prop on <TimePicker> (default `true`) and matching per-slider `ruler` opt-out. Propagated via visual context. - Demo: new switch to toggle the ruler scale. 67/67 component-audit PASS, 39/39 morfo:check PASS, svelte-check 0 errors / 0 warnings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
391de6e04c |
feat(eidos): add TimePicker wrapper + demo following P-1..P-5 picker contract
Builds on the existing soma TimePicker slider-based clock (HourSlider / MinuteSlider / SecondSlider + DayPeriodToggle). Adds the canonical picker shell: - mode='inline' | 'modal' on the Provider, propagated to popover.modal - Provider helpers commit() / cancel() / clear() with watch(open) snapshot for cancel-revert - Footer composition (TimePicker.Footer / Clear / Cancel / Close as pure-composition Eidos parts; no *Button root props) - New TimePicker.Clock soma component that carries the data-time-picker-clock contract attr, wraps the slider stack - Eidos shell: Provider visual root, Trigger (clock icon), Content (popover content), Clock, Footer + action parts, slider/toggle pass-throughs Renews the time-picker morfo to declare Provider / Trigger / Clock / DayPeriodToggle / DayPeriodItem / Footer / Clear / Cancel / Close, plus all 6 commit/emerge events (open + close-commit/cancel/dismiss/ dismiss-outside + commit-clear). apg=dialog-modal. Demo at /uix/components/time-picker exercises all controls, Footer composition switches, soma + eidos code snippets, morfo / sema / recipe / a11y tabs. README documents the picker contract. 67/67 component-audit PASS, 39/39 morfo:check PASS, svelte-check 0 errors / 0 warnings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
ff279ae828 |
feat(eidos): add TimeField + ColorField wrappers and demos
Mirrors the date-field pattern (Provider visual root + Label / Input / Segment / HiddenInput parts; ColorField also exposes FormatSelect). Soma owns segment editing, ARIA spinbuttons, validation, format switching and form submission; Eidos adds size (xs..xl), control variant (surface/outline/ghost) and intent color recipes. Adds time-field and color-field recipe sections to recipes/base.ts (regenerated generated/base.css) and registers both routes in the sidebar nav. apg=spinbutton declared on both morfos. READMEs follow the Baseline / Superficie / Comparativa / Decisiones / Gaps / Referencias template required by audit F-1.x. All 67 component-audit components PASS, 38/38 morfo:check PASS, svelte-check 0 errors/0 warnings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
72ff90b71f |
docs: consolidate kind contract + composition norms (sweep)
After several commits on pickers, the docs lagged behind the actual
contract. This sweep aligns PENDIENTES + eidos README +
DEMO_AUTHORING_GUIDE with what landed.
PENDIENTES.md:
- 'Pickers' section rewritten as a consolidated state table.
Everything done is marked hecho; the two big items
(MonthPicker/YearPicker, MonthRangePicker/YearRangePicker as
separate components) are explicitly **descartar** because they're
achieved via <DatePicker kind='X'> / <DateRangePicker kind='X'>.
Duplicating component surfaces for what a prop captures is
doctrinally rejected.
- Promotion of YearView/MonthView to standalone <YearCalendar> /
<MonthCalendar> is **diferir** — currently coupled to picker
provider context, no real use case outside picker yet.
- Time picker / color picker propagation of modal+Footer pattern
marked **implementar**.
- Playwright browser tests for the picker flows marked
**implementar** — range state machine + kind chip + modal need
coverage.
- Range view: 'differentiate start/end vs in-range visually' added
to theming backlog (currently all 3 use primary-solid, range
tint not visible).
- Two new norms N-6 and N-7:
* N-6 picker kind = single source for input + view. Filtering
lives at DateFieldProvider (soma), consumers iterate the
segments output. Views are canonical Eidos parts.
* N-7 composition over visibility props. Parts opt-in by inclusion,
not by boolean prop. Demo wraps parts in {#if showX} with local
state so the UI toggles still work without leaking demo logic
into the parts.
eidos/README.md:
- New 'Cambios 2026-05-21 — pickers: kind + composition' section
summarising kind + Footer composition + provider helpers + the
'composition wins, no separate variant components' decision.
DEMO_AUTHORING_GUIDE.md:
- §12.9 'Composition over visibility props': right vs wrong example
for <DatePicker.Footer> with the Clear/Cancel/Close children.
- §12.10 'Chakra-style kind for picker variants': demo skeleton
for the Input snippet (no filter) and the Content {#if} branch.
Task list: #29 retired (MonthRangePicker/YearRangePicker as separate
components — replaced by <DateRangePicker kind='X'>).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
05a432afc0 |
date-range-picker: YearRangeView + MonthRangeView (kind grids)
Completes the kind contract for the range picker. The popover used to
render the day-grid range calendar regardless of kind; now it switches
to a year-grid or month-grid view that handles range selection on the
appropriate granularity.
New Eidos parts:
- <DateRangePicker.YearView>: 3×4 decade grid with prev/next decade
buttons. Selection state machine (mirrors the day-grid range
selection):
* empty → click sets startValue (and clears endValue)
* pending (start set, no end) → click sets endValue. Swap if the
clicked year is before the existing start.
* complete (both set) → click resets to a fresh start + clears end.
Year endpoints normalise to Jan 1 (start) / Dec 31 (end).
- <DateRangePicker.MonthView>: 3×4 month grid for the placeholder's
year, with prev/next year navigation. Same state machine using
`year*12 + month` as the comparison key, so ranges span multi-year
boundaries cleanly. Start = day 1; end = last day of the month
(computed via Date(y, m, 0).getDate()).
- Localised month names via DateFormatter(locale, { month: 'short' }).
Index + CSS:
- index.ts: registers YearView + MonthView on the DateRangePicker
namespace.
- date-range-picker.css: shared grid layout (3 columns, gap-2),
hover surface-overlay, selected cells get primary-solid +
content-on-solid, in-range cells get color-mix(primary-solid 22%,
transparent) as a stripe with border-radius:0 (start/end keep
their outer corner radius). Reduced-motion respected.
Demo:
- {#if kind === 'year'} → <YearView>; {:else if 'month'} → <MonthView>;
{:else} → existing day calendar.
- pickerFooter snippet rendered once at the Content level (removed
the inline render at the end of calendarBody to avoid duplication).
Verified in browser:
1. kind='year' chip → popover shows year grid '2020 – 2031'.
2. Click 2024 → cell marked start (purple solid).
3. Click 2027 → cell marked end, 2025-2026 marked in-range. trace
shows 'range 2024-01-01 -> 2027-12-31'. Popover stays open
(closeOnRangeSelect=false in the demo).
0 type errors, 67/67 component:audit PASS.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
9baccecda9 |
picker demos: footer composition switches + popover closed by default
Two follow-ups from user feedback:
(1) User: 'en los demos no existen para definir las propiedades y
visibilidad del pie y los botones' — after the previous refactor that
moved visibility from props to composition, the demos lost their UI
switches and there was no way to toggle the parts on/off.
Restored toggles but with the correct semantics: the switches now
decide whether each part is INCLUDED in the markup, not whether the
part renders internally. Composition first, demo UX second.
- Both demos: new state vars showFooter / showClear / showCancel /
showClose (default all true).
- New `{#snippet pickerFooter()}` snippet that renders
<Footer><Clear/><Cancel/><Close/></Footer> with {#if show*}
guards on each child. Rendered once via {@render pickerFooter()}
at the end of every calendar branch (Calendar / MonthView /
YearView for single; the rangeCalendar for range).
- New 'footer' control row with 4 switches per demo, labelled with
the structural intent ('compose parts inside <Footer>').
(2) User: 'el popover del calendar aparece, eso es un error' — the
previous commit defaulted open=true on both demos so the new Footer
was visible at first paint. That conflicted with the natural flow
(user expects to click the trigger to open). Reverted to open=false
by default.
Also removed the auto-reopen $effect that re-opened the popover
whenever `kind` changed. The chip click can close the popover via
outside-click in inline mode; the user re-opens deliberately. No more
magic.
Verification: 0 type errors, 67/67 component:audit PASS.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
4a866cf86a |
date-range-picker: propagate kind to start/end inputs
User: 'lo has trasladado da daterangepicker?' — only the Footer pure-
composition refactor had landed in range; the `kind` prop (Chakra
date/month/year variant) was still single-picker only. The range
demo's input segments stayed MM/DD/YYYY regardless of the chip.
This commit propagates `kind` end-to-end through the range stack.
Calendar view-mode (year-grid / month-grid for ranges) is still
pending — that's the next commit.
soma:
- DateRangePickerKind type exported alongside DateRangePickerMode.
- DateRangePickerProvider opts gain `kind`. Root .svelte threads it
through readableActive both to the picker context AND to the
DateRangeFieldProvider it creates.
- DateRangeFieldProvider opts gain `kind` (single value — there's no
use case for kind='year' on start + kind='month' on end; the range
picker enforces a uniform kind on both endpoints).
- date-range-field-input forwards provider.opts.kind to each endpoint
DateField it spawns. The constant `kind: 'date'` placeholder is
gone.
- types.ts (range-picker + range-field): JSDoc + prop declarations.
- exports.ts: surface DateRangePickerKind alongside DateRangePickerMode.
- Test fixtures (range-picker + range-field): extended with the new opt.
demo:
- `let kind = $state<'date' | 'month' | 'year'>('date')` + chip control
next to the `mode` radio + prop pass-through to <DateRangePicker>.
Browser confirmed: clicking 'year' chip collapses BOTH start and end
inputs to a single `2026` segment. 'month' shows MM/YYYY on both.
'date' restores MM/DD/YYYY.
What's still pending (next commit):
- Year-grid and month-grid views for the range popover. Currently
the day calendar still renders regardless of kind. The range
picker's popover view-mode needs RangeCalendar to gain a `view`
prop and the eidos surface to dispatch to YearRangeView /
MonthRangeView components (range selection state machine: first
click sets start, second click sets end).
Verification: 0 type errors, 18/18 tests across the date-* soma
suite, 67/67 component:audit PASS.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
6299e6ebc6 |
pickers: Footer parts = pure composition (drop *Button props)
User correctly pointed out: button visibility should not be exposed as
properties on the picker root. It should be expressed via composition
— if you include <DatePicker.Clear/> inside <DatePicker.Footer/>, it
shows; if you omit it, it doesn't. Same as how Header parts work, and
extensible to derivatives (date-range-picker follows the same rule).
This commit removes the visibility props + visibility checks. Pure
composition wins.
soma:
- DatePickerProvider opts: drop clearButton / cancelButton /
closeButton. The root soma component drops the props + the
readableActive passes. Same for DateRangePickerProvider.
- types.ts: drop the prop declarations + JSDoc.
- Test fixtures: drop the state() entries for the removed opts.
eidos parts:
- date-picker-clear / cancel / close: drop the `visible` $derived
and the {#if visible} guard. Render unconditionally.
- date-range-picker-clear / cancel / close: same.
- date-picker-footer / date-range-picker-footer: also drop the
combined `visible` $derived. The Footer container always renders
whatever children are composed inside.
Modal mode: previously the Close part forced itself visible whenever
mode='modal'. That magic is gone too — the consumer is now responsible
for including <Close/> if mode='modal'; otherwise the modal has no
exit affordance (and that's documented in the Close part's comment).
demos:
- Drop clearButton/cancelButton/closeButton state vars.
- Drop the prop pass-through on <DatePicker> / <DateRangePicker>.
- Drop the 'footer buttons' switch group.
- Drop snippet code refs to those props.
- Keep the same <Footer><Clear/><Cancel/><Close/></Footer> markup
inside the calendar branches — now visibility is purely structural.
Verification: 0 type errors, 7/7 date-picker + date-range-picker
soma tests, 67/67 component:audit PASS. The picker still renders
with all three buttons by default (because the demos compose them).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
0956aa1c37 |
date-picker: kind derives segments at soma layer + year/month views
User correctly pointed out: the input segments depend on the kind of calendar — they're *derived* values, not independently controlled. Filtering segments in the demo snippet (commit A) was wrong; the derivation belongs in the DateField provider. This commit refactors the contract + lands the year/month-grid views (combining commit B + C into one). Architecture fix (segments derive from kind): - DateFieldProvider opts gain `kind: 'date' | 'month' | 'year'`. - `segmentContents` filters `allSegmentContent.arr` by a derived `visibleDatePartsByKind` set, collapsing runs of literals and trimming leading/trailing separators. Time segments (hour/minute/ second/dayPeriod) are passed through untouched — `kind` is orthogonal to `granularity`. - DateField root component accepts `kind` prop and threads it. - DatePicker root forwards its `kind` to the DateField provider it creates internally. - DateRangeField passes `kind: 'date'` as a constant for now; range propagation lands in commit D. - Test fixtures extended with the new opt. New Eidos parts (commit B + C in one shot): - `<DatePicker.YearView>`: 3×4 decade grid centred on placeholder's decade boundary. Header shows the decade range with prev/next decade buttons. Click on a year sets value to (year, 1, 1) and calls handleDateSelect (closes if closeOnDateSelect=true inline). - `<DatePicker.MonthView>`: 3×4 month grid for the placeholder's year. Localised month names via DateFormatter. Header shows the year with prev/next year buttons. Click sets (year, month, 1). - CSS for both: shared 3-column grid layout, hover surface-overlay background, selected cell uses primary-solid + content-on-solid. focus-visible outline. prefers-reduced-motion honoured. Demo wiring: - Removed the local `filterByKind` helper — soma derives it now. - The snippet just iterates `segments` as it comes. - The popover content branches on `kind`: Calendar for 'date', MonthView for 'month', YearView for 'year'. The Footer renders in all three branches. - An $effect re-opens the popover whenever `kind` changes (clicking the chip outside the popover would otherwise close it in inline mode). What this commit DOES NOT do (commit D): - Propagate `kind` to date-range-picker (start + end inputs + the range-calendar popover view-mode). - date-range-field consumes `kind: 'date'` only for now. Verification: 0 type errors, 11/11 date-field + date-picker tests, 67/67 component:audit PASS. Browser confirmed: - kind=date → MM/DD/YYYY input + day calendar - kind=month → MM/YYYY input + month grid (2026) - kind=year → YYYY input + year grid (2020 – 2031) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
46ce1d800d |
date-picker: kind prop (date/month/year) — commit A scaffolding
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>
|
5 months ago |
|
|
3956a5d39e |
picker demos: open by default + footer buttons enabled
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> |
5 months ago |
|
|
bf9dc8934d |
pickers: visible readonly segment styling + date-picker demo control
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> |
5 months ago |
|
|
394aeaa476 |
pickers: promote Footer/Clear/Cancel/Close to canonical Eidos parts
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>
|
5 months ago |
|
|
9bd9db408d |
date-picker: mode='modal' + footer (clear/cancel/closeButton)
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>
|
5 months ago |
|
|
dfb64511de |
eidos: theming fixes + size/variant parity batch + docs
- theme: add surface.muted + content.muted to color contract (neutral-3 + neutral-10). Plugs 17 broken --color-content-muted and 3 broken --color-surface-muted references in recipes/components. - form.css: fix --color-neutral-element-hover typo → --color-neutral-hover. - archetypes.css + events.css: replace raw hsl/rgba indigo with color-mix(var(--color-primary-solid) …) — no raw colors left in eidos. - combobox dark scrollbar: unscope ::-webkit-scrollbar rules in uix.css and duplicate --uix-line on :root + :root[data-mode='dark'] so portaled overlays (Combobox listbox, Popover, Dialog, Drawer) inherit the theme. - sizes: 20 components expand from sm/md/lg to xs..xl (form controls, text inputs, progress/meter, field/form) or xs..lg (nav controls: breadcrumb, pagination, tag-group, toolbar). Composite panels keep sm/md/lg deliberately. - variants: field + toolbar drop arbitrary ControlVariant narrowings; both expose all 3 (surface | outline | ghost) with new outline CSS. - pagination demo: disambiguate siblingCount/boundaryCount as "per side" in label + API table (Radix/MUI convention). - docs: CHECKLIST §C-2.6 (contract token presence) + §D-7.4 (chip parity) + §D-7.5 (size category) added. DEMO_AUTHORING §12.7 (chip parity) + §12.8 (size category cheatsheet) added. eidos/README + active_architecture.md sync texts/migration nomenclature. - PENDIENTES.md: normas N-1..N-5 implantadas en esta sesión. Verification: 88/88 eidos tests, 0 type errors, 67/67 component:audit PASS. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
5d31645939 |
search-field demo: clear button is an X icon, not a word
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> |
5 months ago |
|
|
fd9fb633e0 |
search-field demo: clear button text follows the locale
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>
|
5 months ago |
|
|
332ef63177 |
demos: theme scrollbars + add missing data-uix-radio chrome
Two visible inconsistencies in dark theme that the user flagged: 1. Scrollbars in code blocks (and any other scrollable inside the demos) rendered with the user-agent default chrome — bright white track + thumb on a dark surface. Only `[data-uix-rail]` (the left navigation column) had a themed scrollbar. Added a universal WebKit scrollbar theme scoped to `[data-uix-docs]`: transparent track, `--uix-line` thumb with 2px padding-clipped border so it reads as a thin rounded handle that hovers to `--uix-line-strong`. Both axes (10px width). Firefox handled via `scrollbar-color` + `scrollbar-width: thin` on the root. 2. `[data-uix-radio]` had zero CSS rules — the date-range-picker demo's `mode` (inline / modal) selector fell back to native radio inputs and an unstyled label, while its sibling controls used the themed `[data-uix-switch]`. Two switches-as-controls + one set of native radios in the same panel. Mirrored the data-uix-switch chrome at radio size: 16x16 circle, `--uix-line` border, `--uix-surface-sunken` background, inner pill in `--uix-accent` when checked, focus ring matches the rest. Label uses the same color + size as data-uix-switch-label. Verified in browser on date-range-picker demo: the `mode` radios now read as the same control family as the surrounding switches, and the code-snippet scrollbar is dark-themed instead of bright white. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
8ccd9de033 |
demos: homogenize input chrome across all demo Controls
The `[data-uix-control] input[type='text|number']` rule only covered two input types, so demos that used `<input type='date'>` (date- and date-range pickers' min/max/start/end inputs, calendar bounds, etc.) or `<input type='email|search|tel|url|password|time|month|week| datetime-local'>` (form/field demos) fell back to the user-agent chrome — different padding, borders, fonts, and the native calendar/clock indicator with no theme alignment. Replaced the type-specific selector with an exclusion list: [data-uix-control] input:not([type='checkbox']):not([type='radio']):not([type='range']) Every text-like input now picks up the demo's standard chrome (6px / 32px / `--uix-line` border / `--uix-surface` bg / focus ring). Checkbox + radio keep their bespoke toggle treatments; range keeps its native track with the demo accent. Added: - Consistent `min-block-size: 32px` + `box-sizing: border-box` so inputs line up vertically with the demo's button-style pill controls. - Disabled state: 0.55 opacity + `not-allowed` cursor. - `::-webkit-calendar-picker-indicator` recolored for date/datetime-local/month/time/week inputs — inverted 70% so the calendar/clock icon reads on dark theme and matches the field text tone, with full opacity on hover. - `::-webkit-search-cancel-button` recolored. Verified in browser: date-range-picker demo's start/end/minValue/ maxValue inputs now match minDays/maxDays (number) and label/name (text) inputs. Form demo Field-rendered inputs and the controls panel both read as one consistent surface. date-field controls panel (locale select + name text + min/max date inputs) matches. Closes #47 in the task list. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
491e4be75c |
file-upload demo: ship valid PNG bytes so previews render
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> |
5 months ago |
|
|
383e2ff7f7 |
date-range-picker: mode prop + footer button props + revert group chrome
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>
|
5 months ago |
|
|
0d164b54de |
date-range-picker: unify start + end into a single input chrome
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>
|
5 months ago |
|
|
3b464ee361 |
range-calendar: smart heading + per-calendar title in header
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>
|
5 months ago |
|
|
5f8adbeca6 |
date-picker + date-range-picker: trigger embedded inside the input
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> |
5 months ago |
|
|
39eb0eec86 |
uix: date-picker + date-range-picker components + component audit infra
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> |
5 months ago |
|
|
d6f550554d |
Add Eidos calendar component
|
5 months ago |
|
|
fb5dd0919c |
Advance Eidos demos and form validation docs
|
5 months ago |
|
|
09e2618787 |
Continue Eidos component migration
|
5 months ago |
|
|
bddfff9e39 |
Add UIX permutation axis coverage
|
5 months ago |