Improve table view #11

Closed
opened 2026-03-17 22:40:00 +00:00 by maxtkc · 0 comments
Owner

Summary

The timetable/schedule view has several UX rough edges: direction tabs show cryptic Direction 0 (3) labels instead of meaningful destination names; tabs use the outdated tabs-bordered DaisyUI v4 class; the pinned "Stop" header cell is visually occluded by sticky body cells when scrolling; the trip property rows use a bespoke (i) tooltip icon instead of the shared field-component presence-mark system; and the schedule header duplicates context already shown in breadcrumbs. All fixes are contained in two files — timetable-renderer.ts and timetable-data-processor.ts.


Relevant Context

src/modules/timetable-renderer.ts

  • renderTimetableHTML() (line 48) — top-level entry point; calls renderScheduleHeader, renderDirectionTabs, renderTimetableContent. Remove renderScheduleHeader call to eliminate the duplicate title.
  • renderDirectionTabs() (line 105) — builds tab markup. Change tabs-borderedtabs-border (DaisyUI v5).
  • getDirectionDisplayName() (line 574) — returns "${direction.name} (${direction.tripCount})". Change to "Direction X: To [lastStopName]" or "Direction X: No trips".
  • renderTimetableHeader() (line 391) — the <th> for "Stop" needs z-[2] bg-base-100 so it renders above sticky body <th> cells when both table-pin-rows and table-pin-cols are active.
  • renderTripPropertyRows() (line 214) — replace ad-hoc renderTooltip() + requiredMark with: tooltip on the whole <th> via data-tip, colored presence mark (*) from config.presence, no (i) icon.

src/modules/timetable-data-processor.ts

  • DirectionInfo interface (line 57) — add lastStopName?: string.
  • getAvailableDirectionsAsync() (line 574) — after grouping trips by direction, for each direction with trips > 0: pick first trip, query its stop_times sorted by stop_sequence, look up the last stop via this.relationships.getStopByIdAsync(), attach stop_name as lastStopName.

src/modules/schedule-controller.ts

  • renderSchedule() (line 826) — the default-direction padding block (lines 850-857) creates bare DirectionInfo objects without lastStopName; these will correctly render as "No trips" so no change needed here.

DaisyUI v5 note: table-pin-rows sets z-index: 1 on thead th; table-pin-cols sets z-index: 1 on tbody th:first-child. At the intersection the header cell needs z-index: 2 (Tailwind class z-[2]) plus an opaque background to avoid bleed-through.


Phase 1 — All fixes (single phase, low risk)

All changes are rendering/display only; data writes and patch system are untouched.

  • timetable-data-processor.ts: Add lastStopName?: string to DirectionInfo.
  • timetable-data-processor.ts: In getAvailableDirectionsAsync, for each direction with trips, fetch stop_times for one representative trip, sort by stop_sequence, look up last stop name, attach to DirectionInfo.
  • timetable-renderer.ts: Update getDirectionDisplayName — return Direction X: To [lastStopName] when name is known, Direction X: No trips when tripCount === 0, Direction X as fallback.
  • timetable-renderer.ts: Change tabs-borderedtabs-border in renderDirectionTabs.
  • timetable-renderer.ts: Add z-[2] bg-base-100 to the "Stop" <th> in renderTimetableHeader; add bg-base-100 to body <th> cells in renderTimetableBody and renderTripPropertyRows for consistent opaque backgrounds.
  • timetable-renderer.ts: In renderTripPropertyRows, replace renderTooltip(description) + bare requiredMark with: data-tip tooltip on the <th> itself (whole cell hoverable), colored * from config.presence using the same color map as field-component.ts (text-error / text-warning / text-success / text-base-content opacity-40), no (i) SVG icon.
  • timetable-renderer.ts: Remove renderScheduleHeader call from renderTimetableHTML (method body can stay, call is removed).

Gotchas

  • getStopByIdAsync returns Record<string, unknown> | null — cast to access stop_name.
  • Body <th> cells have class="stop-name ..." — they already have border and padding; only add bg-base-100.
  • The plan said renderTooltip was used in other paths — it was not; the method was removed entirely since noUnusedLocals is enforced.


Phase 2 — Bug fixes from phase 1 review

Six bugs were found during testing. They fall into three groups: code-sharing problems (the timetable duplicated logic that should live in field-component.ts), tooltip content problems, and layout/sticky-column problems.

