Skip to content

0011: Responsive layout

Every Concourse app works on a phone screen without horizontal scrolling

  • ID: PRD-0011
  • Status: Accepted
  • Decision maker: Trevor Gerhardt
  • Scope: Concourse, MIDOT, the Concourse documentation, the TREDIS manual, US counties, @concourse/ui, @concourse/docs, and @concourse/vitest-config
  • Product owner: Trevor Gerhardt
  • Design owner: Trevor Gerhardt
  • Engineering owner: Conveyal
  • Last reviewed: 2026-10-15
  • Related issues or projects: Not applicable. This work follows a direct planning request.
  • Related designs: Not applicable. The responsive layout standard owns the layout rule and its examples.
  • Related ADRs: Not applicable. The rule is a working standard, not an architectural decision.
  • Related user documentation: Not applicable. Users do not need instructions to use a page that fits their screen.

Summary

Every page of every Concourse app fits the width of the screen, from a 320 pixel phone to a desktop. A user never scrolls a page sideways. Tables change into stacked lists on a narrow screen, and only code samples can scroll inside their own box. Each app's browser tests check every route, so a change that breaks the layout fails before it merges.

User problem and evidence

Users open Concourse, MIDOT, and the documentation sites on phones. Before this change, some pages were wider than a phone screen:

  • The Concourse header was 660 pixels wide on every signed-in page.
  • The account, members, and audit tables scrolled sideways inside the page.
  • The documentation page links at the end of each page went past the screen edge when a page name was long.
  • Markdown tables in the TREDIS manual went past the screen edge.

A page that scrolls sideways hides content. The user must scroll in two directions to read one row, and can miss content that is off the screen. No test checked the layout, so new pages could add the same problem.

Goals and success measures

GoalMeasureTarget or review method
No page scrolls sidewaysRoutes with an element past the right screen edgeZero routes at 320 and 375 pixels, in each app
Tables stay readable on a phoneStacked table cells without their column nameZero in application tables and Markdown tables
Regressions fail before a mergeApps whose browser tests run the layout checkAll five apps, in pnpm verify

Non-goals

  • A separate mobile design or a mobile app. The same pages adapt to the screen width.
  • Screens narrower than 320 CSS pixels.
  • Code samples. They can scroll inside their own box, because wrapping changes the meaning of code.
  • Visual design review at every width. The check finds overflow. It does not judge appearance.

Users and use cases

Use case: Read a page on a phone

  • Actor: Any Concourse, MIDOT, or documentation user with a phone.
  • Need: Read and use a page without horizontal scrolling.
  • Goal: Complete the same tasks as on a desktop.

Use case: Change a page safely

  • Actor: A developer or an agent who changes a page.
  • Need: Know before merge when the change makes a page wider than a phone screen.
  • Goal: Keep every page usable on a phone.

Requirements

PRD-0011-US-01: Pages fit a phone screen

  • Story status: Current
  • Replaces: N/A
  • Replaced by: N/A
  • User need: As a user on a phone, I want each page to fit my screen, so that I can read all of it without horizontal scrolling.
  • Requirement: At a viewport width of 320 CSS pixels or more, no element of a page extends past the right edge of the screen. Content is not clipped to hide overflow. This applies to every route of Concourse, MIDOT, the Concourse documentation, the TREDIS manual, and US counties.
  • Done when:
    • The layout check passes for every route of each app at 320 and 375 pixels.
    • Long values, such as email addresses, organization names, and page names, wrap.
    • Headers and navigation wrap onto more lines instead of passing the screen edge.

PRD-0011-US-02: Tables stay readable on a phone

  • Story status: Current
  • Replaces: N/A
  • Replaced by: N/A
  • User need: As a user on a phone, I want each table row as one readable block, so that I can read a row without horizontal scrolling.
  • Requirement: Below the md breakpoint, each table row is a stacked block. Each cell shows its column name on a line above its value. Screen readers still get the column headers.
  • Done when:
    • The account, members, and audit tables in Concourse stack on a phone and show a column name above each value.
    • Markdown tables in both documentation sites stack on a phone and take each column name from their header row.

PRD-0011-US-03: Code samples keep their format

  • Story status: Current
  • Replaces: N/A
  • Replaced by: N/A
  • User need: As a reader of the documentation on a phone, I want code samples to keep their lines, so that I can copy and read commands without changed line breaks.
  • Requirement: A code block can scroll horizontally inside its own box. The box fits the screen, and the page around it does not scroll.
  • Done when:
    • A documentation page with a long command line passes the layout check.
    • The code block keeps its original line breaks.

PRD-0011-US-04: A layout regression fails before merge

  • Story status: Current
  • Replaces: N/A
  • Replaced by: N/A
  • User need: As a developer, I want the browser tests to fail when a page passes the screen edge, so that I fix the layout before users see it.
  • Requirement: Each app's browser tests open every route at 320 and 375 pixels and fail when an element extends past the screen edge or a box clips its content. The check reads element positions, so clipping cannot hide a failure. pnpm verify runs these tests.
  • Done when:
    • Each of the five apps has a responsive-layout.spec.ts browser test.
    • The documentation tests read their pages from /llms.txt, so a new page is checked without a test change.
    • A failure names each route, width, and element that passes the screen edge.

Implementation

  • The responsive layout standard states the rule, the allowed exception, and how the rule is checked.
  • expectRoutesFitNarrowViewports in @concourse/vitest-config/layout runs the check.
  • Each app has a tests/e2e/responsive-layout.spec.ts. The documentation apps and US counties serve their production build for the check.
  • The shared table component stacks rows below the md breakpoint and shows each cell's label. The Markdown table sets each label from its header row.
  • The Concourse header wraps, and the documentation page links wrap long page names.

Experience and content

  • Entry points: every route of the five apps.
  • Narrow screens: navigation and actions wrap onto more lines. Tables become stacked blocks with a column name above each value. Code blocks scroll inside their box.
  • Wide screens: layouts do not change, except that long table values wrap instead of making the table wider.
  • Accessibility: stacked tables keep their header row for screen readers. Text is not clipped, so all content stays visible.

Constraints and dependencies

  • The responsive layout standard owns the rule. This PRD owns the product outcome.
  • Michigan Digital Guidelines and shadcn components can have fixed widths. A page that uses one must still pass the check.
  • The check needs a browser test suite in each app, and Chromium on the machine that runs pnpm verify.

User documentation

  • Audience: Not applicable. The behavior needs no instructions.
  • Tasks to explain: Not applicable.
  • Agreed terms: Not applicable.
  • Examples or screenshots needed: N/A
  • Published documentation: Not applicable.

Revision history

No revisions.

References