Repository navigation
Conversation
A component that needs its own node and also forwards the caller's ref feeds both through one callback ref. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FieldFrame gives every text control the same border, radius, padding and focus ring, with hover, invalid, disabled and read-only states as data attributes. On it: Input (32 tall, leading/trailing icons and prefix/suffix text), Textarea, PromptTextarea (104 minimum, grows to a maximum then scrolls; Enter submits, Shift+Enter breaks the line, an IME composition never submits), SearchField (Escape clears a value and stops; on an empty field it bubbles) and SecretField (masked, Reveal with aria-pressed, Copy writes the real value and reports the outcome to the caller for the toast). Field wires Label, HelperText and ErrorText to the control: the label names it, aria-describedby lists the rendered texts, and an error sets aria-invalid and renders as role=alert. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A caller that passes id now gets stable describedby ids, and the markup snapshot no longer depends on useId render order. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…up frames Fill is the background token (216:8126), inputs pad 10 with a 6 gap (676:9487), the prompt pads 10 vertically (676:9547), and a label sits 6 above its control (639:5296). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The composed callback now returns a cleanup, so React calls it instead of the callback with null: a ref that returned its own cleanup gets it run, and every other ref is set to null. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SearchField: a Radix dialog that skips dismissal for data-escape-clears calls preventDefault in the capture phase, so the clear now ignores a preventDefault made before it; only the caller's own onKeyDown cancels it. The lock reads disabled and read-only from props and Field alike, and a controlled field with no onValueChange lets Escape through. PromptTextarea: Enter does not submit when read-only. Where field-sizing is missing, it re-fits after its form resets and keeps the height automatic while scrollHeight is 0. SecretField: onCopyResult is required and is called once, after the clipboard write, so a throwing handler is not reported as a failed copy. An empty value is not written and reports status empty. Reveal and Copy follow a disabled Field and stay usable when read-only. new-password, the password-manager ignore hints and spellcheck off are set after the props spread. Tests cover each fix, a real Radix Dialog, and the wiring of all five controls to Field. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Object and callback refs reach the control, and are released on unmount, for the three controls that compose refs. FieldAction inside a form clicks without submitting it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
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.



What
frontend/src/v1/components/ui/form/, all drawn by oneFieldFrame(border, radius, padding, focus ring and the hover, invalid, disabled and read-only states):Input: height 32, with leading and trailing icons and prefix and suffix text.Textarea, andPromptTextarea: at least 104 tall, grows with its text to a maximum, then scrolls. Enter submits, Shift+Enter breaks the line, and an IME composition never submits.SearchField: Escape clears it when it has a value; on an empty field Escape reaches the dialog or popover around it. A Clear button does the same for the mouse.SecretField: masked by default; Reveal (aria-pressed) and Copy, which copies the real value and reports the result throughonCopyResult.Field,Label,HelperText,ErrorText:Fieldwires the label,aria-describedbyandaria-invalidfor whichever control it holds;ErrorTextis announced (role="alert").lib/compose-refs.ts, which merges a component's own ref with the caller's, including React 19 ref cleanups.Why
How
ui:classes only, no raw colours or lengths. Styling follows the kit's Input Group frames (216:8125) and the search and prompt spec frames (676:9483, 676:9513, 676:9543).SearchFieldinside a Radix dialog: Radix callspreventDefaulton Escape in the capture phase when the dialog chooses not to close. The field ignores that, so a dialog that skips dismissal for targets carryingdata-escape-clearsgets "Escape clears the field, then closes the dialog", which a test with a real Radix Dialog checks. Only apreventDefaultfrom the caller's ownonKeyDowncancels the clear.SecretFieldsetsautoComplete="new-password",data-1p-ignore,data-lpignoreand turns off spellcheck, autocapitalise and autocorrect, so password managers and browser spellcheck services leave the value alone; callers cannot override these. Copy on an empty field writes nothing and reports{status: "empty"}.onCopyResultis required: no toast component exists yet (P1-T13 builds it), so the caller raises the toast. This is the seam to swap when it lands.field-sizing: contentis unsupported (Firefox),PromptTextareasizes itself fromscrollHeightthrough the--prompt-heightcustom property, and re-fits after a form reset.Can this PR break any existing features. If yes, please list possible items. If no, please explain why. (PS: Admins do not merge the PR without this section filled)
frontend/src/v1/, which no v0 route renders. No v0 file, dependency or build file changes.Database Migrations
Env Config
Relevant Docs
83e4fff.Related Issues or PRs
mainand rebase onto it from UN-4299 [FEAT] v1 design tokens, themes and style isolation (P0-T04) #2399's last tip.test:v1).Dependencies Versions
Notes on Testing
lint:v1,typecheck:v1andbiome check src/v1clean.test:v1: 118 vitest tests (83 on the base) and 59 boundary fixtures. The same checks pass on a merge with the newest scaffold commits (65 fixtures).keyCode 229, Escape clear and bubbling, the Radix dialog case, read-only and disabled locks from props and fromField, copy result handling, empty copy, privacy attributes, ref cleanups, the fallback re-fit): removing each one fails a test.Design questions (no frame answers them)
dark:input/30, which no token carries. This PR uses--backgroundin every theme.--destructiveat 50% (one focus treatment) or the kit's raw red at 20%? Disabled fill#e5e5e540has no token.Screenshots
Checklist
I have read and understood the Contribution Guidelines.
🤖 Generated with Claude Code