Bug 1: General editor field labels (all of field-component.ts) still render a (i) SVG via renderTooltip. The timetable trip property rows do NOT share this code — they roll their own data-tip inline. Fix: update renderTooltip in field-component.ts to drop the SVG icon and instead put the data-tip on a slim <a> that wraps a ? glyph (or the spec link text) next to the label. This unifies the pattern across the whole app.

Bug 2: Trip property <th> cells have no clickable GTFS spec link. renderTooltip in field-component.ts already wraps in <a href={specUrl}> when a URL is available. After fixing Bug 1, export getSpecUrl from field-component.ts and import it in timetable-renderer.ts; wrap the label text in <a href={specUrl} target="_blank" rel="noopener noreferrer"> so the whole label opens the spec.

  • field-component.ts: Change renderTooltip — remove the inline SVG, instead render <a class="tooltip tooltip-right" data-tip='...' href="${specUrl}" ...>ⓘ</a> (or no link wrapper when specUrl is empty). The (i) SVG element is eliminated entirely.
  • field-component.ts: Export getSpecUrl (change function to export function).
  • timetable-renderer.ts: Import getSpecUrl from field-component.ts. In renderTripPropertyRows, wrap the human-readable label text in <a href="${getSpecUrl('trips.txt')}" target="_blank" rel="noopener noreferrer">…</a> so clicking the label opens the GTFS spec.

Group B — Tooltip content & compactness (bug 3)

The tooltip on trip property <th> cells shows only the field description. The presence level is not mentioned, and the snake_case field name is displayed as a separate visible sub-line rather than inside the tooltip.

  • timetable-renderer.ts: In renderTripPropertyRows, build the data-tip value as a multi-part string: ${description} + (if presence and presence !== 'Optional') \n\n${config.presence} + (if presenceCondition) \n${config.presenceCondition} + \n\n${config.field}. Escape the whole thing as a single attribute value.
  • timetable-renderer.ts: Remove the <div class="stop-id text-xs opacity-70">${config.field}</div> sub-line from the <th> — the snake_case name is now in the tooltip.

Group C — Layout / sticky-column bugs (bugs 4, 5, 6)

Bug 4: Trip property <th> cells appear above the "Stop" <thead th> when scrolling. The <thead th> has z-[2] but is still being obscured. Root cause: the tooltip tooltip-right class on the trip property <th> creates a stacking context that competes with the sticky header corner cell.

Bug 5: Trip property label column is narrower than the stop column — each <th> shrinks to fit its text. Fix: add min-w-[200px] to the trip property <th> cells (matching the Stop header).

Bug 6: When scrolling right, trip property label cells get pushed aside by the data cells next to them. Cause: tooltip tooltip-right on the <th> itself interferes with position: sticky + z-index established by table-pin-cols. Fix: move the tooltip off the <th> and onto an inner <div> wrapper; the <th> becomes a clean sticky container.

  • timetable-renderer.ts: In renderTripPropertyRows, move tooltip tooltip-right and data-tip off the <th> and onto the inner <div class="stop-name-text"> wrapper. The <th> itself retains only: stop-name min-w-[200px] p-2 font-medium border-r border-base-300 bg-base-100.
  • timetable-renderer.ts: Verified the Stop header <th> in renderTimetableHeader has z-[2]. Trip property <th> cells now have no tooltip stacking context — the tooltip is on the inner <div>, so DaisyUI's z-index: 1 from table-pin-cols is not competing with the header corner.

Gotchas

  • getSpecUrl currently builds trips.txt#tripstxt (strips the .). Verify the generated anchor matches the live GTFS spec page before shipping.
  • Removing tooltip tooltip-right from the <th> means the tooltip trigger element is now the inner <div>. DaisyUI requires the .tooltip container to be a block or inline-block element — add block or w-full if the tooltip arrow misaligns.
  • Bug 4 and Bug 6 share the same root cause (tooltip stacking context on the <th>). Fixing Bug 6 (moving the tooltip off <th>) should also resolve Bug 4 — validate both after the single change.

Phase 3 — Shared tooltip abstraction, apostrophe fix, floating header fix

Three issues remain from Phase 2 review: the tooltip content has no labels so it's hard to read at a glance; the Trip Headsign tooltip is silently broken (apostrophe in the description crashes the data-tip='...' attribute); and the trip property <th> cells still float over the "Stop" <thead> row when scrolling vertically. The fix also extends the new shared pattern to the route and stop property pages, which currently look nothing like the timetable headers.

