Work-item description: a Markdown TABLE overflows the description card and paints across the right rail — `.motir-prose` has no `table` rules at all (no scroll container, no borders)
Type: Bug (code) · UI / layout — the horizontal-overflow bug class (a wide child with no containment), sibling of MOTIR-448, MOTIR-1307, MOTIR-1329, MOTIR-1483.
Parent: MOTIR-1464 — the open "Planner self-improvement · auto-reported quality bugs" epic, the de-facto home for agent-discovered quality bugs (Yue's call, 2026-08-03; filed at root first). NOT parented by where the defective code lives: that would be the Markdown render stack (MOTIR-45 · MarkdownView) and Story 5.8 (MOTIR-1399) under Epic 5 (MOTIR-263) — both done, so per the bug-parent rule the bug is not re-attached into a sealed epic. Nothing open is blocked by it.
Discovered in: out-of-band manual dogfooding (screenshot of the live MOTIR-1981 detail page, 2026-08-03). Reported symptom: "the content of the item overflows."
Symptom (observed)
On MOTIR-1981 the description contains a GFM table (symptom | measured | why). In the rendered read view:
- The table's width is set by its content, so it runs past the right edge of the description card, past the
1frmain column, under the right-rail cards (Priority / Assignee / Reporter / Parent / Labels / Components) and off the right edge of the viewport. The third column (why…) is only visible in the gutters between the rail cards — the rest is unreachable: no horizontal scrollbar, no clipping, no wrap. - The internal-link chip for MOTIR-1974 sits inside one of those table cells and overflows with it — its
max-w-full(the MOTIR-1483 fix) resolves against the already-unbounded table cell, so the fix is defeated. This is a consequence, not a second defect. - Secondary, same root cause: the table renders with no borders, no header rule, no row separators and no cell padding — it reads as loose floating text, not a table.
Root cause (VERIFIED against origin/main @ c4ec51b1)
lib/markdown/render.tsx:115runsremarkPlugins={[remarkGfm]}, so GFM tables do parse and emit real<table>/<thead>/<tr>/<td>intoMarkdownView(components/ui/MarkdownView.tsx:45,className="wmde-markdown motir-prose").components/ui/markdown-editor.css(the entire.motir-proseblock, 218 lines) has zerotable/thead/tr/th/tdrules.grep -nE 'table|th|td' components/ui/markdown-editor.cssreturns nothing but comment text. There is no@tailwindcss/typographyplugin and no global table rule inapp/globals.csseither, so nothing else supplies them.- The only element in
.motir-prosethat is taught to contain itself ispre(overflow-x: auto, line 63-67) andimg(max-width: 100%, line 73). A bare<table>does neither: it is not a scroll container, andmin-width: 0on the grid cell does not shrink it. app/(authed)/items/[key]/page.tsx:406-413already documents the intended contract —<main className="min-w-0 …">floors the1frtrack's min-content "so a wide child scrolls inside its own block (.motir-prose prehasoverflow-x:auto)".min-w-0on the track is necessary but not sufficient: it only works for children that scroll themselves. Tables were never given that block, so the escape lands exactly where MOTIR-448 landed.
This is a pure implementation gap — table support was enabled at the parser (remark-gfm) but never at the stylesheet. No design/plan defect, so no notes.html entry.
Reproduce
- Put a Markdown table with 3+ columns and long cell text (a fenced-code span or a
MOTIR-Nreference in a cell makes it worse) in a work item's description. MOTIR-1981's live description is a ready-made repro. - Open that item's detail page (
/items/<key>) at a normal desktop width. - Expected: the table stays inside the Description card and scrolls horizontally inside its own block if it is wider than the column; it renders with visible header/row rules.
- Actual: the table paints across and behind the right rail and off the viewport; the overflowing columns are unreachable; there are no rules or padding.
Fix direction (for the close-out subtask)
- Give the table its own scroll block, so
min-w-0on the<main>track can do its job — either wrap<table>in anoverflow-x: autoelement via acomponents: { table: … }override inlib/markdown/render.tsx(preferred: keeps realtablecolumn sizing), or.motir-prose table { display: block; overflow-x: auto; max-width: 100% }(simpler, butdisplay:blockcosts column auto-sizing). Whichever is chosen, the table must never exceed the description column width, and the overflow must be reachable (scrollable), not clipped. - Add the missing table typography in the same pass: header weight + bottom rule, row separators, cell padding — colours from
--el-border/--el-text*only, radii/spacing from the element-semantic tokens (no invented colours, no hardcoded border-style). - The same
.motir-prosestyles are shared with the WYSIWYG editor surface, so verify BOTH the read view (MarkdownView) and the editor. - Also re-check the chip inside a table cell after the fix —
.wi-chip { max-w-full }should truncate correctly once the cell has a bounded width. - Regression test: measure rendered geometry (
scrollWidth <= clientWidth + 1on the description card, plus the card's right edge vs. the rail's left edge) in E2E — happy-dom returns all-zero geometry, so the component test can only assert structure. Write the FAILING repro first.
Resolution: (open — the close-out subtask fills this)