Transferred from objectstack-ai/objectstack#11243 (triage 2026-08-23, file-at-destination: this is generic record chrome in the console/record page — the body stands with objectstack removed). Original author: os-zhuang, filed 2026-08-23. Original body verbatim below.
The record page's lifecycle path looks like a control and is inert: it renders stageField as a segmented button group with no handler, no cursor, and no tab stop
Measured on 17.1.0 (open edition, objectstack dev), HotCRM crm_quote — but it is the generic record chrome, so it applies to every object that declares a stageField.
What renders
The record page draws the object's lifecycle across the top:
[ 草稿 ] [ 审核中 ] [ 已提交 ] [ 已接受 ] [ 已拒绝 ] [ 已过期 ]
▲ current, filled
Pill-shaped segments, the current one filled in the accent colour, the rest in a resting state — visually a segmented button group, and positioned exactly where a CRM user expects the stage control to be.
What it is
document.querySelectorAll('main [role="listitem"]') // ← listitem, not button/tab
getComputedStyle(el).cursor // "auto"
el.getAttribute('tabindex') // null
el.closest('button,[role=button],[role=tab],a') // null
A full pointer sequence (pointerdown → mousedown → pointerup → mouseup → click) on the target segment changes nothing; the record's status is still draft afterwards. Advancing the record needs 编辑 → the status field → 更新, or a bulk action.
Why it is worth changing
The path is the affordance every CRM user reaches for first — in the products this chrome is modelled on, clicking the next segment is how you advance a record. Rendering it as buttons and wiring nothing is the shape that costs a user two or three clicks before they conclude it is decoration, and it teaches them to distrust the rest of the chrome.
Two directions, either defensible:
- Make it a control. Clicking a segment writes
stageField, subject to the same permission/validation path an edit takes (so a locked or unauthorised record refuses with the normal envelope). This is what the visual promises.
- Make it look like a readout. If advancing must stay in the form, drop the button styling — a progress rail without pill-and-fill reads as status, not as a control, and nobody loses a click to it.
What it should not keep doing is looking like (1) and behaving like (2).
Accessibility note
The segments carry role="listitem" with no tabindex, so a keyboard user cannot reach them at all — which is correct for a readout and wrong for a control. Whichever direction is taken, the role should follow: listitem for a readout, tab/button with a tab stop for a control.
Transferred from objectstack-ai/objectstack#11243 (triage 2026-08-23, file-at-destination: this is generic record chrome in the console/record page — the body stands with objectstack removed). Original author: os-zhuang, filed 2026-08-23. Original body verbatim below.
The record page's lifecycle path looks like a control and is inert: it renders
stageFieldas a segmented button group with no handler, no cursor, and no tab stopMeasured on
17.1.0(open edition,objectstack dev), HotCRMcrm_quote— but it is the generic record chrome, so it applies to every object that declares astageField.What renders
The record page draws the object's lifecycle across the top:
Pill-shaped segments, the current one filled in the accent colour, the rest in a resting state — visually a segmented button group, and positioned exactly where a CRM user expects the stage control to be.
What it is
A full pointer sequence (pointerdown → mousedown → pointerup → mouseup → click) on the target segment changes nothing; the record's status is still
draftafterwards. Advancing the record needs 编辑 → the status field → 更新, or a bulk action.Why it is worth changing
The path is the affordance every CRM user reaches for first — in the products this chrome is modelled on, clicking the next segment is how you advance a record. Rendering it as buttons and wiring nothing is the shape that costs a user two or three clicks before they conclude it is decoration, and it teaches them to distrust the rest of the chrome.
Two directions, either defensible:
stageField, subject to the same permission/validation path an edit takes (so a locked or unauthorised record refuses with the normal envelope). This is what the visual promises.What it should not keep doing is looking like (1) and behaving like (2).
Accessibility note
The segments carry
role="listitem"with notabindex, so a keyboard user cannot reach them at all — which is correct for a readout and wrong for a control. Whichever direction is taken, the role should follow:listitemfor a readout,tab/buttonwith a tab stop for a control.