Draft
Conversation
Add three high-value missing components: - **List + ListItem**: Interactive selectable list primitive. ListItem shares the same CSS as DropdownMenuItem/ContextMenuItem/ComboboxOption (same implementation, different `k` attribute). Supports `variant="nav"` for sidebar-style accent-colored hover. Polymorphic (button or anchor). - **Listbox**: Non-modal filterable list (inline command palette pattern). Structurally identical to Combobox but always visible — uses `<div>` instead of `<dialog>`, no focus trap or blur-to-close. Shares filtering logic with Combobox via extracted `filterItems()` utility in `src/lib/filter.ts`. - **Chip**: Badge with an optional icon button (`Chip.Button`). Pure CSS component with zero JS logic. Button fires standard click events. Also: - Extract `filterItems()` to `src/lib/filter.ts`, refactor Combobox to use it - Extend keyboard navigation in `commands.ts` to support non-dialog containers (`[k="list"]` with focus-based nav, `[k="listbox"]` with selected-attribute nav) - Add `[k="list-item"]` and `[k="listbox-option"]` to all shared item CSS selectors in `dropdown-menu/style.css` https://claude.ai/code/session_01VUkypWpSmfEMA5zhE3DCEg
✅ Deploy Preview for kinu-sh ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Contributor
|
Size Change: +1.3 kB (+4.11%) Total Size: 32.9 kB 📦 View Changed
ℹ️ View Unchanged
|
- List: Navigation category (alongside Tabs) — interactive selectable list with default and nav variants, shows separator support - Listbox: Data Input category (alongside Combobox) — non-modal filterable list with search input and option items - Chip: Data Display category (alongside Badge) — badge with inline action button, shows removable tags and all variants Adds markdown docs, interactive examples, manifest.json entries, and metadata.mjs entries for all three components. https://claude.ai/code/session_01VUkypWpSmfEMA5zhE3DCEg
Chip: - Fully rounded (pill shape) with secondary default, subtle hover - Chip.Button now spans the full height and hugs the rounded edge with a semi-transparent background. Auto-detects start/end placement via :first-child/:last-child for correct border-radius - Add `selected` attribute (consistent with menu items) - Changed default variant from primary to secondary (more subtle) - Added `primary` variant, removed `secondary` (was the old default) - Demo uses × (multiplication sign) instead of "x" List: - Selected items now use foreground as background with contrast-aware text (hsl(--k-foreground) bg, hsl(--k-background) text). This automatically provides correct contrast in both light and dark mode since the token system guarantees these are contrasting pairs. Listbox: - Selection is now developer-controlled. filterItems() gained an autoSelect parameter (default true for Combobox backward compat). Listbox passes autoSelect=false so filtering only shows/hides options without touching the selected attribute. - Demo updated to show onClick-driven selection. https://claude.ai/code/session_01VUkypWpSmfEMA5zhE3DCEg
Introduce `Item` (`k="item"`) as the single component used across all list-like containers: List, Listbox, DropdownMenu, ContextMenu, Combobox. The same `<Item>` component works everywhere — the parent container determines the styling context and behavior, just like `<li>` in HTML. Key changes: - New `src/components/item/` with unified component, types, and CSS - All items now polymorphic (button/a via href), tabIndex:-1 - ComboboxOption's click-to-select moved to delegated handler on ComboboxList (reads `value` attr or textContent) - CSS collapsed from 5-way selector to single `[k="item"]` (-1.6KB CSS) - commands.ts keyboard nav updated for `[k="item"]` - Context-specific TS interfaces (DropdownMenuItemProps, ComboboxItemProps, etc.) narrow the docs surface while sharing the runtime component - Backward-compat: DropdownMenuItem, ContextMenuItem, ComboboxOption, ListboxOption, ListItem all re-exported as aliases of Item - Parent components expose `.Item` (e.g. DropdownMenu.Item, Combobox.Item) - All docs/examples updated to use `<Item>` - Demo still builds (72 pages) — old names work via re-exports https://claude.ai/code/session_01VUkypWpSmfEMA5zhE3DCEg
- Remove negative left margin that caused button to overlap text - Increase button padding for a proper hit target - Bump font-size to 1em so × character renders at readable size https://claude.ai/code/session_01VUkypWpSmfEMA5zhE3DCEg
The button margins now use calc(-padding - 1px) to fully cancel both the chip's padding and its 1px border, so the button sits flush against the rounded edge of the chip instead of being inset. https://claude.ai/code/session_01VUkypWpSmfEMA5zhE3DCEg
Use position:relative + left:0.625rem to push the button flush to the chip's right edge. The chip's overflow:hidden + border-radius clips it to the pill shape naturally. Much simpler than fighting with margins. https://claude.ai/code/session_01VUkypWpSmfEMA5zhE3DCEg
Rewrite with the correct mental model: the button is a small circular icon (like Material Chip) inline with the text, not a full-height bar stretching to the chip's edge. - Chip uses gap:0.375rem for natural separation between text and button - ChipButton is 1.125rem × 1.125rem with a circular background - :has() selector tightens chip padding on the edge where the button sits - No positioning tricks or overflow hacks — just flexbox https://claude.ai/code/session_01VUkypWpSmfEMA5zhE3DCEg
…rgin - Chip keeps its normal padding on all sides - Button uses align-self:stretch + aspect-ratio:1 for a full-height circle - Negative margins on the outward edge pull the button flush to the chip's rounded edge (margin-right for last-child, margin-left for first-child) - margin-block cancels vertical padding so the button fills chip height - Button's border-radius naturally aligns with the chip's pill shape https://claude.ai/code/session_01VUkypWpSmfEMA5zhE3DCEg
aspect-ratio:1 doesn't give flex items a width when only height is derived via stretch. Set explicit width and height to match the chip's full border-box height (1.5rem + 2px), keeping the negative margin only on the outward edge. https://claude.ai/code/session_01VUkypWpSmfEMA5zhE3DCEg
The button already has align-items:center on the chip, so it stays vertically centered without needing to cancel padding. The explicit height is taller than the chip's content box so it visually overlaps the padding area naturally. https://claude.ai/code/session_01VUkypWpSmfEMA5zhE3DCEg
The flush-to-edge approach is a dead end: text nodes don't count for :first-child/:last-child, so a button next to text always matches both selectors and gets both outward margins. Back to a simple inline button that respects the chip's normal padding + gap. https://claude.ai/code/session_01VUkypWpSmfEMA5zhE3DCEg
Chip renders as <button> so it gets native keyboard activation, focus ring, and accessible button role for free. Chip.Button renders as a <span role="button"> so it can be nested inside without breaking HTML validity (nested <button> elements are reparented by the HTML parser). Chip.Button installs a default onClickCapture that calls stopPropagation, so clicks on the action button don't bubble up to trigger the chip's main click handler. The span is marked aria-hidden so screen readers announce only the chip's text, not the × glyph. Developers who need a keyboard-accessible remove button can add tabIndex and a keydown handler themselves — that's an explicit opt-in for a less common pattern. https://claude.ai/code/session_01VUkypWpSmfEMA5zhE3DCEg
onClickCapture with stopPropagation was preventing the user's onClick
prop on the same element from firing. Switch to attaching a plain
addEventListener('click') handler via the ref callback, which runs
after Preact applies the user's onClick prop listener. At click time,
handlers fire in registration order: user's onClick first, then our
stopPropagation.
https://claude.ai/code/session_01VUkypWpSmfEMA5zhE3DCEg
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR introduces three new UI components to the component library:
List,Listbox, andChip. These components extend the existing menu/command infrastructure with additional interactive list patterns and a compact chip/tag component.Key Changes
List Component: A flexible interactive list with
ListandList.Itemsubcomponentsnavvariant for sidebar-style navigation with accent colorsListbox Component: A non-modal, filterable list component with
Listbox,Listbox.Input,Listbox.List, andListbox.OptionsubcomponentsChip Component: A compact
Chipcomponent with optionalChip.Buttonsubcomponentsecondary,destructive, andoutline(primary is default)Shared Utilities:
filterItems()utility function insrc/lib/filter.tsfor reusable filtering logicinstallMenuShortcuts()to support List and Listbox containers in addition to dialogsExports: Added all new components and their type definitions to main
src/index.tsImplementation Details
createSimpleComponentfactory patternhttps://claude.ai/code/session_01VUkypWpSmfEMA5zhE3DCEg