Root cause — apostrophe bug: escapeHtml (DOM-based div.textContent → div.innerHTML) escapes &, <, > but NOT single quotes. trip_headsign's description contains "trip's", which breaks data-tip='...' by closing the attribute early. Fix: add escapeAttr that escapes '&#39; and "&quot;, switch all data-tip to double-quoted.

Root cause — floating header: DaisyUI v5's table-pin-rows CSS applies z-index: 1 to <thead tr> (the row, not individual <th> cells). table-pin-cols sets no z-index on sticky <tbody tr th> cells (confirmed from source). The current z-[2] is on the corner <th> — this only affects stacking within the <thead tr> stacking context, not vs. other rows. Fix: move z-[2] to the <thead tr> element itself.

Scope note: renderFormField / renderLabel in field-component.ts are used by the route browse page, stop view, agency view, feed info, and all other property panels. Updating renderLabel to use the new shared pattern automatically brings all those pages in line with the timetable.

Group A — Shared tooltip abstraction

  • field-component.ts: Add escapeAttr(text: unknown): string — wraps escapeHtml then replaces '&#39; and "&quot;. Replace all data-tip='${escapeHtml(...)}' occurrences in the file with data-tip="${escapeAttr(...)}".
  • field-component.ts: Add exported buildFieldTooltipContent(config: FieldConfig): string — assembles labeled parts joined by \n\n: Description: [text] (if any), ID: field_name, Presence: [value] (if non-Optional), Condition: [presenceCondition] (if present).
  • field-component.ts: Simplify renderPresenceMark(config: FieldConfig) — drop the specUrl param and the per-* condition tooltip (condition is now in the main tooltip). Return just a plain colored <span class="${colorClass}">*</span>.
  • field-component.ts: Add exported renderFieldLabelContent(config: FieldConfig): string — the one shared "display name *(colored)" pattern: label text wrapped in <a href="specUrl"> when a spec URL exists, whole label wrapped in <span class="tooltip tooltip-right cursor-help" data-tip="${escapeAttr(buildFieldTooltipContent(config))}">, followed by renderPresenceMark(config). No ⓘ icon anywhere.
  • field-component.ts: Update renderLabel(config: FieldConfig, inputId: string) — drop the tooltip: string parameter, call renderFieldLabelContent(config) for the label text + presence mark, keep the readonlyIcon appended after. Update the one call site in renderFormField accordingly.
  • field-component.ts: Delete renderTooltip entirely (no remaining callers).
  • timetable-renderer.ts: Add renderFieldLabelContent to the import from field-component.ts. In renderTripPropertyRows, replace the entire inline tipParts / escapedTip / tooltipAttrs / presenceMark / labelContent block with a single renderFieldLabelContent(config) call placed inside <div class="stop-name-text">. Remove getSpecUrl from the import if it's no longer used elsewhere in the file.

Group B — Floating header fix

  • timetable-renderer.ts: In renderTimetableHeader, add class="z-[2]" to the <thead tr> element. Remove z-[2] from the corner <th class="stop-header ..."> (it was on the wrong element — z-index on a child of a sticky <tr> only affects intra-row stacking, not row-vs-row stacking).

Gotchas

  • renderPresenceMark is currently called with specUrl in two places within field-component.ts (renderLabel and inline). After removing specUrl, verify neither call site passes it.
  • escapeAttr must handle null/undefined gracefully (same as escapeHtml).
  • The routes page (page-content-renderer.ts) and stop view (stop-view-controller.ts) both call renderFormFields from field-component.ts — they automatically pick up the new pattern without any changes to those files.
  • After removing z-[2] from the corner <th>, verify the Stop text isn't occluded by the trip property row th cells at the intersection of horizontal + vertical scroll.

Original Issue

It looks like shit rn

