diff --git a/.agents/references/README.md b/.agents/references/README.md
index 790336f6..c2fc3f13 100644
--- a/.agents/references/README.md
+++ b/.agents/references/README.md
@@ -10,7 +10,7 @@ Use these references from both implementation-time and review-time skills. Treat
- [react-use-effect.md](react-use-effect.md) — state ownership, `useEffect`, async lifecycle, subscriptions, memoization
- [accessibility.md](accessibility.md) — interactive semantics, keyboard support, focus, labels, announced state
- [low-level-apis.md](low-level-apis.md) — timers, measurement, observers, imperative DOM, browser coordination
-- [composite-field-controls.md](composite-field-controls.md) — field semantics, composite inputs, `Field` / `Form` bridging, proxy or hidden inputs, validation wiring, and alignment with nearby Base UI-backed control contracts
+- [composite-field-controls.md](composite-field-controls.md) — field semantics, composite inputs, `Field` / `Form` bridging, proxy or hidden inputs, validation wiring, the `Field.Control` self-wired list, and alignment with nearby Base UI-backed control contracts
## Inclusion Criteria
diff --git a/.agents/references/composite-field-controls.md b/.agents/references/composite-field-controls.md
index e39f230d..ee813024 100644
--- a/.agents/references/composite-field-controls.md
+++ b/.agents/references/composite-field-controls.md
@@ -37,6 +37,10 @@ Review for consistency around:
Be skeptical when a composite field control invents a parallel API unless the underlying semantics truly differ.
+### Register as self-wired
+
+Every control that draws its own frame and connects to `Field.Root` by itself — composite or a thin Base UI wrapper — gets an entry in `selfWiredControls` in `components/field/field.tsx`, including compound members such as `.Async`. `Field.Control` uses that list to warn when the control is passed to its `render` prop, which draws a double frame. A control missing from the list fails silently: no warning, the same double frame.
+
### One semantic value across all input paths
Check whether these paths still converge on the same meaning when they exist:
diff --git a/.changeset/field-control-render-warning.md b/.changeset/field-control-render-warning.md
new file mode 100644
index 00000000..e9104146
--- /dev/null
+++ b/.changeset/field-control-render-warning.md
@@ -0,0 +1,5 @@
+---
+"@tailor-platform/app-shell": patch
+---
+
+`Field.Control` now logs a console warning when it is given an AppShell control that draws its own frame through `render`, such as `} />`. That composition renders a double border. The warning logs in every build, not only in development, because it only fires for UI that is already broken. `Select`, `Combobox`, `Autocomplete`, `Checkbox`, `Textarea`, `DateField`, `DatePicker`, and `DateRangePicker` go directly under `Field.Root` instead. The Form, Select, and form-modal docs now say which controls go where.
diff --git a/docs-manifest.json b/docs-manifest.json
index 0589330d..387f1956 100644
--- a/docs-manifest.json
+++ b/docs-manifest.json
@@ -699,9 +699,9 @@
"symbols": ["Form", "FormProps"],
"hashes": {
"typeSurface": "7b374d45d53d4535",
- "outline": "79c9b0f08ca38027",
+ "outline": "7fefd291a00de910",
"snapshot": "5d7a7904c5256f29",
- "outputMd": "91d901b17aed7781",
+ "outputMd": "e4a66f7b1ee4aa98",
"examples": null
}
},
@@ -733,9 +733,9 @@
"symbols": [],
"hashes": {
"typeSurface": null,
- "outline": "3301b263f6802090",
+ "outline": "a39589955fe582c8",
"snapshot": null,
- "outputMd": "9d068e568c9f3bdc",
+ "outputMd": "be00d1109761b7f1",
"examples": "7836309f2df327ee"
}
},
@@ -1209,9 +1209,9 @@
"symbols": ["Select", "SelectAsyncFetcher"],
"hashes": {
"typeSurface": "429710973dfa912b",
- "outline": "721e8995f53f0e8f",
+ "outline": "a8b7834a8817e0d5",
"snapshot": "2ef5b7324018ff8f",
- "outputMd": "ae287e2bb5abc1c6",
+ "outputMd": "b3dd3b9a58a9d290",
"examples": "1ce89cec30045466"
}
},
@@ -1759,14 +1759,14 @@
"packages/core/skills/app-shell-patterns/references/components/dialog.md": "ec6a0ac098d1d87a",
"packages/core/skills/app-shell-patterns/references/components/document-progress-card.md": "d0fb940f685f8869",
"packages/core/skills/app-shell-patterns/references/components/filter-rail.md": "c6a48c4d8e8b0078",
- "packages/core/skills/app-shell-patterns/references/components/form.md": "91d901b17aed7781",
+ "packages/core/skills/app-shell-patterns/references/components/form.md": "e4a66f7b1ee4aa98",
"packages/core/skills/app-shell-patterns/references/components/global-header-layout.md": "dd2b0da3702cf8d9",
"packages/core/skills/app-shell-patterns/references/components/grid.md": "9051eb53dde4179f",
"packages/core/skills/app-shell-patterns/references/components/input.md": "a3a6c9679af99d37",
"packages/core/skills/app-shell-patterns/references/components/layout.md": "66696139a46fabb0",
"packages/core/skills/app-shell-patterns/references/components/menu.md": "ceb4a05822538d20",
"packages/core/skills/app-shell-patterns/references/components/metric-card.md": "5a1c557768c6590f",
- "packages/core/skills/app-shell-patterns/references/components/select.md": "ae287e2bb5abc1c6",
+ "packages/core/skills/app-shell-patterns/references/components/select.md": "b3dd3b9a58a9d290",
"packages/core/skills/app-shell-patterns/references/components/sheet.md": "1f81ebd562c50371",
"packages/core/skills/app-shell-patterns/references/components/sidebar-group.md": "a36192df64f16adc",
"packages/core/skills/app-shell-patterns/references/components/sidebar-item.md": "9236c03e53aa79f2",
@@ -1807,7 +1807,7 @@
"packages/core/skills/app-shell-patterns/references/api/use-time-zone.md": "30c15545ab3c4954",
"packages/core/skills/app-shell-patterns/references/api/use-toast.md": "30983a761bc61593",
"packages/core/skills/app-shell-patterns/references/patterns/form-composer.md": "ed187ac3a0773d3d",
- "packages/core/skills/app-shell-patterns/references/patterns/form-modal.md": "9d068e568c9f3bdc",
+ "packages/core/skills/app-shell-patterns/references/patterns/form-modal.md": "be00d1109761b7f1",
"packages/core/skills/app-shell-patterns/references/patterns/form-sectioned.md": "d31cd3ae6ad0790c",
"packages/core/skills/app-shell-patterns/references/patterns/form-single-page.md": "10a0f4d91a120752",
"packages/core/skills/app-shell-patterns/references/patterns/form-wizard.md": "851afb4672da6673",
diff --git a/docs-src/components/form.docs.outline.md b/docs-src/components/form.docs.outline.md
index 08958cbd..7e6f81d8 100644
--- a/docs-src/components/form.docs.outline.md
+++ b/docs-src/components/form.docs.outline.md
@@ -102,14 +102,14 @@ A compound component that groups all parts of a form field and manages its valid
### Sub-components
-| Sub-component | Description |
-| ------------------- | --------------------------------------------------------------------------------------------- |
-| `Field.Root` | Context boundary for a single field. Manages validation state. |
-| `Field.Label` | Accessible label; `htmlFor` is resolved automatically via context. |
-| `Field.Control` | Styled ``. Can be omitted when using a Base UI-backed AppShell component as the input. |
-| `Field.Description` | Supplementary help text; automatically linked via `aria-describedby`. |
-| `Field.Error` | Validation error message. Use `match` to target specific validity states. |
-| `Field.Validity` | Render-prop access to the field's `ValidityState` for fully custom validation UI. |
+| Sub-component | Description |
+| ------------------- | ---------------------------------------------------------------------------------------------------------------------- |
+| `Field.Root` | Context boundary for a single field. Manages validation state. |
+| `Field.Label` | Accessible label; `htmlFor` is resolved automatically via context. |
+| `Field.Control` | Styled text ``. `Select`, `Combobox`, and the other self-wired controls go directly under `Field.Root` instead. |
+| `Field.Description` | Supplementary help text; automatically linked via `aria-describedby`. |
+| `Field.Error` | Validation error message. Use `match` to target specific validity states. |
+| `Field.Validity` | Render-prop access to the field's `ValidityState` for fully custom validation UI. |
### Field.Root Props
@@ -136,7 +136,17 @@ A compound component that groups all parts of a form field and manages its valid
### Using Another AppShell Component as the Control
-`Field.Control` can be omitted when using a Base UI-backed AppShell component (e.g. `Select`, `Combobox`). The component registers itself with the `Field` context automatically, inheriting label association and validation state.
+`Field.Control` is a text input. It is the control for plain text-like fields, and you can pass it a native element such as `render={}`. The controls in the last row of the table below go **directly under `Field.Root`**, as a sibling of `Field.Label`. Each one registers itself with the `Field` context, so it gets label association, `aria-describedby`, `disabled`, and validation state without `Field.Control`.
+
+| Control | Where it goes |
+| ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- |
+| Plain text, email, number, password, … input | `` |
+| Native element (`