Skip to content

Add new InputGroup compound component with Addon, Suffix, and Button support#249

Open
pedromenezes1 wants to merge 38 commits intocloudflare:mainfrom
pedromenezes1:feat/input-group-addon
Open

Add new InputGroup compound component with Addon, Suffix, and Button support#249
pedromenezes1 wants to merge 38 commits intocloudflare:mainfrom
pedromenezes1:feat/input-group-addon

Conversation

@pedromenezes1
Copy link
Copy Markdown
Collaborator

@pedromenezes1 pedromenezes1 commented Mar 18, 2026

New InputGroup compound component for building inputs with icons, addons, inline suffixes, and action buttons.

Includes comprehensive documentation page with demos and unit tests.

Features

  • Field Integration — InputGroup accepts label, description, error, required, and labelTooltip props directly; automatically wraps in Field when label is provided
  • Addons — Place icons or text before/after the input using align="start" or align="end"
  • Compact Button — Small button inside an Addon for secondary actions (copy, clear, toggle visibility)
  • Action Button — Full-height flush button as a direct child for primary actions (submit, search)
  • Inline Suffix — Text that flows seamlessly next to the typed value (e.g., .workers.dev); input width adjusts automatically as user types
  • Size Variants — xs, sm, base, lg sizes cascade to all children via context
  • Error State — Error flows through context; InputGroup.Input auto-sets aria-invalid="true" when error is present
  • Disabled State — disabled prop disables all interactive children

Sub-components

Component Description
InputGroup Root container; provides context and accepts Field props
InputGroup.Input Styled input; inherits size, disabled, error from context
InputGroup.Addon Container for icons, text, or compact buttons; align="start" (default) or align="end"
InputGroup.Button Full-height button (direct child) or compact button (inside Addon)
InputGroup.Suffix Inline text suffix with automatic width measurement

Usage Examples

// With Field props (label, description, error, tooltip)
<InputGroup
  label="Email"
  description="We'll never share your email"
  error={{ message: "Invalid email", match: "typeMismatch" }}
  labelTooltip="Used for account recovery"
>
  <InputGroup.Input type="email" />
  <InputGroup.Addon align="end">@example.com</InputGroup.Addon>
</InputGroup>

// Inline suffix with auto-measuring width
<InputGroup>
  <InputGroup.Input placeholder="my-worker" />
  <InputGroup.Suffix>.workers.dev</InputGroup.Suffix>
</InputGroup>

// Search with icon addon and action button
<InputGroup>
  <InputGroup.Addon><MagnifyingGlassIcon /></InputGroup.Addon>
  <InputGroup.Input placeholder="Search..." />
  <InputGroup.Button variant="primary">Search</InputGroup.Button>
</InputGroup>

// Password with compact toggle button inside addon
<InputGroup label="Password">
  <InputGroup.Input type={show ? "text" : "password"} />
  <InputGroup.Addon align="end">
    <InputGroup.Button variant="ghost" size="sm" onClick={toggle}>
      {show ? <EyeSlashIcon /> : <EyeIcon />}
    </InputGroup.Button>
  </InputGroup.Addon>
</InputGroup>

Caveats

  • InputGroup.Input omits label, description, error, size, and labelTooltip props — these are handled by the parent InputGroup
  • When using InputGroup.Suffix, the input width is measured dynamically via a hidden ghost element
  • InputGroup.Button renders differently based on placement: full-height flush when direct child, compact when inside Addon

Screenshots

Screenshot 2026-03-18 at 14 13 05 Screenshot 2026-03-18 at 14 13 10 Screenshot 2026-03-18 at 14 13 13
Screenshot 2026-03-18 at 14 13 15 Screenshot 2026-03-18 at 14 13 18 Screenshot 2026-03-18 at 14 13 21
Screenshot 2026-03-18 at 14 13 25
Screenshot 2026-03-18 at 14 13 39 Screenshot 2026-03-18 at 14 13 42 Screenshot 2026-03-18 at 14 13 46
Screenshot 2026-03-18 at 14 13 50 Screenshot 2026-03-18 at 14 13 53 Screenshot 2026-03-18 at 14 13 59
Screenshot 2026-03-18 at 14 14 02

