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 }
127 Commits (61ffbade460bc4042f2557d47f94fa02256263b0)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
61ffbade46 |
feat(toggle-group): port from soma to eidos (Tier 1 sprint, 1/5)
First component in the soma→eidos porting sprint. Two-part compound (Provider + Item), modeled on radio-group's structure. Eidos surface: - variant: 'solid' | 'outline' | 'ghost' — mirrors <Toggle> - color: 7 tones, propagated to items via data-color cascade - size: xs/sm/md/lg/xl (responsive) - attached: boolean — segmented control affordance (shared borders, active item z-indexed above neighbors) - block: boolean — items grow to fill the inline-size - iconOnly: per-item flag — square aspect, sr-only label Items consume the Toggle palette token vocabulary (--toggle-*) so a standalone <Toggle> and a <ToggleGroup.Item> at the same variant × color × size are visually identical — single source of truth for the "toggle button" recipe. Morfo scope fix: ['soma'] → ['soma', 'sema', 'eidos']. Adds apg URL (WAI-ARIA Button pattern) so the audit's A-1.4 rule passes. The commit-toggle event's sound comes from the `commit` family base — no per-component cascade needed. Demo: canonical 6-tab template (Live / API / Morfo / Sema / Recipe / A11y) with every prop exposed as a chip/switch, soma + eidos snippets update live, sema "▶ play" button on the commit-toggle event. Reference comparison vs Radix Primitives / Bits UI / Ark UI / Chakra v3 / Mantine / Radix Themes / shadcn documented in README §Comparativa. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
e11d3539ef |
feat(scroll-area): add size/radius/scrollbars props + fix scroll-type race
Reference-library parity (Radix Themes, Chakra v3): - size: 'xs' | 'sm' | 'md' | 'lg' (Chakra t-shirt scale) → thickness via --scroll-area-scrollbar-size. - radius: 'none' | 'sm' | 'md' | 'lg' | 'full' (Radix Themes) → thumb curvature via --scroll-area-thumb-radius. - scrollbars: 'vertical' | 'horizontal' | 'both' (Radix Themes) → recipe-level sugar that auto-mounts Scrollbar / Thumb / Corner from the root. Explicit composition still supported (omit the prop). Bug fix — type='scroll' + horizontal sometimes stuck hidden: - `mounted` on ScrollAreaScrollbarProvider is now $state. Previously a plain field, so the `shouldShow` derived didn't invalidate when the requestFrame mount-flag flipped during the first frame. - The reveal-on-scroll $effect now skips its first run via a `firstRun` flag. That first run was the subscription pass with scrollTop/scrollLeft still 0 — it was eagerly calling show() and setting `visible=true` before `mounted` flipped. Subsequent real scrolls then assigned visible=true again (no-op), so shouldShow didn't re-run and the bar stayed hidden until the hide timer expired. Now the bar only reveals on genuine scroll deltas. Demo: chip controls for size + radius, switched to scrollbars prop on the root (drops the explicit Scrollbar/Thumb children), eidos snippet shows the recipe-sugar form, soma snippet still shows the explicit Radix-style composition. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
560ce8f66c |
fix(scroll-area, drawer): horizontal scrollbar visibility + drawer drag affordance
- scroll-area soma: defer the mount-frame request to `$effect` so SSR doesn't hit `dom::window_required` during constructor evaluation. - scroll-area recipe: scrollbars now `position: absolute` (overlay style) so the horizontal track lands at the viewport's block-end instead of flowing below it. `:has` rules reserve corresponding edges when both axes mount; corner switched to absolute as well. - drawer demo: drop the ↑↓←→ glyph pop-outs and `overflow: visible` during dragging. The handle's own pill grip is the affordance — the same shape the splitter uses, no extra chrome. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
bd2e40366a |
feat(layout): Layout Batch 3 — scroll-area, separator, splitter + splitter sema rewire
3 new layout primitives close the air port set (15 / 15 done).
`npm run component:audit`: 91 / 91 PASS, all 3 new PASS with 0 errors.
**scroll-area** — eidos compound wrapper over the existing soma:
`<ScrollArea><ScrollArea.Viewport>{content}</ScrollArea.Viewport>
<ScrollArea.Scrollbar orientation="vertical"><ScrollArea.Thumb />
</ScrollArea.Scrollbar><ScrollArea.Corner /></ScrollArea>`. Mirrors
Radix Primitives / Bits UI shape. Demo 667 lines.
**separator** — visual primitive (`scope: ['eidos']`) with conditional
ARIA: `role="separator"` + `aria-orientation` when not decorative,
`aria-hidden="true"` when decorative. New morfo
`src/uix/morfo/components/separator.ts`. Demo 645 lines.
**splitter** — eidos compound over existing soma:
`<Splitter orientation="horizontal"><Splitter.Panel>{a}</Splitter.Panel>
<Splitter.ResizeTrigger /><Splitter.Panel>{b}</Splitter.Panel>
</Splitter>`. Demo 824 lines.
**Splitter sema rewire — match Slider's handle/drag/commit shape:**
The morfo previously declared only `commit-resize`. Direct-manipulation
primitives (Slider, Splitter) should share the same event vocabulary so
audio/haptic projections feel coherent across the family. Renamed +
expanded to mirror Slider:
- `handle-pick` (family: handle, verb: pick, sequence: coincident,
target: resize-trigger) — fires on pointerdown when user grabs the
trigger.
- `handle-drag` (family: handle, verb: drag, sequence: coincident,
target: resize-trigger) — fires on each pointermove frame.
- `commit-set` (family: commit, verb: set, intent: neutral,
sequence: post, target: provider) — fires on pointerup. The runtime
`commit-set` handler is wired to call the consumer's `onResizeEnd`
callback, so sema and JS callback land at the same moment (parity
with Slider's `onValueCommit`).
Soma provider triggers these via `this.runtime.trigger(name)` in
`onpointerdown` / `onpointermove` / `notifyResizeEnd` (the latter
unchanged externally — internally now routes through the runtime
trigger).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
a08a0a9890 |
fix(aspect-ratio): fill inline axis by default + panel demo uses solid colors
Two bugs surfaced when verifying the panel demo visually: 1. **AspectRatio shrunk to content width inside grid/flex containers.** Grid items default to `width: max-content`, so AspectRatio without an explicit width was sizing itself to its child's intrinsic content instead of filling the slot. The ratio still applied but against the collapsed width, producing a tiny box. Fix: recipe sets `inline-size: var(--box-width, 100%)` so the default is "fill the slot". `width=…` from Box still wins; `maxWidth=…` constrains the upper bound exactly as before. Block-level containers are unaffected (100% of the line box matches their natural width). 2. **Panel demo gradient invisible in dark mode.** The demo used `--color-primary-track` + `--color-affirm-track` which are surface tokens (near-black in dark theme). Swapped to `*-solid` tokens with `--color-content-on-solid` text for high contrast in both themes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
d421f1fb5d |
feat(typography): 8 primitives — Heading, Display, Code, CodeBlock, Kbd, Mark, Highlight, Link
Full canon for the rest of the typography family, following Text
(commit `03531709`) as template. Each primitive ships morfo (passive,
scope eidos, 0 events) + full eidos set (svelte/css/types/index +
README with required sections) + canon 6-tab demo (≥400 lines each).
**Heading** — semantic h1..h6 via `level` prop, visual style decoupled
via `style` prop (defaults to match level but `<Heading level={3}
style="h1">` allowed). Demo 594 lines.
**Display** — always-h1 page-title primitive. Always uses
`style="hero"`. Tracking via literal em values until foundation emits
hero-scale tracking tokens. Demo 546 lines.
**Code** — inline code via `<code>`. `variant: 'plain' | 'soft' |
'outline'` for subtle background chrome. Demo 423 lines.
**CodeBlock** — multi-line `<pre><code>` with optional title +
copy button (button utility, NOT a sema verb — kept out of morfo per
2-of-3 rule). Demo 402 lines.
**Kbd** — keyboard shortcut chrome over `style="code"`. Demo 418 lines.
**Mark** — text highlight via `<mark>`. Palette uses canonical
`ColorRole` intents (default `fulfill` ≈ legacy yellow). Demo 402 lines.
**Highlight** — substring highlighter inside Text. Scans children for
`query` and wraps matches. Same intent palette as Mark. Demo 407 lines.
**Link** — text link via `<a>`. `variant: 'default' | 'subtle' |
'plain'` for underline style, `intent: ColorRole` for color. Passive
on the morfo — hover/active is CSS-only, navigation belongs to the
consumer (router). Demo 448 lines.
**Architectural deviations from air documented per README's
Decisiones section:**
- Heading: semantic level vs visual style decoupling
- Code split into inline `<Code>` + block `<CodeBlock>` (was one
component with `variant='block'` in air)
- Mark/Highlight palette swapped from literal hue names to canonical
ColorRole intents (theme contract)
- Link palette opened to full ColorRole; `quiet`/`emphasized` air
variants dropped in favour of composable patterns
`npm run component:audit`: **90 / 90 PASS**, 0 NEEDS-WORK.
svelte-check: 0 errors, 0 new warnings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
0353170982 |
feat(typography): Text — POC consuming new --style-* foundation tokens
First typography primitive consuming the named-style tokens added in
`015bd761`. Sets the canon for the remaining typography ports
(Heading, Display, Code, CodeBlock, Kbd, Mark, Highlight, Link).
API:
- `style: 'body' | 'prose' | 'label' | 'caption'` (default `body`).
Selects which `--style-{name}-*` token set the recipe consumes.
- Per-axis overrides via `size`, `family`, `weight`, `color`, `align`.
Each writes a local `--_text-{axis}` CSS variable that wins over
the named style's default via the cascade.
- `italic`, `underline` shorthand booleans.
- `truncate` (single-line ellipsis) + `clamp={N}` (multi-line via
CSS `-webkit-line-clamp`).
- `as` prop switches the tag (default `'span'` — inline composable).
- `tagStyle` escape hatch for one-off inline CSS that the prop
surface doesn't cover (separate from the `style` named-style prop).
Architectural choices documented in README:
- Sizes map to eidos `xxs..xxxl` (not air's `1..12`).
- Families use the eidos slot names (`primary/secondary/mono/display`)
not air's domain aliases (`ui/prose/code`) — coherence with the
foundation contract.
- Colors are restricted to the content-role slots
(`--color-content-*`) to preserve contrast guarantees. Free colors
via `tagStyle`.
- Canvas line-count measurement (air's `text` + bindable `lineCount`)
deferred; CSS line-clamp covers the visual-truncate use case.
Sidebar nav gains a `Typography` group with Text as its first entry.
`npm run component:audit`: 82 / 82 PASS, 0 NEEDS-WORK. svelte-check 0
errors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
376f9e33eb |
feat(layout): Layout Batch 2 — aspect-ratio, auto-grid, banner, float
4 new layout primitives, full canon (morfo + eidos full set + canon
demo + README). `npm run component:audit`: 81 / 81 PASS, 0 NEEDS-WORK.
**AspectRatio** — composes through Box; emits single `--aspect-ratio`
CSS var (uses modern CSS `aspect-ratio` property, drops legacy
padding-bottom hack). Accepts numeric (16/9 → 1.777…) or string
("16/9") values.
**AutoGrid** — composes through Grid. Resolves `templateColumns` from
`minItemWidth` to `repeat(auto-fill, minmax(MIN, 1fr))` in JS so the
recipe stays declarative. Uses `auto-fill` (not auto-fit) to preserve
empty tracks when item count is low.
**Banner** — `<header role="banner">` announcement strip with intent
(full 8-role UIX `ColorRole`), variant (`ChipVariant` soft/solid/
outline/ghost), size (sm/md/lg). Dismissal is composition-driven:
consumer wraps in {#if show} and adds `<Banner.Close>`. No
`dismissible` boolean.
**Float** — CSS `float` primitive with logical `inline-start` /
`inline-end` sides. Redefined from air's 9-zone overlay primitive
(which becomes a future `<Positioned>` component if real demand
surfaces). Useful for inline images / pull-quotes / drop caps.
Each component ships morfo (`scope: ['eidos']`, 1 part, 0 events,
justified) + eidos set + README (Baseline / Comparativa /
Decisiones / Eventos Sema / Gaps with disposition markers /
Referencias / Passive justification) + canon demo with 6 tabs.
Sidebar nav appends the 4 entries to the existing Layout group.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
265a3946b5 |
feat(layout): plug Batch 1 gaps — Flex.alignContent + Grid.columns/rows/inline/alignContent
Addresses the "should" features documented as gaps in the layout
READMEs from `fdb0e680`. Audit script still reports 77 / 77 PASS,
svelte-check 0 errors.
**Flex**
- `alignContent` prop (multi-line cross-axis alignment). Maps to
`--flex-align-content`; defaults to `normal` in the recipe so it
has no effect when wrap is off. Closes the Radix / Chakra parity
gap.
**Grid**
- `inline` boolean — renders as `display: inline-grid` (paridad con
Flex.inline).
- `columns` / `rows` numeric shorthands — Radix Themes pattern.
Internally resolved to `repeat(N, minmax(0, 1fr))`. `templateColumns`
/ `templateRows` string still wins if both are passed.
- `alignContent` prop — same shape as Flex.
**Shared**
- New `LayoutAlignContent` type added to `_layout/shared.ts`
(covers start, end, center, stretch, space-between, space-around,
space-evenly, flex-start, flex-end, normal, baseline).
**Group**
- Verified `grow` boolean already implemented in `0ec2a57a` /
`591b0885` — no change needed; the README now correctly reflects it.
**Docs**
- Flex + Grid READMEs updated: comparison tables mark previously
missing features as Sí; Gaps section marks them ✅ implementado.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
ec66999b6c |
docs+style: close last 3 NEEDS-WORK → 77/77 PASS audit
`npm run component:audit` now reports 77 / 77 PASS, 0 NEEDS-WORK, 0 BROKEN. **month-grid + year-grid READMEs** Add Baseline / Comparativa / Decisiones / Gaps sections (F-1.1 / F-1.2 / F-1.3 / F-1.4). Both grids are standalone primitives extracted from DatePicker's `kind: 'month' | 'year'` views (task `#70`); they have no air counterpart. Documented Sema event design (single `commit-set` for value mutation, `nav-step` for paginated header navigation), the 4×3 grid choice, configurable `decadeSize` for YearGrid, and known gaps (multi-year range, quick-jump, localized month names — all deferred). **Audit script `focusMoveActions` extension** `A-3.7` was firing on month-grid/year-grid because the script didn't recognise `next-row`, `prev-row`, `first-month`, `last-month`, `first-year`, `last-year`, `next-page`, `prev-page` as focus moves. They ARE focus moves inside their respective grids (same semantic as `next-day` / `first-day-of-week` inside Calendar). Added to the focusMoveActions set in `scripts/component-audit.ts` so the event-vs-keyboard ratio heuristic no longer over-counts. **time-range-picker** - README Gaps section rewritten as a table with explicit disposition markers (`implementar` / `diferir` / `descartar`) per F-1.4. - Recipe `time-range-picker.css` now styles `[data-readonly]` (lower opacity + pointer-events: none on trigger/clear) and `[data-invalid]` (threat-colour border + focus ring) — closes R-1.3 / R-1.4. Both attrs were declared in morfo but unstyled. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
fdb0e68028 |
docs(layout): READMEs for 8 layout primitives (audit PASS)
Closes `E-2.3` audit error for the 8 Layout Batch 1 primitives. Each README follows the canonical structure required by the `component-audit` script: - Baseline — origin (air branch), adaptations applied (drop air- prefix, rename --air-space-N → --space-N, ActiveEidos.resolve) - Superficie — minimal usage snippet - Comparativa — feature parity table vs Radix Themes / Chakra UI / Mantine, with explicit "No — gap conocido" markers for missing features - Decisiones — architectural rationale (Radix item/container split, composition over inheritance, etc.) - Eventos Sema — 0-event justification - Gaps — known feature gaps with disposition markers (implementar / diferir / descartar) and reference attribution - Referencias — links to canonical reference docs - Passive justification — why scope is `eidos` only Notable gaps documented for backlog: - Flex.alignContent (Radix/Chakra) - Grid.columns/rows numeric shorthand (Radix) - Grid.inline boolean (parity with Flex) - Grid.alignContent (Radix/Chakra) - Stack.divider slot (Chakra) - Stack/Flex HStack/VStack helpers (Chakra ergonomics) - Group.grow boolean (Mantine — children fill equally) - Group.preventGrowOverflow (Mantine) - Wrap.shouldWrapChildren (Chakra) - Section.as prop for semantic <section> render `npm run component:audit`: 77 components, 74 PASS, 3 NEEDS-WORK (month-grid, year-grid, time-range-picker — pre-existing, unrelated to layout work). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
66c6897c61 |
feat(layout): canon Box demo + relocate grid item placement props to Box
POC for redoing Layout Batch 1 demos to follow DEMO_AUTHORING_GUIDE. Box becomes the reference template for the remaining 7 primitives. Demo follows the canon shape: - Header with eyebrow + 4 meta pills (parts, events, props, scope). - Stage permanently rendered with 3 nested boxes + trace strip showing live display/padding/gap state. - 6 tabs in canonical order (Live · API · Morfo · Sema · Recipe · A11y). - Live tab: 7 controls + reactive soma/eidos snippets. - API tab: 6 subsections grouped by concern (sizing / spacing / display+position / flex/grid item / escape hatch / reference comparison vs radix-themes / chakra / mantine). - Morfo / Sema / Recipe / A11y tabs each populated with the contract appropriate to a passive eidos-native primitive. Architectural fixes (heredados de air, ahora correctos): - `gridColumn`, `gridRow`, `gridArea` move from `<Grid>` to `<Box>` — these are item placement props, they belong on the CHILD that declares its slot, not the container. - Add `placeSelf` to Box (grid item shorthand for align-self + justify-self). - Grid recipe + props updated accordingly; comment clarifies that item placement lives on Box. Add `src/uix/morfo/components/box.ts` (scope `eidos`, 1 part, 0 events with justification comment). Box demo updated lede to point at Flex / Grid for container-side props (Radix Themes split, not Chakra everything-on-Box). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
9ec2a57a85 |
feat(eidos): port 8 layout primitives from air (box, flex, grid, stack, group, wrap, container, section)
Visual-only primitives translated from the legacy `air/components/layout`
on the morfo-runtime branch. Architectural adjustments for the eidos
target: drop the `air-` class prefix, target `[data-{name}]` directly,
rename CSS vars to `--{name}-*`, replace `var(--air-space-N)` with the
eidos foundation `var(--space-N)` token, resolve responsive props via
`ActiveEidos.resolve(prop)`.
- `box` — universal box-model utility (size, padding, margin, gap,
position, overflow, flex/grid item props). Every prop maps to a
CSS custom property; unset props fall through via `revert-layer`.
- `flex` / `grid` / `stack` / `group` / `wrap` — layout containers
that render through Box (single `<div>` per primitive, no
semantic-tag prop in this batch). Markers stack as
`[data-box][data-{name}]`.
- `container` — max-width centered (sm/md/lg/xl/full).
- `section` — semantic-padding block with background tone variants.
Shared helpers at `_layout/shared.ts` (formatLayoutSpace /
formatLayoutLength / formatLayoutRaw / pushStyleVar / composeStyle)
plus an optional `Layout.*` namespace barrel for consumers who prefer
that style; the individual `import { Box }` from each component dir
also works.
Each primitive ships a demo at `/uix/components/{name}/` with a live
example, a props table, and a reference comparison row (radix-themes /
chakra-ui / mantine).
svelte-check: 0 errors / 0 new warnings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
20709caf9c |
refactor(combobox): SelectedTags as band above the Control (Chakra pattern)
Switched the multi-mode chips from MUI-style "inline inside the input" to the Chakra v3 multi-select pattern — chips render as a separated band ABOVE the Control. Two wins: - The dropdown popover (opens downward) never covers the selected chips, which sit comfortably above the input. - The Control stays a normal-height single-line input. No layout shifts, no absolute-positioned affordances, no `display: contents` gymnastics, no input flex tuning. Reverts the inside-the-Control CSS gymnastics from `30e9517a` / `64bbe9b1` / `87e3d68f` (flex-wrap, padding-right reservation, absolute clear+trigger, input flex basis). `<Combobox.SelectedTags>` now renders as a plain `flex-wrap` row with chip gap and a margin- block-end separating it from the Control. Demo composes `<Combobox.SelectedTags />` BEFORE `<Combobox.Control>` when `mode === 'multiple'`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
91b0efc461 |
fix(combobox): inline chips + input flow as siblings via display:contents
`<Combobox.SelectedTags>` was a nested flex container so the input always wrapped to its own row below the entire chip block — wasting the empty space on the last chip row. Use `display: contents` so each chip becomes a direct flex child of `<Combobox.Control>`. Chips + input now share the same wrap context: the input flows on the same row as the last chip when there's room. `[data-empty]` still toggles to `display: none` so empty-state still collapses correctly (and the `:has(...:not([data-empty]))` selector that pins clear/trigger absolutely keeps working). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
87e3d68f86 |
fix(combobox): pin clear+trigger to first row, not vertical center
With many chips wrapping into 3+ rows, the previous `top: 50%` + translateY centered the clear/trigger pair at the midpoint of the whole stack — visually below the first row of chips. Pin to `top: var(--space-1)` instead so they sit flush with the first chip row no matter how many additional rows of chips wrap below. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
64bbe9b158 |
fix(combobox): pin clear+trigger absolute when chips present (MUI pattern)
When `<Combobox.SelectedTags>` has at least one chip, anchor the ClearButton + Trigger absolutely at the right edge of the Control (MUI Autocomplete pattern). Chips + input can now wrap freely without ever pushing the right-side affordances to a new row. `--_combobox-affordances-w` reserves the right-padding the absolutely-positioned buttons consume, scaled to the control's current height token. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
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 |
|
|
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 |
|
|
da10548a68 |
revert(popover): drop auto-anchor min-width default; keep content-driven sizing
Yesterday's checkpoint normalized popover min-width to the floating anchor
width across all components, then re-anchored pickers to their input
wrapper so the rule would line popover ≥ input. Tested at /uix/components/
date-picker the result was a popover stretched to 458 px (input width)
while the calendar inside is only ~268 px — leaving an empty band to the
right of every row.
After re-checking the industry research:
- Radix / shadcn / React Aria popovers default to INTRINSIC content width
and expose `--*-trigger-width` as opt-in.
- Ark UI only auto-matches via `positioning.sameWidth` (opt-in).
- bits-ui Select is the only one that bakes width matching by default.
Conclusion: forcing min-width = anchor-width across all popovers is wrong;
each picker's content has its own intrinsic width that should drive the
popover. Reverting the normalization to the previous (Radix-style)
content-driven default. The Select + Combobox `matchAnchorWidth = true`
default is preserved (those are listbox-style popovers where the trigger
width is a sensible reference) and the sideOffset=6 normalization stays.
Files reverted:
- src/uix/eidos/components/popover/popover.css: removed
`--_popover-min-width: var(--floating-anchor-width)` default.
- src/uix/eidos/components/date-picker/date-picker-content.svelte:
restored `--_popover-content-min-width-override: max-content` default
and dropped the `customAnchor={picker.inputRef.current}` wiring.
- src/uix/eidos/components/date-picker/date-picker.css: restored
`min-inline-size: max-content` on the calendar/grid popover rules.
- src/uix/soma/components/date-picker/date-picker-provider.svelte.ts:
removed `inputRef` + `setInputRef`.
- Removed src/uix/{soma,eidos}/components/date-picker/date-picker-input.svelte.
- src/uix/{soma,eidos}/components/date-picker/{exports,index}: revert
`Input` to the DateField re-export.
Kept from yesterday:
- sideOffset = 6 default in popover-content + 5 pickers (genuine
normalization, no width tension).
- Select/Combobox matchAnchorWidth = true (listbox convention).
- floating.svelte.ts `opts.style.current` bug fix (was preventing
any consumer style from reaching the popover wrapper).
- Combobox single-mode auto-sync of inputValue to selected label.
Verified at /uix/components/date-picker: popover renders at 268 px
(calendar content width), aligned end of the 458 px input — matches the
Radix-style behaviour we now consider canonical.
svelte-check --threshold error → 0 errors / 0 warnings / 4584 files.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
79c94d85d5 |
wip(popover): unified min-width = anchor + sideOffset 6 across pickers
Day-end checkpoint for the floating-layer normalization work. Documented in CONTINUE.md. Done: - popover.css: new `--_popover-min-width` defaults to `var(--floating-anchor-width, auto)` so popover layer is NEVER narrower than its anchor (Radix / React Aria convention). Consumers that should opt out (tooltip, dialog) override with `auto`. - soma popover-content default sideOffset 0 → 6. - 5 picker eidos contents: sideOffset 10 → 6 (matches the new default; per-component override removed visually but kept as explicit prop). - combobox provider: reverted the multi-on-close stamp-labels patch. Multi-mode selections will be rendered as inline tags via a future `Combobox.SelectedTags` part (Material UI Autocomplete pattern, decided with user). Pending (see CONTINUE.md): - Re-anchor pickers to their Input wrapper (currently anchored to the chevron trigger ~14px). Required for the min-width normalization to actually show — research vs Ark UI / Radix / React Aria confirmed composite-input pickers anchor to the control wrapper, not the trigger button. - Combobox `SelectedTags` part for multi-mode inline tags. - SearchField floating Results part (user opted to make it Combobox-like rather than reuse Combobox). svelte-check --threshold error → 0 errors / 0 warnings / 4584 files. 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 |
|
|
826481f1d5 |
fix(picker-shell): footer buttons scale with picker size (xs/sm/md/lg)
The footer buttons (Clear / Cancel / Done) were hardcoded to control-height-sm + font-size-sm + space-3 padding regardless of the host picker's size. At xs the buttons looked oversized next to a compact picker; at lg they looked undersized. - picker-shell.css: refactored button rules to consume four `--_picker-shell-*` CSS vars (control-h, padding-x, font, gap, row-spacing). Defaults match the previous sm sizing. Added explicit xs/md/lg variant blocks keyed off `[data-popover-content][data-picker-size='X']`. - date-picker-content / date-range-picker-content / time-picker-content / time-range-picker-content / color-picker-content: forward the picker's resolvedSize to the popover via a separate `data-picker-size` attr, independent from the popover chrome's clamped `data-size`. This decouples the popover's own chrome (which still clamps xs → sm because the popover layer has no xs variant) from the picker-shell button scaling, which now uses the picker's true size. Verified at /uix/components/date-picker: - xs → 22 / 12px (control-height-xxs, font-size-xs) - sm → 30 / 14px (default) - md → 36 / 16px - lg → 44 / 16px Also fixes the horizontal scroll at xs that surfaced the issue. The date-picker.css `[data-popover-content]:has(...)` rule lets the popover grow to max-content when it hosts a calendar / grid. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
b11ef88bca |
fix(month-grid, year-grid): stretch to popover content width inside picker
When a MonthGrid / YearGrid is rendered inside a DatePicker.Content popover (via DatePicker.MonthView / YearView), the grid was using inline-size:max-content while the picker-shell Footer below it stretched to the popover's full content width — leaving an empty band to the right of each row of cells. Scope the override to `[data-popover-content]` so the standalone grids on their own demos keep their natural max-content sizing. Inside the popover, the grid root and the inner [data-*-grid] both stretch to 100% so the 3 columns (minmax(0, 1fr)) distribute across the same inline size as the footer. Also added box-sizing: border-box to both grid roots so the padding + border don't push them past the popover content area. Verified at /uix/components/date-picker with kind='year' size='md': popover content width = 288px, year-grid width = 262px (= footer width), inner grid = 228px (= grid container minus padding). 3 columns align flush to the footer row above. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
187d7d3751 |
refactor(date-picker): MonthView / YearView delegate to MonthGrid / YearGrid
date-picker-month-view.svelte and date-picker-year-view.svelte previously
rolled their own 3×4 grids with custom data-date-picker-{month,year}-view-*
markers and ~90 lines of selection / nav / formatter logic. They now
render the standalone MonthGrid / YearGrid primitives (introduced in #70)
and bridge a thin DatePickerProvider context adapter:
- value: CalendarDate ↔ {year, month?} / {year}
- placeholder: CalendarDate ↔ {year}
- onValueChange → new CalendarDate(year, month, 1) + handleDateSelect
- onPlaceholderChange → mutate provider.placeholder.year
- forwards locale / disabled / readonly straight from the provider
CSS: removed the data-date-picker-{month,year}-view-* selector block
(~90 lines of dead code). Visuals now come from month-grid.css /
year-grid.css, which means the views also inherit the size='xs' work,
roving tabindex, RTL keyboard flip and sema events for free.
Verified in browser at /uix/components/date-picker:
- kind='month' → MonthGrid renders 12 cells (Jan..Dec) inside the popover
- click Mar → value becomes 2026-03-01, input segments update to 03/2026
- kind='year' → YearGrid renders the 2016–2027 page
- svelte-check --threshold error → 0 errors / 0 warnings / 4572 files
date-range-picker MonthView / YearView left untouched — their range
state machine (start/end/in-range markers, complete-vs-pending swap) is
not modelled by the standalone grids.
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 |
|
|
c65235337d |
fix(month-grid, year-grid): cell label fallback now renders
The eidos Cell wrapper previously delegated a children snippet that called
`bodyContent?.(snippetProps)`. When the consumer wrote `<Cell {year} />`
without a children snippet, the snippet still resolved to nothing, which
made the soma cell's `{:else} {label}` fallback never fire — cells came
out empty in the DOM.
Move the fallback into the eidos cell's own children snippet:
`{#if bodyContent}{@render bodyContent(...)}{:else}{label}{/if}`.
Verified in browser: MonthGrid renders Jan..Dec with the placeholder
year, YearGrid renders the decade page correctly with data-current on
today's year. svelte-check still 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 |
|
|
cae638c993 |
fix(color-picker): ValueSwatch color overlay now actually shows on top of checker
Follow-up to
|
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 |
|
|
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 |
|
|
aa62f0203c |
refactor(pickers): date/time/date-range pickers consume shared PickerShell
Follow-up to commit
|
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 |
|
|
5d007ed9f9 |
refactor(color-picker): channel slider composes generic SliderProvider + N-8 norm
Drops ColorPickerChannelSliderProvider / TrackProvider / ThumbProvider (~200 lines of duplicated pointer/keyboard/ARIA handling) and rewrites `components/color-picker-channel-slider.svelte` to compose SliderProvider underneath. The channel slider now keeps only the channel-aware bridge: setChannel writes the value, channelGradient paints the track. What the picker gains by reusing Slider --------------------------------------- - sema events: `slider:handle-pick`, `slider:handle-drag`, `slider:commit-set` now flow through the channel sliders (they were silent before — the Sema layer had no perceptual hook on channel drag/commit). - single pointer/keyboard implementation: Arrow/Home/End/PageUp/PageDown, multi-touch, focus trap, RTL, step-snap — all from SliderProvider. - ARIA: role=slider, aria-valuenow/min/max, aria-orientation, tabindex managed by the slider's thumb provider. - recipe tokens: channel sliders consume `--slider-*` (sizes, radii, shadows) so they stay in lockstep with the standalone Slider and with TimePicker.HourSlider / .MinuteSlider / .SecondSlider. The `--color-picker-channel-slider-*` tokens are removed. Public API impact ----------------- - `<ColorPicker.ChannelSliderTrack>` and `<ColorPicker.ChannelSliderThumb>` remain as part names but now re-export `Slider.Range` / `Slider.Thumb`. Consumers that compose them inline keep working. - The inner DOM changes: `[data-slider]` / `[data-slider-thumb]` / `[data-slider-range]` (governed by the slider morfo) replace `[data-color-picker-channel-slider-track]` / `[data-color-picker-channel-slider-thumb]`. The wrapper still emits `[data-color-picker-channel-slider][data-channel]` so per-channel CSS targeting works; the gradient is exposed via `--cp-channel-gradient` and routed onto the slider's track ::before. - Morfo: drops `channel-slider-track` and `channel-slider-thumb` parts (the slider morfo owns those contracts now). `channel-slider` remains as the wrapper marker with `data-channel`. - Eidos: deletes `color-picker-channel-slider-track.svelte` and `color-picker-channel-slider-thumb.svelte`; `index.ts` re-points `ChannelSliderTrack` / `ChannelSliderThumb` at the slider eidos components. New norm N-8 added to PENDIENTES.md ----------------------------------- "Reutilizar componentes ya definidos en componentes complejos siempre que no supongan una pérdida de características demostrable." Documented with this refactor as the worked example. 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 |
|
|
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 |
|
|
e7b2e461bc |
fix(eidos/time-picker): default-compose Range+Thumb and add labelled ruler
Reported: the popover sliders showed a track with no visible thumb,
and there was no indication of the value scale.
- Each TimePicker.{Hour,Minute,Second}Slider eidos wrapper now renders
<Slider.Range /> + <Slider.Thumb /> by default when no children are
passed. Consumers can still override with custom children.
- Adds a labelled ruler row next to each slider:
- HourSlider: scale ABOVE the track. 24h shows 0/3/6/9/12/15/18/21,
12h shows 1/3/6/9/12. Numbers zero-padded for 24h.
- MinuteSlider / SecondSlider: scale BELOW the track at 0/15/30/45.
- Tick positions are computed proportionally and offset inward by
half the thumb width so the endpoint labels align with the actual
track endpoints (not the bounding box).
- Reinforces thumb visibility inside the clock popover: ring shadow
against the popover surface + accented border tied to the picker's
intent color. Range fill matches the accent border so the filled
portion of the track is unambiguous.
All checks clean: 67/67 audit PASS, svelte-check 0 errors.
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 |
|
|
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 |
|
|
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 |
|
|
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 |