## Summary The timetable/schedule view has several UX rough edges: direction tabs show cryptic `Direction 0 (3)` labels instead of meaningful destination names; tabs use the outdated `tabs-bordered` DaisyUI v4 class; the pinned "Stop" header cell is visually occluded by sticky body cells when scrolling; the trip property rows use a bespoke `(i)` tooltip icon instead of the shared field-component presence-mark system; and the schedule header duplicates context already shown in breadcrumbs. All fixes are contained in two files — `timetable-renderer.ts` and `timetable-data-processor.ts`. --- ## Relevant Context **`src/modules/timetable-renderer.ts`** - `renderTimetableHTML()` (line 48) — top-level entry point; calls `renderScheduleHeader`, `renderDirectionTabs`, `renderTimetableContent`. Remove `renderScheduleHeader` call to eliminate the duplicate title. - `renderDirectionTabs()` (line 105) — builds tab markup. Change `tabs-bordered` → `tabs-border` (DaisyUI v5). - `getDirectionDisplayName()` (line 574) — returns `"${direction.name} (${direction.tripCount})"`. Change to `"Direction X: To [lastStopName]"` or `"Direction X: No trips"`. - `renderTimetableHeader()` (line 391) — the `<th>` for "Stop" needs `z-[2] bg-base-100` so it renders above sticky body `<th>` cells when both `table-pin-rows` and `table-pin-cols` are active. - `renderTripPropertyRows()` (line 214) — replace ad-hoc `renderTooltip()` + `requiredMark` with: tooltip on the whole `<th>` via `data-tip`, colored presence mark (`*`) from `config.presence`, no `(i)` icon. **`src/modules/timetable-data-processor.ts`** - `DirectionInfo` interface (line 57) — add `lastStopName?: string`. - `getAvailableDirectionsAsync()` (line 574) — after grouping trips by direction, for each direction with trips > 0: pick first trip, query its `stop_times` sorted by `stop_sequence`, look up the last stop via `this.relationships.getStopByIdAsync()`, attach `stop_name` as `lastStopName`. **`src/modules/schedule-controller.ts`** - `renderSchedule()` (line 826) — the default-direction padding block (lines 850-857) creates bare `DirectionInfo` objects without `lastStopName`; these will correctly render as "No trips" so no change needed here. **DaisyUI v5 note:** `table-pin-rows` sets `z-index: 1` on `thead th`; `table-pin-cols` sets `z-index: 1` on `tbody th:first-child`. At the intersection the header cell needs `z-index: 2` (Tailwind class `z-[2]`) plus an opaque background to avoid bleed-through. --- ## Phase 1 — All fixes (single phase, low risk) All changes are rendering/display only; data writes and patch system are untouched. - [x] **`timetable-data-processor.ts`**: Add `lastStopName?: string` to `DirectionInfo`. - [x] **`timetable-data-processor.ts`**: In `getAvailableDirectionsAsync`, for each direction with trips, fetch stop_times for one representative trip, sort by `stop_sequence`, look up last stop name, attach to `DirectionInfo`. - [x] **`timetable-renderer.ts`**: Update `getDirectionDisplayName` — return `Direction X: To [lastStopName]` when name is known, `Direction X: No trips` when `tripCount === 0`, `Direction X` as fallback. - [x] **`timetable-renderer.ts`**: Change `tabs-bordered` → `tabs-border` in `renderDirectionTabs`. - [x] **`timetable-renderer.ts`**: Add `z-[2] bg-base-100` to the "Stop" `<th>` in `renderTimetableHeader`; add `bg-base-100` to body `<th>` cells in `renderTimetableBody` and `renderTripPropertyRows` for consistent opaque backgrounds. - [x] **`timetable-renderer.ts`**: In `renderTripPropertyRows`, replace `renderTooltip(description)` + bare `requiredMark` with: `data-tip` tooltip on the `<th>` itself (whole cell hoverable), colored `*` from `config.presence` using the same color map as `field-component.ts` (`text-error` / `text-warning` / `text-success` / `text-base-content opacity-40`), no `(i)` SVG icon. - [x] **`timetable-renderer.ts`**: Remove `renderScheduleHeader` call from `renderTimetableHTML` (method body can stay, call is removed). ### Gotchas - `getStopByIdAsync` returns `Record<string, unknown> | null` — cast to access `stop_name`. - Body `<th>` cells have `class="stop-name ..."` — they already have border and padding; only add `bg-base-100`. - The plan said `renderTooltip` was used in other paths — it was not; the method was removed entirely since `noUnusedLocals` is enforced. --- --- ## Phase 2 — Bug fixes from phase 1 review Six bugs were found during testing. They fall into three groups: code-sharing problems (the timetable duplicated logic that should live in `field-component.ts`), tooltip content problems, and layout/sticky-column problems. ### Group A — Code sharing: `(i)` icon and GTFS ref link (bugs 1 & 2) **Bug 1**: General editor field labels (all of `field-component.ts`) still render a `(i)` SVG via `renderTooltip`. The timetable trip property rows do NOT share this code — they roll their own `data-tip` inline. Fix: update `renderTooltip` in `field-component.ts` to drop the SVG icon and instead put the `data-tip` on a slim `<a>` that wraps a `?` glyph (or the spec link text) next to the label. This unifies the pattern across the whole app. **Bug 2**: Trip property `<th>` cells have no clickable GTFS spec link. `renderTooltip` in `field-component.ts` already wraps in `<a href={specUrl}>` when a URL is available. After fixing Bug 1, export `getSpecUrl` from `field-component.ts` and import it in `timetable-renderer.ts`; wrap the label text in `<a href={specUrl} target="_blank" rel="noopener noreferrer">` so the whole label opens the spec. - [x] **`field-component.ts`**: Change `renderTooltip` — remove the inline SVG, instead render `<a class="tooltip tooltip-right" data-tip='...' href="${specUrl}" ...>ⓘ</a>` (or no link wrapper when specUrl is empty). The `(i)` SVG element is eliminated entirely. - [x] **`field-component.ts`**: Export `getSpecUrl` (change `function` to `export function`). - [x] **`timetable-renderer.ts`**: Import `getSpecUrl` from `field-component.ts`. In `renderTripPropertyRows`, wrap the human-readable label text in `<a href="${getSpecUrl('trips.txt')}" target="_blank" rel="noopener noreferrer">…</a>` so clicking the label opens the GTFS spec. ### Group B — Tooltip content & compactness (bug 3) The tooltip on trip property `<th>` cells shows only the field description. The presence level is not mentioned, and the `snake_case` field name is displayed as a separate visible sub-line rather than inside the tooltip. - [x] **`timetable-renderer.ts`**: In `renderTripPropertyRows`, build the `data-tip` value as a multi-part string: `${description}` + (if presence and presence !== 'Optional') `\n\n${config.presence}` + (if presenceCondition) `\n${config.presenceCondition}` + `\n\n${config.field}`. Escape the whole thing as a single attribute value. - [x] **`timetable-renderer.ts`**: Remove the `<div class="stop-id text-xs opacity-70">${config.field}</div>` sub-line from the `<th>` — the snake_case name is now in the tooltip. ### Group C — Layout / sticky-column bugs (bugs 4, 5, 6) **Bug 4**: Trip property `<th>` cells appear above the "Stop" `<thead th>` when scrolling. The `<thead th>` has `z-[2]` but is still being obscured. Root cause: the `tooltip tooltip-right` class on the trip property `<th>` creates a stacking context that competes with the sticky header corner cell. **Bug 5**: Trip property label column is narrower than the stop column — each `<th>` shrinks to fit its text. Fix: add `min-w-[200px]` to the trip property `<th>` cells (matching the Stop header). **Bug 6**: When scrolling right, trip property label cells get pushed aside by the data cells next to them. Cause: `tooltip tooltip-right` on the `<th>` itself interferes with `position: sticky` + `z-index` established by `table-pin-cols`. Fix: move the tooltip off the `<th>` and onto an inner `<div>` wrapper; the `<th>` becomes a clean sticky container. - [x] **`timetable-renderer.ts`**: In `renderTripPropertyRows`, move `tooltip tooltip-right` and `data-tip` off the `<th>` and onto the inner `<div class="stop-name-text">` wrapper. The `<th>` itself retains only: `stop-name min-w-[200px] p-2 font-medium border-r border-base-300 bg-base-100`. - [x] **`timetable-renderer.ts`**: Verified the Stop header `<th>` in `renderTimetableHeader` has `z-[2]`. Trip property `<th>` cells now have no tooltip stacking context — the tooltip is on the inner `<div>`, so DaisyUI's `z-index: 1` from `table-pin-cols` is not competing with the header corner. ### Gotchas - `getSpecUrl` currently builds `trips.txt` → `#tripstxt` (strips the `.`). Verify the generated anchor matches the live GTFS spec page before shipping. - Removing `tooltip tooltip-right` from the `<th>` means the tooltip trigger element is now the inner `<div>`. DaisyUI requires the `.tooltip` container to be a block or inline-block element — add `block` or `w-full` if the tooltip arrow misaligns. - Bug 4 and Bug 6 share the same root cause (tooltip stacking context on the `<th>`). Fixing Bug 6 (moving the tooltip off `<th>`) should also resolve Bug 4 — validate both after the single change. --- ## Phase 3 — Shared tooltip abstraction, apostrophe fix, floating header fix Three issues remain from Phase 2 review: the tooltip content has no labels so it's hard to read at a glance; the Trip Headsign tooltip is silently broken (apostrophe in the description crashes the `data-tip='...'` attribute); and the trip property `<th>` cells still float over the "Stop" `<thead>` row when scrolling vertically. The fix also extends the new shared pattern to the route and stop property pages, which currently look nothing like the timetable headers. **Root cause — apostrophe bug:** `escapeHtml` (DOM-based `div.textContent → div.innerHTML`) escapes `&`, `<`, `>` but NOT single quotes. `trip_headsign`'s description contains `"trip's"`, which breaks `data-tip='...'` by closing the attribute early. Fix: add `escapeAttr` that escapes `'` → `&#39;` and `"` → `&quot;`, switch all `data-tip` to double-quoted. **Root cause — floating header:** DaisyUI v5's `table-pin-rows` CSS applies `z-index: 1` to `<thead tr>` (the row, not individual `<th>` cells). `table-pin-cols` sets no z-index on sticky `<tbody tr th>` cells (confirmed from source). The current `z-[2]` is on the corner `<th>` — this only affects stacking within the `<thead tr>` stacking context, not vs. other rows. Fix: move `z-[2]` to the `<thead tr>` element itself. **Scope note:** `renderFormField` / `renderLabel` in `field-component.ts` are used by the route browse page, stop view, agency view, feed info, and all other property panels. Updating `renderLabel` to use the new shared pattern automatically brings all those pages in line with the timetable. ### Group A — Shared tooltip abstraction - [x] **`field-component.ts`**: Add `escapeAttr(text: unknown): string` — wraps `escapeHtml` then replaces `'` → `&#39;` and `"` → `&quot;`. Replace all `data-tip='${escapeHtml(...)}'` occurrences in the file with `data-tip="${escapeAttr(...)}"`. - [x] **`field-component.ts`**: Add exported `buildFieldTooltipContent(config: FieldConfig): string` — assembles labeled parts joined by `\n\n`: `Description: [text]` (if any), `ID: field_name`, `Presence: [value]` (if non-Optional), `Condition: [presenceCondition]` (if present). - [x] **`field-component.ts`**: Simplify `renderPresenceMark(config: FieldConfig)` — drop the `specUrl` param and the per-`*` condition tooltip (condition is now in the main tooltip). Return just a plain colored `<span class="${colorClass}">*</span>`. - [x] **`field-component.ts`**: Add exported `renderFieldLabelContent(config: FieldConfig): string` — the one shared "display name *(colored)" pattern: label text wrapped in `<a href="specUrl">` when a spec URL exists, whole label wrapped in `<span class="tooltip tooltip-right cursor-help" data-tip="${escapeAttr(buildFieldTooltipContent(config))}">`, followed by `renderPresenceMark(config)`. No ⓘ icon anywhere. - [x] **`field-component.ts`**: Update `renderLabel(config: FieldConfig, inputId: string)` — drop the `tooltip: string` parameter, call `renderFieldLabelContent(config)` for the label text + presence mark, keep the `readonlyIcon` appended after. Update the one call site in `renderFormField` accordingly. - [x] **`field-component.ts`**: Delete `renderTooltip` entirely (no remaining callers). - [x] **`timetable-renderer.ts`**: Add `renderFieldLabelContent` to the import from `field-component.ts`. In `renderTripPropertyRows`, replace the entire inline `tipParts` / `escapedTip` / `tooltipAttrs` / `presenceMark` / `labelContent` block with a single `renderFieldLabelContent(config)` call placed inside `<div class="stop-name-text">`. Remove `getSpecUrl` from the import if it's no longer used elsewhere in the file. ### Group B — Floating header fix - [x] **`timetable-renderer.ts`**: In `renderTimetableHeader`, add `class="z-[2]"` to the `<thead tr>` element. Remove `z-[2]` from the corner `<th class="stop-header ...">` (it was on the wrong element — z-index on a child of a sticky `<tr>` only affects intra-row stacking, not row-vs-row stacking). ### Gotchas - `renderPresenceMark` is currently called with `specUrl` in two places within `field-component.ts` (`renderLabel` and inline). After removing `specUrl`, verify neither call site passes it. - `escapeAttr` must handle `null`/`undefined` gracefully (same as `escapeHtml`). - The routes page (`page-content-renderer.ts`) and stop view (`stop-view-controller.ts`) both call `renderFormFields` from `field-component.ts` — they automatically pick up the new pattern without any changes to those files. - After removing `z-[2]` from the corner `<th>`, verify the Stop text isn't occluded by the trip property row th cells at the intersection of horizontal + vertical scroll. --- ## Original Issue It looks like shit rn
maxtkc self-assigned this 2026-03-17 22:40:00 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
gtfs.zone/coloring-book#11
No description provided.