Checklist

  • bonk has reviewed the change
  • Tests included/updated

@pedromenezes1 pedromenezes1 force-pushed the feat/input-group-addon branch from b348d28 to 86e9d5d Compare March 18, 2026 01:38
@pedromenezes1 pedromenezes1 changed the title fix(InputGroup): flush button properly covers container ring border feat(InputGroup): revamp component with new compound API Mar 18, 2026
@pedromenezes1 pedromenezes1 changed the title feat(InputGroup): revamp component with new compound API feat: new InputGroup compound component with Addon, Suffix, and Button support Mar 18, 2026
@pedromenezes1 pedromenezes1 changed the title feat: new InputGroup compound component with Addon, Suffix, and Button support Add new InputGroup compound component with Addon, Suffix, and Button support Mar 18, 2026
@pedromenezes1 pedromenezes1 force-pushed the feat/input-group-addon branch 17 times, most recently from 1f88e51 to ca11f35 Compare March 18, 2026 14:10
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Mar 18, 2026

npm i https://pkg.pr.new/@cloudflare/kumo@249

commit: bb67d2d

@pedromenezes1 pedromenezes1 force-pushed the feat/input-group-addon branch from ca11f35 to 5c4d6f2 Compare March 18, 2026 14:12
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 18, 2026

Docs Preview

View docs preview

Commit: bb67d2d

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 18, 2026

Visual Regression Report — 7 changed, 26 unchanged

7 screenshot(s) with visual changes:

Button / Loading State

8 px (0.01%) changed

Before After Diff
Before After Diff

Select / Select Sizes

901 px (0.48%) changed

Before After Diff
Before After Diff

Select / Select With Field

635 px (0.54%) changed

Before After Diff
Before After Diff

Select / Select With Tooltip

294 px (0.29%) changed

Before After Diff
Before After Diff

Select / Select Loading

0 px (0%) changed

Before After Diff
Before After Diff

Select / Select Long List

898 px (0.76%) changed

Before After Diff
Before After Diff

Select (Open)

0 px (0%) changed

Before After Diff
Before After Diff
26 screenshot(s) unchanged
  • Button / Basic
  • Button / Variant: Primary
  • Button / Variant: Secondary
  • Button / Variant: Ghost
  • Button / Variant: Destructive
  • Button / Variant: Outline
  • Button / Variant: Secondary Destructive
  • Button / Sizes
  • Button / With Icon
  • Button / Icon Only
  • Button / Disabled State
  • Button / Title
  • Dialog / Dialog Basic
  • Dialog / Dialog Confirmation
  • Dialog / Dialog With Dropdown
  • Dialog (Open)
  • Select / Select Basic
  • Select / Select Without Label
  • Select / Select Placeholder
  • Select / Select Custom Rendering
  • Select / Select Multiple
  • Select / Select Complex
  • Select / Select Disabled Options
  • Select / Select Disabled Items
  • Select / Select Grouped
  • Select / Select Grouped With Disabled

Generated by Kumo Visual Regression

@mattrothenberg mattrothenberg force-pushed the feat/input-group-addon branch from 0caf233 to 5bbecc8 Compare March 18, 2026 14:40
@pedromenezes1 pedromenezes1 force-pushed the feat/input-group-addon branch 4 times, most recently from 09d553b to 5917fe8 Compare March 23, 2026 22:15
pedromenezes1 and others added 9 commits April 4, 2026 21:44
- Fix nested <label> bug: conditional <div>/<label> container based on label prop
- Resolve focusMode API inconsistency: mark @internal consistently
- Remove dead child.type === 'button' string check in addon
- Remove misleading variant/shape props from InputGroupButtonProps
- Add dev-mode console.warn when sub-components used outside InputGroup
- Remove dead CSS outline: auto override in kumo-binding.css
- Remove data-focus-scope scaffolding from demo
- Delete accidentally committed empty dist.md
- Update changeset to reflect final shipped API
- Use Omit<InputGroupRootPropsInternal, 'focusMode'> for public type
- Remove focusMode from KUMO_INPUT_GROUP_VARIANTS export
- Move focusMode variant data to internal-only constant
- Add dist.md to kumo-docs-astro .gitignore
Removes lint error for unused variable. The focusMode variant data was
kept as documentation but nothing referenced it.
backward compat (stratus: 86 files):
- removed old input/input-group.tsx, re-exported from old path
- added InputGroup.Label/Description as deprecated aliases for Addon/Suffix
- restored variant and shape on InputGroup.Button
- migrated pagination to new inputgroup import

