Appearance
Biodata editor — drawer and field-control prompt (round 3)
Purpose: close the gaps the design does not currently answer, before any of it is implemented.
This page exists because a systematic audit of the Marriage Biodata editor on 2026-09-07 sorted every finding into two buckets, and only one of them is ours to fix in code:
| Bucket | Meaning | Who resolves it |
|---|---|---|
| Fidelity | the design specifies it and the implementation diverged | us, in code, no design round |
| Design gap | the design does not answer it, or the owner is asking for something the design deliberately refused | Claude Design, first |
The owner's instruction, verbatim: "if you find gaps in the design itself then we should pass a detailed prompt to claude design and get it fixed first then we should implement to avoid inconsistency and wrong." This is that prompt.
The failure this page is written to avoid
Round 2 of the biodata brief gave excellent FEATURE context and named prototype/consumer/ zero times. It asked for "the five screens, in your order" and got five good screens in a parallel mini-app — prototype/my-qrsetu/ with its own ds-base.js, icons.js and a biodata-core.js importing nothing, with no navigation back to ConsumerHome.dc.html (QRS-913).
Round 1 had the frame right and round 2 threw it away by replacing it with a screen list. So this round restates the product frame rather than assuming it carried forward. Everything in Section 0 is non-negotiable context, not preamble.
Read alongside: Consumer context prompt (round 1, the product frame) · Consumer decisions · PDPR master brief · Screen Coverage Mandate.
What this round must NOT reopen
Stated first, because a prompt that only lists problems invites a redesign of things that are already correct. Each of these is a reasoned decision in the design's own words and stays:
- Religion and caste are free text, never a picker. "A picker is a controlled vocabulary, a controlled vocabulary is a filterable axis, and a filterable axis is one step from a searchable directory of people by caste." No dropdown, autocomplete, suggestion, normalisation, count, filter or sort on either field, now or later.
- No complexion field. Refused twice on the evidence.
- Income stays
privateand unreleasable. It may get a better input; it does not get published. - The exact date of birth stays private, which is why the kundli cluster sits at released tier.
- Content is never translated. The UI is trilingual; what the family typed is stored and shown exactly as entered, in both directions.
- No religion tab strip, no "pick your faith first" step, and nothing is ever keyed off a tradition tag.
- A choice field's stored value must still round-trip a foreign string untouched, because
isShippedOption()andformatValue()depend on it. Whatever is decided in G1 below, do not narrow the stored type to a closed enum.
The prompt
Paste everything between the rules into Claude Design as a single message.
text
QR SETU: MARRIAGE BIODATA EDITOR - DRAWER AND FIELD CONTROL PASS
ROUND 3. YOU ARE EXTENDING AN APP, NOT BUILDING ONE.
WHAT YOU ARE WORKING ON
You are extending the consumer app that already exists in this project, at
prototype/consumer/. Specifically you are revising the bottom sheets inside
prototype/consumer/Biodata.dc.html, which is the Marriage Biodata editor. That artboard
already defines twelve sheets, a field registry of sixty fields and a full tier model.
None of that is being replaced.
The files you must read before designing anything, and must import rather than
re-create:
prototype/consumer/Biodata.dc.html the editor you are revising, all twelve sheets
prototype/consumer/biodata-core.js FIELDS, INPUTS, INPUT_LEADS, TIERS, RELATIONS,
PLACES, VALUE_MAPS, ENUM_FIELDS, isShippedOption,
formatValue, validatePerson, AGE_FLOOR/CEILING
prototype/consumer/consumer-data.js the consumer app's navigation and kinds
prototype/consumer/ds-base.js the design system base - do not write a second one
prototype/consumer/icons.js the icon set - do not add a parallel set
prototype/consumer/qr-toast.js QRToast, the only toast
prototype/consumer/confirm-delete.js QRConfirmDelete, the only destructive confirmation
prototype/setu-card/BiodataPage.dc.html the public page these fields render on
Do NOT create a new folder. Do NOT create a second ds-base.js, icons.js or support.js.
Do NOT introduce a new navigation pattern. Every sheet you touch stays inside
Biodata.dc.html and keeps its existing entry point.
WHY THIS ROUND EXISTS
The product owner tested the implemented editor on an Android device and reported that
the drawers feel, in their words: "scattered and inconsistent", "static and dated rather
than like a modern, energetic, thoughtfully designed application for a new generation of
users", and "fitted into the available space rather than deliberately designed around the
interaction, field type, hierarchy and user experience."
Some of that is our implementation diverging from your design, and we are fixing that
separately. The items below are the ones your artboard does not currently answer.
THE QUALITY BAR THE OWNER ASKED FOR, IN THEIR WORDS
Modern - Clean - Energetic - Intuitive - Purpose-built - Consistent - Mobile-first
with particular attention to: content hierarchy, section grouping, labels and helper text,
appropriate icons and visual cues, option presentation, selection states, spacing and
alignment, scroll behaviour, keyboard handling, bottom action placement, visual feedback,
empty/error/validation states, accessibility and touch targets.
--------------------------------------------------------------------------------
G1. THE ONE THAT NEEDS A PRODUCT DECISION, NOT JUST A LAYOUT: CUSTOM / OTHER
--------------------------------------------------------------------------------
Your design draws a `choice` field as chips only. No text input, no Other chip, no Save
button - a tap commits and closes. You state the rule in prose above the marital field:
"It is a fixed list and never free text, because this is a fact with five possible values
and a typed sentence here reads as an excuse." Your INPUT_LEADS say "Pick one" for choice
and "Pick one or write your own" for suggest. That is a deliberate two-way distinction and
we have implemented it as you specified.
The owner is asking for the opposite, and their reasoning is concrete:
"Many fields are restricted to a small set of predefined options without providing a
Custom/Other option where users may reasonably need to enter their own value. Income
is a clear example: users may have an actual amount or an income range that doesn't
fit the predefined choices."
Nineteen fields are currently `choice`: gender, marital, height (21 options), bloodGroup,
experience, familyType, nakshatra (28), charan, gan, nadi, manglik, expectAge,
expectHeight (22), expectEducation, expectWork, expectDiet, expectIncome, diet,
repRelation.
WHAT WE NEED FROM YOU: decide, per field or by rule, where a family may write their own
answer, and draw it. This is a judgement about each field's nature, not a global switch.
Some are genuinely closed (gender, manglik). Some plainly are not (experience, height for
someone outside the range, expectIncome). Consider a third input kind between choice and
suggest if that is the honest answer.
Two constraints on whatever you decide:
- religion and caste stay free text with NO options, per your own hard rule. Do not
let a general "add Other everywhere" pass touch them.
- the stored value must keep round-tripping a foreign string untouched, because
isShippedOption() and formatValue() already depend on it.
--------------------------------------------------------------------------------
G2. THE KEYBOARD, THE SYSTEM NAVIGATION BAR AND THE SAFE-AREA INSET
--------------------------------------------------------------------------------
Your artboard specifies none of this, and it cannot: it renders in a desktop preview,
where there is no soft keyboard, no Android navigation bar and no safe-area inset. We
grepped it - zero occurrences of safe-area, env(, scrollIntoView, visualViewport,
autoFocus or enterkeyhint, and one inputmode in the whole file.
Meanwhile FIVE of your twelve sheets have keyboard input above a sticky footer: the field
sheet (suggest, text, long), the custom-field sheet, the people sheet (two inputs per
row), the share sheet and the opening composer.
The owner found both halves of this on a device and made it a standing rule:
"Whenever a bottom drawer/sheet contains keyboard-based input, it must properly respond
to the mobile keyboard and remain usable/visible. The keyboard must never obscure the
active input, entered content, validation messages, or required actions."
plus: bottom actions must clear the Android system navigation.
WHAT WE NEED FROM YOU, as design decisions rather than engineering ones:
- When the keyboard opens on a sheet, what moves? Does the sheet lift, does the body
scroll to the focused input, or both? What happens to the sticky footer?
- Does the focused field scroll into view, and with how much room above the keyboard?
- Where does a validation message sit when the keyboard is covering the field's usual
place? (See G8 - the field sheet currently has no inline error slot at all.)
- What is the sheet's maximum height WITH a keyboard open, given yours are 86-96%
without one?
- Which fields want which keyboard? phoneNo and repPhone are currently getting the
alphabetic keyboard; linkedin is getting autocapitalisation.
- How much clearance sits below the last action, given the bar is ~24dp on a gesture
device and ~48dp on a three-button one and 0 in some landscape modes? Express it as
a rule over the inset, never a constant - a constant is wrong on most devices and
wrong silently, because it looks right on whichever device the author was holding.
--------------------------------------------------------------------------------
G3. `dense` IS DECLARED AND READ BY NOTHING
--------------------------------------------------------------------------------
biodata-core.js sets dense: true on four fields - height (21 options), bloodGroup (9),
nakshatra (28) and expectHeight (22) - and fieldInput() returns it. The artboard never
reads it: grep for "dense" in Biodata.dc.html returns zero.
So a 28-chip nakshatra grid renders at exactly the same chip size as a 2-chip gender
field. On a 360dp phone that is roughly fourteen rows of pills, and the family must
scroll past all of them to reach Save.
WHAT WE NEED FROM YOU: what a dense option list looks like. A smaller chip? A different
arrangement - two or three columns, a sectioned list, an index? A search field above it
once the list passes some length? Nakshatra in particular is a list a family scans for one
known value rather than browses, which may argue for a different control entirely.
--------------------------------------------------------------------------------
G4. THE LOCATION AND MAP FIELDS
--------------------------------------------------------------------------------
Three fields declare optionsFrom: 'areas' - city (required, basic tier), mapPlace and
placeOfBirth - and your PLACES table plus placeSuggestion() exist to serve them. Our
implementation never passed the areas argument, so all three currently render as a bare
text box with no suggestions. That part is our defect and we are fixing it.
The owner is asking for more than suggestions:
"A field asking for a Map is presented as a plain text input instead of an appropriate
map/location interaction."
Your round 6 position is explicit and we are not overriding it: mapPlace is "an area and a
landmark", mapUrl() builds the Google Maps link, and the public page shows a STILL map
with an Open in Google Maps action because "a live embed on a page that is deliberately
not indexed would be the one thing on it phoning out."
WHAT WE NEED FROM YOU: does the EDITOR get a richer location control than a text field
with suggestions, and if so what - given that the privacy argument against a live embed on
the public page does not automatically apply to a private editor, but a map picker implies
storing coordinates, which is a new kind of data about where a family lives. If the answer
is "no, suggestions are enough", say so and we will close it as answered.
--------------------------------------------------------------------------------
G5. INCOME
--------------------------------------------------------------------------------
income is currently suggest with six options, private tier, mono face. The owner wants it
to "support realistic user input and appropriate predefined ranges, with a Custom/Other
path where required". expectIncome, the one a family states as an expectation, is choice
with six options and no free-text path at all.
Your position that income stays private and unreleasable is unchanged and not in question
here. What we need is the INPUT: ranges, an exact amount, a unit, or a combination - and
the same question for expectIncome, which is the one a reader actually sees.
--------------------------------------------------------------------------------
G6. SMALLER THINGS YOUR ARTBOARD LEAVES OPEN
--------------------------------------------------------------------------------
Each of these is a real ambiguity we would otherwise have to invent an answer for:
a) AGE BOUNDARY. dobRange() is year-granular, so with AGE_FLOOR 18 every day in
maxYear passes dateAllowed(). Someone born 31 December of that year is 17. Is that
intended, or should the boundary be a real date?
b) THE `yesno` BRANCH. The field sheet implements it and no field in INPUTS declares it.
manglik shipped as a choice with "Not sure" in the list instead. Wire a field to it
or drop it.
c) THE dob HINT NEVER RENDERS. fieldHint is forced to '' for a date, so "Used to work
out the age that is shown. The date itself never leaves QR setu." appears on the row
and never in the sheet - which is the one place a family is deciding whether to give
us their date of birth.
d) NO INLINE ERROR SLOT IN THE FIELD SHEET. All its validation is toast-only ("Pick a
full date", "Too long by N"), while the custom sheet has a draftError panel and the
people sheet has a per-row error line. Three sheets in one feature, three different
answers to the same question.
e) CLEAR'S HEIGHT is a hardcoded 50px while Cancel and Save use fieldActH, which is
44px on a date field. Three buttons in one row, one of them 6px taller.
f) PERSON REMOVAL bypasses QRConfirmDelete and removes on a single tap with a toast,
though confirm-delete.js is loaded and used elsewhere in the same artboard.
g) THE PEOPLE SHEET'S DONE IS UNCONDITIONAL. Invalid rows are permitted and then
silently dropped by peopleOf() downstream, so a family can leave the sheet believing
they added someone who is not there.
h) TWO SHEETS MISS `.qrc-scroll` - peopleSheet and openSheet use raw overflow-y:auto, so
they would show a scrollbar where the other ten do not.
--------------------------------------------------------------------------------
WHAT TO PRODUCE
--------------------------------------------------------------------------------
Revise Biodata.dc.html in place. Keep every existing sheet's entry point, its state props
and its data source. Add states rather than replacing them where a new state is needed, so
the existing scenario dropdown still exercises everything it does today.
For each of G1 to G6, state your decision in the artboard's own comment style - the way the
marital and religion rules are already written - so the reasoning survives next to the
code rather than in this message. Where you decline something, say so and why; a refusal
with a reason is an answer and we will implement it as one.After the round returns
- Re-pull, never read a cache. Fetch
Biodata.dc.htmlandbiodata-core.jsfresh through DesignSync; the local mirror is a cache and is stale by default. - Re-transcribe the field registry into
@qrsetu/domain/biodataifINPUTSchanged, and updatepackages/schemaswith it. - Update the parity contract
design-system/parity-contracts/consumer-biodata.json— four of its scenarios currently assert the field sheet does not exist, which contradicts its ownfield_sheet_savesrow. - Then implement, section by section, against the returned design.