Files
roboco/docs/ux_ui/design/task-navigation-structure.md
T
eefaca1d3b [3dfc43a1] Task detail overhaul: markdown, navigation, collapsible sections, timestamps (#410)
* [35a27c3d] UX/UI: design task-detail overhaul (#404)

* [39ea1900] docs(ux_ui): add content-readability spec for markdown, collapsible sections, timestamps (#388)

Co-authored-by: UX/UI Developer 1 <ux-dev-1@roboco.tech>

* [71f9aec6] docs(ux_ui): add task navigation/structure design spec (#400)

Adds docs/ux_ui/design/task-navigation-structure.md covering the
breadcrumb trail, prev/next sibling navigation, and a distinct visual
treatment for the read-only constraints section, grounded in the real
task-detail components and existing amber/Lock read-only tokens.

Co-authored-by: UX/UI Developer 2 <ux-dev-2@roboco.tech>

---------

Co-authored-by: UX/UI Developer 1 <ux-dev-1@roboco.tech>
Co-authored-by: UX/UI Developer 2 <ux-dev-2@roboco.tech>

* [9baa1c34] Frontend: implement task-detail overhaul (#408)

* [13b6c723] Task detail: inline timestamps + breadcrumb + prev/next navigation (#390)

* [13b6c723] feat(panel): add inline absolute timestamps, task breadcrumb, and prev/next list nav to task detail

Adds a shared formatAbsoluteTimestamp helper used inline (with tooltip)
next to relative time on progress updates and checkpoints in
tab-progress.tsx, progress-timeline.tsx, and checkpoint-card.tsx.
Adds TaskBreadcrumb (renders only when task.parent_task_id is set) and
TaskListNav, which reads a new taskListNav context in the
scroll-restoration zustand store — populated by the Tasks list page from
TaskTable's live filtered/sorted order — to move to the adjacent task.
When no list context exists for the session or the current task isn't
part of the captured order, both nav buttons render disabled with an
explanatory tooltip (the documented fallback).

* [13b6c723] docs(guide): task detail navigation, timestamps, breadcrumb, and prev/next behavior

---------

Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

* [40acdd31] Task detail: collapsible markdown sections + distinct Constraints styling (#407)

* [40acdd31] feat(panel): collapsible task-detail sections + distinct Constraints styling

Wrap the Description, per-field Notes, and Plan cards in a new
CollapsibleSection (Radix Collapsible + tw-animate-css fade/slide, so
collapse/expand only animates opacity/transform) so a long task no longer
forces continuous scrolling. Restyle the read-only Constraints card with an
amber accent border, background tint, and ShieldAlert icon so it reads as
distinct from authored content. Existing edit/preview toggles are
force-open while active and otherwise unchanged. Adds a global
prefers-reduced-motion override in globals.css.

* [40acdd31] docs(panel): CollapsibleSection component API and usage guide

Documents the new CollapsibleSection wrapper component used for independent collapse/expand of task-detail sections (Description, Constraints, Notes, Plan). Covers component API, controlled vs. uncontrolled state patterns, animation behavior (fade+slide, transform/opacity only), prefers-reduced-motion handling, and usage examples across task-description.tsx / tab-notes.tsx / tab-plan.tsx.

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

* [73f8311f] fix(task-table): remove exhaustive-deps suppression on visible-order effect (#409)

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>

---------

Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>
Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>

* [eb417ef1] Fix: apply auto-collapse thresholds to Progress and Acceptance Criteria surfaces (#429)

* [4e855d24] Apply content-readability-spec collapse thresholds to Progress and Acceptance Criteria surfaces (#416)

* [4e855d24] feat(task-detail): auto-collapse long progress/checkpoint/AC content per readability spec

* [4e855d24] refactor(task-detail): remove inline JSX section-marker comments per no-inline-comments convention

* [4e855d24] docs(task-detail): document content-readability-spec collapse thresholds for CollapsibleSection

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

* [3c90ef34] Wire content-readability thresholds into CollapsibleSection, tab-progress, acceptance-criteria (#430)

* [3c90ef34] test(task-detail): add AC4 combined readability test — 30+ progress entries + long acceptance-criteria list

* [3c90ef34] docs: enhance content-readability thresholds documentation and code comments

- Enhance panel/src/lib/content-readability.ts with usage examples and clarified intent
- Enhance CollapsibleSection with auto-collapse logic explanation and precedence rules
- Enhance TabProgress's RECENT_OPEN_COUNT logic with dual-threshold explanation
- Add comprehensive architecture guide: panel/docs/CONTENT_READABILITY_THRESHOLDS.md covering thresholds, components, testing, and implementation notes

The readability feature prevents long-history tasks (30+ updates, 20+ criteria) from rendering fully expanded, keeping pages navigable. Tests confirm 32 progress updates default to 2 open, and long criteria lists collapse while short ones stay expanded.

---------

Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

* [fc04d84a] Round-3 revision: fix 4 named gaps on task-detail overhaul, one dev leaf per fix (#455)

* [cac9b603] fix(panel): fall back to task.created_at for missing written_at stamp in tab-notes.tsx (#446)

Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>

* [31dd4f99] Remove ArrowLeft back button from task-header.tsx (#441)

* [31dd4f99] Remove ArrowLeft back button and Link wrapper from task-header.tsx, drop now-unused imports

* [31dd4f99] docs(task-navigation): mark spec as implemented, clarify ArrowLeft button removal

Update task-navigation-structure.md to reflect v0.21.0+ implementation:
- Status changed from "proposed" to "implemented"
- Clarified that ArrowLeft back button was removed from task-header.tsx
- Noted that breadcrumb and prev/next navigation now provide all navigation
- Constraints section styling with amber tint and ShieldAlert icon is complete
- Referenced related guide documentation for task-detail-navigation features

---------

Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

* [75fd7444] Wire content prop into EditableNoteCard's CollapsibleSection (#449)

* [75fd7444] feat(panel): wire content prop into EditableNoteCard's CollapsibleSection

Pass the note field's current value into CollapsibleSection's content
prop and derive EditableNoteCard's initial sectionOpen state from
exceedsReadabilityThreshold, so long notes default collapsed with an
expand affordance while short notes render fully expanded.

* [75fd7444] docs(panel): document EditableNoteCard's content-driven collapse pattern in collapsible-section.md

Updated docs/frontend/components/collapsible-section.md to reflect how EditableNoteCard in tab-notes.tsx uses both controlled mode (force-open while editing) and content-driven initialization (seed sectionOpen from content length). Added a new "Combined: controlled + content-driven initialization" example showing this pattern for future developers extending editable-content sections.

Pattern: long notes default collapsed with expand affordance, short notes default expanded, edit forms always visible during editing.

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

* [18ada610] docs(ux-ui): reconcile prev/next nav design spec with shipped list-order behavior (#453)

Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>

---------

Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>
Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>

* [3dfc43a1] round-3 fixes: reconcile nav spec, Alt+Arrow shortcuts, CHANGELOG

The breadcrumb section of task-navigation-structure.md now describes the
shipped single-ancestor design (and drops the stale DropdownMenu claims);
Alt+ArrowLeft/Right on TaskListNav mirror the visible prev/next buttons,
suppressed while an editable element has focus, with tests; the
user-facing CHANGELOG entry lands under Unreleased. Also reflows the
round-1 content-readability-spec so the prose gate is green branch-wide.

* [3dfc43a1] blank line between Unreleased and 0.22.0 sections

---------

Co-authored-by: UX/UI Developer 1 <ux-dev-1@roboco.tech>
Co-authored-by: UX/UI Developer 2 <ux-dev-2@roboco.tech>
Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>
Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-11 07:41:15 +02:00

11 KiB

Navigation/structure spec: breadcrumb, prev/next, constraints distinction

Status: implemented (v0.21.0+) Owner: ux-dev-2 Surface: task detail page (panel/src/app/(dashboard)/tasks/[taskId]/page.tsx) and its header (panel/src/components/tasks/task-detail/task-header.tsx) and description tab (panel/src/components/tasks/task-detail/task-description.tsx).

Dial read

Per the team design bar, this is dense product UI (task detail / admin panel):

  • DESIGN_VARIANCE: 2 — the breadcrumb and prev/next controls are a predictable single row above the existing header; no new grid or layout shape.
  • MOTION_INTENSITY: 1 — hover/focus states only (existing Button / Link hover treatment), no transition is introduced.
  • VISUAL_DENSITY: 8 — compact 28px-tall controls that add one row of chrome, nothing more; the constraints card stays a Card, not a heavier modal or full-width banner.

Problem

The task detail page currently has no sense of where this task sits: the header's only navigation is a single ArrowLeft icon button that always goes to /tasks (task-header.tsx lines 474-479), regardless of whether the task has a parent. A reader drilling into a subtask of a subtask loses the parent chain the moment they land on the page, and moving between sibling tasks (e.g. checking each dev subtask of the same parent while triaging) requires going back to the list and re-finding the next one every time.

Separately, task.constraints (the auto-attached project-wide architectural standard, read-only) renders in task-description.tsx as a Card with only border-dashed and a muted title (lines 181-196) — visually one dash away from the free-form, user-authored description card right above it. A reader skimming the page has no fast visual cue that one box is "the project's rule" and the other is "this task's own words."

This spec is a pure design/markup change: no new dependency, no new backend field (parent_task_id, sequence, and project_id already exist on Task; useTask and useSubtasks already exist in @/hooks/use-tasks).

1. Breadcrumb trail

Implementation status: ✓ Complete (v0.21.0+). This diverged from the rich multi-crumb trail originally proposed for this section — see "What shipped" below for the design that actually rendered.

Component: TaskBreadcrumb at panel/src/components/tasks/task-detail/task-breadcrumb.tsx, rendered in the task detail page ([taskId]/page.tsx) above the TaskHeader component, replacing the standalone ArrowLeft button that was removed from task-header.tsx.

What shipped. A single ancestor level, not the multi-crumb trail below: {Parent title} > {Current title}. It renders only when task.parent_task_id is set — null for a root task, so the row is simply absent rather than showing an empty or placeholder crumb. The parent is fetched with one useTask(parentId) call (a Skeleton covers the loading gap); the parent title is a Link to /tasks/{parent.id}, truncated with a title= tooltip; the current task's own title renders last, non-interactive, in text-foreground/70. Separator: ChevronRight (lucide-react). Deeper ancestry is reached by following the chain up one hop at a time — clicking into the parent shows its own parent, and so on — rather than being flattened into one row.

Why not the rich trail. The proposed Tasks crumb existed to carry the back-to-list affordance the standalone ArrowLeft button used to own; that's redundant with the persistent sidebar's own "Tasks" nav link (components/layout/sidebar.tsx), so dropping it doesn't remove a control, it removes a duplicate. The {Project name} crumb and the multi-ancestor walk (a useTask(parentId) fetch per generation, collapsing behind a DropdownMenu past 3 entries) added fetches and layout branching for a case — chains long enough to need collapsing — that's rare given hierarchies cap at 3 levels per CLAUDE.md's MegaTask section; the one-hop-at-a-time model covers it with a fifth of the markup. Full writeup: docs/guide/task-detail-navigation.md.

Accessibility. The row carries aria-label="Parent task"; the current-task span is plain non-interactive text, not a focusable element.

2. Prev/next list navigation

Implementation status: ✓ Complete (v0.21.0+). List-context-aware navigation, documented separately in docs/guide/task-detail-navigation.md.

Component: TaskListNav at panel/src/components/tasks/task-detail/task-list-nav.tsx, rendered in the task detail page ([taskId]/page.tsx) alongside the breadcrumb — two chevron icon buttons that move to adjacent tasks within the current Tasks list filter/sort context, or disabled when viewed outside the list context.

Data & ordering. This diverged from the original sibling-order proposal during implementation (see docs/guide/task-detail-navigation.md for the full writeup) — "adjacent" means the previous/next row in the Tasks list's last-visited filter/sort order, not a parent_task_id sibling. The Tasks list page (tasks/page.tsx) reports its currently visible, filtered/sorted task order to useScrollRestorationStore.setTaskListNav({ items, queryString }) whenever it changes; TaskListNav reads that taskListNav context, finds task.id's index in items, and derives the prev/next item from index - 1 / index + 1. The context is session-scoped (sessionStorage via the existing Zustand persist middleware), so it survives navigation within a session but doesn't persist across sessions. useSubtasks / parent_task_id / sequence are not part of this feature — sibling-order navigation, as originally proposed below, was not what shipped.

Controls. Two Button variant="outline" size="icon" using ChevronLeft / ChevronRight (lucide-react), each wrapped in a Tooltip:

  • Prev links (Link href="/tasks/{id}{queryString}") to items[index - 1]; disabled (not hidden — a disabled control at the boundary communicates "this is the first one," an absent control reads as "there is no prev/next feature here") when the current task is first in the captured list order, or when no list context exists for this session, or when the current task isn't part of the captured order (opened via a direct link, search, or notification instead of from the list).
  • Next mirrors this at index + 1, disabled when current is last (or the same no-context/not-in-list cases as Prev).
  • Each button's tooltip shows the target item's title when enabled, or the fixed explanation "Open this task from the Tasks list to enable prev/next navigation within that list's filter/sort order." when disabled — so hovering a disabled button still explains why, rather than looking broken.
  • The href carries the captured queryString (the Tasks list's filters/sort at the time it was visited), so navigating there preserves that view.

Keyboard. ✓ Implemented. Alt+ArrowLeft / Alt+ArrowRight navigate prev/next exactly as the visible buttons do (same disabled-at-boundary and no-context behavior — the shortcut is a no-op when the corresponding button would be disabled). A window keydown listener in TaskListNav is suppressed whenever the focused element is an input, textarea, or contenteditable node, so it doesn't fight text-field cursor movement.

Empty state. When no list context has been captured this session, or the current task isn't found in the captured items, both buttons render disabled with the fallback tooltip above rather than being omitted — consistent with the boundary-disabled treatment, so the control's presence is predictable regardless of which task the reader is on.

3. Constraints visual distinction

Implementation status: ✓ Complete (v0.21.0+). Constraints now render with distinctive amber styling and a ShieldAlert icon.

File: panel/src/components/tasks/task-detail/task-description.tsx (lines 181-199).

Treatment — amber "read-only architectural" tint, matching the existing convention already used for the same concept elsewhere in the panel: the per-project Conventions tab uses border-amber-500/40 for its "this is a committed, canonical rule" card (conventions-tab.tsx line 412), and edit-project-dialog.tsx uses text-amber-600 dark:text-amber-400 plus a KeyRound icon (line 214-215) for the same "system-controlled, read-only" semantic on the git-token field. task.constraints is generated from that same .roboco/conventions.yml map (per CLAUDE.md's Architectural Conventions Standard section), so reusing that exact token pairing — instead of introducing a new color — makes the same underlying concept look the same everywhere it appears, rather than inventing a fourth "read-only" treatment.

<Card className="border-amber-500/40 bg-amber-500/5">
  <CardHeader className="pb-3">
    <CardTitle className="text-base flex items-center gap-2 text-amber-700 dark:text-amber-400">
      <Lock className="h-4 w-4" />
      Constraints
    </CardTitle>
  </CardHeader>
  <CardContent>
    <p className="text-xs text-muted-foreground mb-3">
      Architectural standard derived from the project conventions 
      read-only. Applies to every task in this project.
    </p>
    <Markdown>{task.constraints}</Markdown>
  </CardContent>
</Card>
  • Lock (lucide-react, already a project dependency — no new icon package) replaces no icon at all today, signaling "not editable" at a glance before the reader even reads the label — the description card above it has an Edit3 pencil in its header for the opposite reason (it IS editable), so the two icons now read as a matched pair of opposite affordances.
  • bg-amber-500/5 is a barely-there tint (5% opacity) — enough to differentiate the card body from the plain-white description card above without competing with it or reading as a warning/error state (which would call for destructive/red, wrong semantic here — this is informational, not an alert).
  • The header text and icon both take text-amber-700 dark:text-amber-400 (matching edit-project-dialog.tsx's exact light/dark pair) instead of the current plain text-muted-foreground, so the section title itself carries the distinction, not just the border.
  • The explanatory caption paragraph (already present, unchanged) stays text-muted-foreground — only the card chrome and heading change color; body copy stays neutral for readability.

Placement. Unchanged — still renders directly below the description Card in the Overview tab, so the reading order (task's own words, then the project-wide rule) is preserved; only the visual weight changes.

Non-goals

  • No new shadcn/ui primitive (no breadcrumb.tsx added to components/ui) — the breadcrumb composes existing Link, Skeleton, and lucide-react icons already in the tree, per the "fewest files" bar.
  • No change to how ancestors are fetched server-side — the breadcrumb reuses useTask exactly as it exists today; no new endpoint, no new query param. Prev/next reuses the Tasks list's already-fetched page data instead of a dedicated sibling endpoint.
  • No persisted "last visited sibling" or breadcrumb history — this is a point-in-time structural view of the current task's position, not a session/browsing-history feature.
  • No persisted keyboard-shortcut preference (e.g. a toggle to disable Alt+Arrow) — the shortcut always mirrors whatever the visible buttons currently allow.