PR: #615: feat(import): instruction copy in a fixed slot below the dropzone [Story-2400] by @ankitmaheshwari-com
View PR →

Story-2400 · pramaan-app · 14 Jul 2026

The instruction never dims, and never moves

Story-2393 gave the import drawer its teaching copy, but put it inside the dashed dropzone. Two costs followed. The copy inherited the zone’s disabled dimming, so the words counsel most needs to read were the faintest words on the screen. And because the copy lived in the zone, it moved and re-flowed as counsel armed a source — the drawer visibly jumped on the one click every import begins with. This Story takes the copy out of the box. Same words, same keys; a fixed slot below the zone.

The one idea

Instructions are chrome, not content of the box they describe. A dropzone is a target; its state (armed / disabled / dragging) is expressed by colour and by a single invitation line. The explanation of what arming means belongs beside the target, at full ink, in a slot that holds its ground — so the only thing a click changes is the words.

What shipped

Piece What it does
The zone goes minimal Inside the dashed border: the cloud icon, one invitation line, the four buttons. Nothing else. Unarmed, the line is now Matters.upload.dropTitle (“Drag files here”) — an existing key, no new copy — because the question it used to ask has moved out of the box.
The instruction slot A left-aligned block directly below the zone, at text-ink-strong / text-ink-muted, never dimmed with the zone. Unarmed it carries unarmedTitle (15px/600, strong ink) + unarmedHint (14px/1.55, muted); armed to a side, armedHint plus a quieter driveFolderHint (13px/1.5) when the Drive door is configured; armed to Strategy, armedHintStrategy alone. Every key already existed — this Story relocates them, it does not rewrite them.
Geometry from the approved design The founder-approved Claude Design (states 1–3) sets the slot: 20px below the zone, 118px reserved, 8px between lines, left-aligned. States 4–5 (the privileged copy, the in-flight compact zone) are a separate follow-up and are deliberately NOT implemented here.
Zero jump, no magic number All three variants mount in the same CSS grid cell (grid *:col-start-1 *:row-start-1); the inactive ones are invisible — out of sight and out of the a11y tree, but still measured. The slot is therefore always as tall as its tallest variant, in any locale. Arming, disarming, or switching side changes only the words.

Why not a fixed height?

The obvious fix is min-h-[92px] on the block. It is also the fix that silently breaks: Hindi runs longer than English, a copy edit adds a line, and the pinned height is now either too short (the jump is back) or too tall (a dead gap). A pixel constant has to be maintained in lockstep with copy in two locales, and nothing fails loudly when it isn’t.

Stacking the variants makes the browser do the measuring. The design’s 118px is honoured as a floor (min-h-[118px]), and the stack guarantees the slot is never shorter than the tallest real variant, in any locale. There is no number to keep in sync with the copy.

The states

unarmed [ ☁ Drag files here ] zone: grey, disabled [ Browse · Folder · Drive · Dropbox ] ───────────────────────────────── Whose documents are these? ← slot, full ink Choose Client's, Opponent's or Other to start uploading. Strategy and privileged papers never enter the Record. armed: client [ ☁ Drop client documents ] zone: green [ Browse · Folder · Drive · Dropbox ] ───────────────────────────────── Everything added here is marked Client's. Any file's side can be corrected after it lands. Importing a folder? Open it in the Drive picker… (quiet) armed: strategy[ ☁ Drop strategy notes ] zone: blue [ Browse · Folder · Drive · Dropbox ] ───────────────────────────────── Everything added here is marked for Strategy, outside the Record. Importing a folder? Open it in the Drive picker… (quiet) ↑ this line sits at the SAME y in all three states

What did not change

Where it lives

File Change
src/components/matters/import-into-matter-sheet-v2.tsx New InstructionSlot component; the three paragraphs removed from inside the dropzone.
src/components/matters/import-into-matter-sheet-v2.stories.tsx New InstructionSlotHoldsItsGround story — click through all four droppable sources with both cloud doors on and watch the block stay put. Empty, ArmedClient, ArmedStrategy keep covering the three states.

How to see it

  1. pnpm storybook → Matters/Import/ImportIntoMatterSheetV2.
  2. Open InstructionSlotHoldsItsGround. The drawer is unarmed: the zone is grey, and the teaching copy below it is fully legible.
  3. Click Client’s, then Opponent’s, then Other, then Strategy, then back to nothing. The zone recolours and the words below it change — nothing above or below the block shifts by a pixel.
  4. Switch the Storybook locale to Hindi and repeat. The slot is taller (Hindi runs longer), and still perfectly still.

Files changed

2 file(s), +113 / -33 lines

Path Role + -
src/components/matters/import-into-matter-sheet-v2.stories.tsxComponent+19-6
src/components/matters/import-into-matter-sheet-v2.tsxComponent+94-27