button behavior:
- reads focusMode from context: container=compact ghost, individual=standalone
- one-size-down rule in container mode (lg→base, base→sm, sm→xs)
- icon sizing fixed for forwardRef components (phosphor icons)
- addon auto-reduces padding when containing a button

focus & interaction:
- invisible label overlay on div container for click-to-focus on suffix
- z-index layering keeps input/addon above overlay
- suffix pointer-events-none for click passthrough

api cleanup:
- disabled omitted from InputGroup.Input type (root only)
- variant description updated on InputGroup.Button
- dev-mode warnings for misplaced props on sub-components

docs & demos:
- demos refactored to self-contained functions for code extraction
- width constraints via className or wrapper div
- ghost button icons use text-kumo-subtle
- all button demos use icon prop, sizes demo uses shape=square
@pedromenezes1
Copy link
Copy Markdown
Collaborator Author

/bonk

@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk bot commented Apr 9, 2026

@pedromenezes1 Bonk workflow was cancelled.

View workflow run · To retry, trigger Bonk again.

@pedromenezes1
Copy link
Copy Markdown
Collaborator Author

/bonk

@pedromenezes1
Copy link
Copy Markdown
Collaborator Author

/bonk

…styling

- Remove focusMode as a public prop; auto-detect from children analysis
- "individual" mode when InputGroup.Button (non-ghost) is a direct child
- "hybrid" mode when both Addon and non-ghost Button coexist
- Fix double borders with border-l-0 on non-first children
- Fix focus ring stacking with outline-offset: -1px
- Fix stale ring-kumo-ring token → ring-kumo-hairline
- Add Usage section to InputGroup docs
- Diversify States demo examples
@pedromenezes1
Copy link
Copy Markdown
Collaborator Author

/bonk please don't fail a 4th time 🤝

Copy link
Copy Markdown
Contributor

@ask-bonk ask-bonk bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

This is a well-designed InputGroup compound component with excellent architecture and comprehensive test coverage. However, there are a few issues that need to be addressed before merging:

Issues to Fix

  1. Changeset accuracy - Per @pedromenezes1's earlier review, the changeset mentions deprecated InputGroup.Label and InputGroup.Description aliases. These are implemented in the code (lines 323-335 of input-group.tsx) but please confirm the changeset accurately reflects the final shipped API.

  2. Nested <label> elements - As @stritt noted, when the label prop is passed, Field renders a <label> with htmlFor, and the code also renders an invisible <label> overlay (line 281-287). This creates nested labels which is invalid HTML. The invisible label should be changed to a <span> with role="button" or the click-to-focus behavior should be handled differently.

  3. CSS dead code - @stritt noted there's outline: auto before outline: solid 2px -webkit-focus-ring-color in kumo-binding.css. Looking at the current code, I don't see this issue, so it may have been fixed already - please confirm.

Minor Suggestions (non-blocking)

  • The focusMode prop is documented in KUMO_INPUT_GROUP_VARIANTS but is auto-detected, not user-configurable. Consider if this should be marked as internal in documentation.

Strengths

  • Clean compound component architecture with proper context handling
  • Comprehensive test coverage (1300+ lines)
  • Smart hybrid focus mode for mixed addon+button layouts
  • Good accessibility handling with aria-invalid from context
  • Well-documented with extensive demos

Overall excellent work - just need to address the nested label issue.

@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk bot commented Apr 10, 2026

Review Complete

I've submitted a comprehensive review for PR #249. Here's a summary:

Issues Identified:

  1. Nested <label> Elements - When the label prop is passed to InputGroup, the Field component already renders a <label> with htmlFor. However, the code also renders an invisible <label> overlay for click-to-focus behavior (lines 281-287 in input-group.tsx). This creates nested <label> elements, which is invalid HTML with undefined assistive technology behavior. The invisible element should be changed to a <span> with click handling instead.

  2. Changeset Accuracy - The changeset mentions deprecated InputGroup.Label and InputGroup.Description aliases. While these are implemented in the code, @pedromenezes1 noted in an earlier review that the changeset should reflect the final API state.

Strengths Highlighted:

  • Clean compound component architecture with proper context handling
  • Comprehensive test coverage (1300+ lines of tests)
  • Smart hybrid focus mode for mixed addon+button layouts
  • Good accessibility handling with aria-invalid flowing through context
  • Well-documented with extensive demos

The PR is in excellent shape overall - the main blocker is fixing the nested label issue.

github run

@pedromenezes1
Copy link
Copy Markdown
Collaborator Author

/bonk these issues you have identified in the last review were all fixed already. can you check it all again?

Copy link
Copy Markdown
Contributor

@ask-bonk ask-bonk bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a well-designed InputGroup compound component with excellent architecture and comprehensive test coverage. The implementation shows thoughtful attention to accessibility, styling consistency, and developer experience.

Summary of Findings

✅ Already Resolved

The previous review identified several issues, and most have been addressed:

  1. Empty dist.md file — Confirmed removed (no longer exists in the working tree)
  2. data-focus-scope scaffolding — Confirmed removed (no occurrences in the codebase)
  3. CSS dead code — The outline: auto issue noted by @stritt has been resolved (lines 233-246 of kumo-binding.css are clean)

🔧 Minor Suggestions

  1. Changeset accuracy — The changeset mentions deprecated InputGroup.Label and InputGroup.Description aliases. While these are implemented in the code (lines 323-335 of input-group.tsx), please confirm this matches the intended final API as noted in previous reviews.

  2. Nested <label> handling — The code has been updated to handle this correctly:

    • When label prop is provided: uses <div> container + invisible <label> overlay with aria-hidden="true"
    • When no label: uses native <label> for click-to-focus
    • The JSDoc (lines 59-68) clearly documents this pattern and warns against nested labels

🎉 Strengths

  • Clean compound component architecture with proper context handling via InputGroupContext
  • Comprehensive test coverage (1316 lines of tests covering rendering, positioning, sizing, states, and edge cases)
  • Smart hybrid focus mode for mixed addon+button layouts — the detectFocusMode() logic elegantly handles container/individual/hybrid modes
  • Excellent accessibility: aria-invalid flows through context, tooltip→aria-label derivation, dev warnings for misuse
  • Well-documented with extensive demos and thorough MDX documentation
  • CSS-first sizing system using static Tailwind classes to avoid JIT detection issues (well-commented in context.ts)
  • Proper dev warnings for ghost buttons outside addons and size prop misuse

The implementation is production-ready. The deprecation aliases are a nice touch for migration ergonomics.

@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk bot commented Apr 10, 2026

I reviewed PR #249 by examining the diff and key files (input-group.tsx, context.ts, changeset, and tests), verified that all previously identified issues were resolved (empty dist.md removed, CSS dead code fixed, nested label handling corrected), and submitted a comprehensive GitHub review highlighting the component's excellent architecture, accessibility, and test coverage.

github run

pedromenezes1 and others added 4 commits April 13, 2026 15:20
# Conflicts:
#	packages/kumo/src/components/input/input-group.tsx
# Conflicts:
#	.opencode/package-lock.json
#	packages/kumo/src/components/pagination/pagination.tsx
@pedromenezes1 pedromenezes1 self-assigned this Apr 17, 2026
@pedromenezes1 pedromenezes1 requested review from Brayden and stritt April 17, 2026 18:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants