Skip to content

design(chrome): nav-bar redesign — glass pageheader, gradient tabs, Tasks board summary#3680

Open
gloriafolaron wants to merge 12 commits into
masterfrom
chrome/nav-redesign
Open

design(chrome): nav-bar redesign — glass pageheader, gradient tabs, Tasks board summary#3680
gloriafolaron wants to merge 12 commits into
masterfrom
chrome/nav-redesign

Conversation

@gloriafolaron

Copy link
Copy Markdown
Contributor

Carved cleanly off master from the integrate/report-chrome line (11 commits cherry-picked, no conflicts). This is the reusable chrome/nav design system + its first application on the Tasks board.

What's in it

Chrome design system (CSS + shared component)

  • pageheader becomes a glass card with even 10px block rhythm; solid-white cards (structure/style/text-styles/dropdowns CSS).
  • Gradient nav bar for the global .tabs horizontal nav (nav.css), with a WCAG-driven conditional scrim: header.blade.php computes --nav-scrim from accent luminance (0 for dark accents, 0.22 for light) so tab contrast stays AA on every theme.
  • Extracted <x-global::subjectSwitcher> (the "Parent › Current ▾" breadcrumb) with an optional subline slot; separator //.
  • Nav bar hugs its tabs on action-less pages (:not(:has(.tabs-actions))).

Tasks board application

  • One-line board header: stats + sprint ⋮ on the right, actions (dark-chip +New, filter) on the nav bar.
  • Board-summary metrics (total / unassigned / due-this-week / last-updated) reflecting current-board filters — new BoardSummary model + getBoardSummary() service (TicketsServiceTest, 32 tests).

Checks (local)

pint ✓ · phpstan ✓ (BoardSummary, Tickets) · 32 board-summary tests green. No dist/ committed — CI builds assets.

🤖 Generated with Claude Code

gloriafolaron and others added 11 commits July 21, 2026 17:39
Modernizes the app-wide page header: the pageheader becomes a proper
glass card (like the content card below it) instead of transparent text
floating on the page's gradient background.

Mechanism (Marcel's "just update the pageheader CSS"): the old design
used a 150px invisible padding cushion on .pageheader plus a -95px
.maincontent overlap so the header title floated transparently on the
gradient. That's replaced by a boxed header — background:
var(--glass-background), border-radius, shadow, real padding, and
.maincontent margin-top back to 0. The gradient itself (on
.rightpanel::before, untouched) still shows around the cards.

Header text flips white → dark to read on the new light card:
.pagetitle h1 and .pageicon move from --main-titles-color (#fff) to
--primary-font-color. Both are tokens, so dark mode adapts for free
(glass-background → dark panel, primary-font-color → light text). The
--main-titles-color token is unchanged, so its other consumers (menu,
etc.) are unaffected.

Applied globally so every page's header is consistent — no per-page
overrides, no scoped body-class hacks. Login/registration keep their
own look via the more-specific .regpanelinner .pageheader rules.

Verified live (cache-busted) on two page types:
- To-Dos (/tickets/showKanban): white glass box, dark readable title,
  the existing "To-Dos // All To-Dos" breadcrumb-switcher sits cleanly
  inside it, no horizontal scroll.
- Dashboard: white box, dark "Strategy" title, no scroll, left menu
  intact.

PR 1 of the chrome-modernization plan. This is the foundation; pages
(report, RA, Tasks) then pass richer slot content — breadcrumb +
subject switcher — into this modernized header.

Follow-up validation before merge: broader page sweep (Settings,
project views), dark mode, and mobile (mobile.css has its own
.pagetitle h1 rule). Plugin pages (RA, reports) validate once they
rebase onto this.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GYSPNgLdUwEnxgYqTxMc85
(cherry picked from commit 020f75c4a8c6df208c45baae741db1cddee59442)
The "Parent // Current ▾" page-title dropdown (header-title-dropdown) was
hand-rolled inline across ~18 templates — To-Dos sprint switcher, canvas
boards, wiki, ideas, goals, projects, etc. This consolidates the chrome
into one <x-global::subjectSwitcher> component (Marcel's "move/optimize
existing, don't build new"), and migrates the first consumer (To-Dos) as
proof.

The component owns the structure — <h1> + optional parent crumb +
separator + a Bootstrap dropdown whose toggle shows the current subject.
The MENU ITEMS are the slot, so each consumer keeps its own domain
options (sprints here carry their own onclick) while sharing the chrome.

It keeps the existing classes (.header-title-dropdown, .dropdown,
.dropdown-menu), so the established CSS (dropdowns.css) and Bootstrap
data-toggle behavior apply unchanged — migrating a consumer is a
zero-visual-change swap, verified on To-Dos: renders "To-Dos //
All To-Dos ▾", menu items intact, dropdown opens, no regressions.

A `switchStyle` prop ('legacy' default | 'pill') is included so the
modern pill treatment is a one-line flip once that call is made — no
rebuild.

First of an incremental consolidation: the other ~17 inline consumers
(timelineHeader, canvas boards, wiki, …) migrate to this component in
follow-ups, each a mechanical swap. The report and RA will consume it
for their subject switchers too.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GYSPNgLdUwEnxgYqTxMc85
(cherry picked from commit b036505ea55820a19751577775288e61240a80db)
- pageheader: drop the 8px bottom margin so the gap to the next block is
  the single 10px from .maincontent's padding-top. Vertical rhythm
  (top-nav→header, header→nav/content) is now an even 10px everywhere
  (was 8+10 = an uneven 18px between header and the block below it).
- --glass-background 0.85 → solid white: the header/content cards were
  translucent, so nested report surfaces read as mismatched whites.
  Solid white makes every card one cohesive surface.
- dropdown pill / pageicon refinements to match the white-card header.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GYSPNgLdUwEnxgYqTxMc85
(cherry picked from commit 5e6208dd23829196f194f9b832c5cddfbe7e9c22)
…jectSwitcher

- subline slot: a light meta/metrics line under the title (report uses it
  for "Strategy report · updated …"; Tasks will use it for task count +
  last-updated once the board summary is wired).
- breadcrumb parent: subtle gray, no underline (was a heavy link).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GYSPNgLdUwEnxgYqTxMc85
(cherry picked from commit 6083fb6b9b3ca81d31786189a2084e40fe19ac2f)
Adds a light metrics line under the "To-Dos // <board>" title (same slot
the report uses): total tasks · unassigned · due this week · last updated.

- TicketService::getBoardSummary() computes the metrics from the already-
  fetched grouped ticket set, so counts reflect the current board filters
  with no extra query. Wired into getTicketTemplateAssignments() so all
  three board views (Kanban/Table/List) get it.
- BoardSummary model holds total/unassigned/dueThisWeek/lastUpdated.
- "Due this week" compares calendar dates in the user's timezone so a
  date-only dateToFinish lands in the intended week; "last updated" is the
  max ticket modified date (the board's most recent change).
- Header renders it via the subjectSwitcher subline slot; zero-value
  segments (unassigned / due-this-week) are hidden to keep the line tidy.
- Unit tests cover the count math, the due-this-week window, last-updated,
  and the empty-board case.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GYSPNgLdUwEnxgYqTxMc85
(cherry picked from commit a1960b8d541a4f58714fd85d0812f3f250f0e9c1)
Restyles the shared .maincontentinner.tabs (Tasks Kanban/Table/List switcher,
timeline tabs, plugin tabs) to the same gradient nav bar as the stakeholder
report's Option C — one model for every horizontal-nav page, no per-template
edits. Active tab = white pill, inactive = muted white; sticky behavior kept.

- Bar uses --accent1/--accent2 so it adapts to theme + dark mode.
- Active-pill text is fixed #004666 (the pill is always white, so a light user
  accent via --primary-color would be illegible). Verified in light and dark.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GYSPNgLdUwEnxgYqTxMc85
(cherry picked from commit ec3aa630e226537406d20dfb5b06d79675b21f4a)
Hidden optional segments (unassigned / due-this-week) left their surrounding
spaces behind, so the line rendered with stray gaps ("6 tasks   · updated")
instead of the report header's single "· " separators. Build the present
segments in PHP and implode(' · ') so spacing is clean regardless of which
metrics show.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GYSPNgLdUwEnxgYqTxMc85
(cherry picked from commit b0203c519bcfe510f11604ed611a283d38da9547)
Reworks the Tasks board chrome so the header and nav read as one compact,
connected unit:

- One-line header: breadcrumb/title on the left; board stats and (for real
  sprint views only) the sprint edit/delete ⋮ in a right cluster. Drops the
  separate sub-line row (header 83px → ~53px).
- Board actions (New / Filter / Group By) move out of their own toolbar row
  into the right of the nav bar — like the report's picker — so there's no
  extra band and tasks start higher. Removed from showKanban/showAll/showList;
  rendered in the shared ticketBoardTabs (guarded on $searchCriteria). showAll
  keeps only its DataTables buttons row.
- Tabs are a framed segmented control (accent-gradient grid + soft outline +
  white active segment); New is the standard Leantime button, Filter/Group By
  are light text controls — nav vs actions read distinctly.
- Removed the nav-bar drop shadow so it sits connected to the content.
- Stacking fixes: header above the nav bar (z-index 12) so the breadcrumb and
  sprint ⋮ dropdowns aren't clipped/covered; nav bar above the sticky column
  headers (z-index 11) so Filter/Group By open on top; right-anchored the
  action panels/menus so they never fall off the right edge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GYSPNgLdUwEnxgYqTxMc85
(cherry picked from commit f9e7a9f9fd019069965e3c130152b6a3c4264cb1)
House-style divider defaults to a single chevron (›) instead of the double
slash (//), which read as URL-ish. Applies everywhere the switcher is used
(Tasks, report, canvas boards, …).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GYSPNgLdUwEnxgYqTxMc85
(cherry picked from commit 6e83a8931b6b260e4b41438f09b97cce16d9d5ee)
- +New becomes a filled dark chip: reads as an action, distinct from both the
  white/framed tabs and the light text organizers (Filter / Group By). No
  divider — keeps the bar's negative space.
- Conditional contrast scrim: white nav controls fail WCAG AA on a light accent
  (a mid-tone). The header computes each theme accent's luminance and sets
  --nav-scrim (0 for dark accents → fully vivid; 0.22 for light accents → white
  controls clear 4.5:1). Dark-accent/default themes are unaffected.
- Filter / Group By go full white (was 0.85) so they pass on the scrimmed
  bright end.
- Visible keyboard focus rings on every tab + action (white ring on the
  gradient; accent ring on the white active tab).

Follow-ups logged (pre-existing / minor): the Filter toggle is an <a onclick>
(not keyboard-focusable) — a ticketFilter fix; active tab could carry
aria-current and the toggles aria-expanded.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GYSPNgLdUwEnxgYqTxMc85
(cherry picked from commit a66934a1eb90fa899fd19a437375dfd6531b19bd)
Pages using the global .tabs nav without a .tabs-actions cluster (timeline —
roadmap/calendar/milestones — and the plugin tabs) were rendering the framed
tab group on the left with ~1000px of empty gradient to the right. Hug the bar
to its content there (:not(:has(.tabs-actions)) → width: fit-content) so it
reads as a compact nav; boards with actions stay full-width so the actions can
sit at the right edge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GYSPNgLdUwEnxgYqTxMc85
(cherry picked from commit 27eba6ad110e5bc514a57ed289138ba2cf466ed8)
Copilot AI review requested due to automatic review settings July 21, 2026 21:44
@gloriafolaron
gloriafolaron requested a review from a team as a code owner July 21, 2026 21:44
@gloriafolaron
gloriafolaron requested review from marcelfolaron and removed request for a team July 21, 2026 21:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Introduces a reusable “chrome” update (page header + gradient tab bar + shared subject switcher) and applies it to the Tickets Tasks board by adding an on-screen board summary (counts + last-updated) that reflects current filters.

Changes:

  • Redesign global page header and horizontal tabs nav (glass/white card header, gradient segmented tabs, action area on the nav bar).
  • Extract a shared <x-global::subjectSwitcher> Blade component for the “Parent › Current ▾” breadcrumb dropdown.
  • Add BoardSummary + Tickets::getBoardSummary() and unit tests; render board metrics in the Tickets header.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/Unit/app/Domain/Tickets/Services/TicketsServiceTest.php Adds unit tests covering board-summary counts and last-updated behavior.
public/theme/default/css/light.css Adjusts glass background token for the updated header card styling.
public/assets/css/components/text-styles.css Updates page title text styling for the new light header surface.
public/assets/css/components/style.default.css Updates icon/button styling and layout behavior for the new header flex layout.
public/assets/css/components/structure.css Converts .pageheader into a card-like header and removes the legacy overlap spacing.
public/assets/css/components/nav.css Redesigns horizontal .tabs as a gradient segmented control and adds right-aligned actions area.
public/assets/css/components/dropdowns.css Restyles header dropdown toggle for readability on the new header surface; adds subline styling.
app/Views/Templates/sections/header.blade.php Computes and sets --nav-scrim based on accent luminance for consistent tab contrast.
app/Views/Templates/components/subjectSwitcher.blade.php Adds the new shared subject switcher component used by header breadcrumbs.
app/Language/en-US.ini Adds new strings for board-summary metrics in the header.
app/Domain/Tickets/Templates/submodules/ticketHeader.blade.php Migrates sprint selector to the shared subject switcher and renders board-summary meta in the header.
app/Domain/Tickets/Templates/submodules/ticketBoardTabs.blade.php Moves board actions (new/filter/group) into the tabs/nav bar as a right-side cluster.
app/Domain/Tickets/Templates/showList.blade.php Removes the old toolbar row since actions are now in the nav bar.
app/Domain/Tickets/Templates/showKanban.blade.php Removes the old toolbar row since actions are now in the nav bar.
app/Domain/Tickets/Templates/showAll.blade.php Removes left toolbar row; keeps table-specific controls right-aligned.
app/Domain/Tickets/Services/Tickets.php Adds getBoardSummary() and injects boardSummary into template assignments.
app/Domain/Tickets/Models/BoardSummary.php Introduces the BoardSummary model to carry computed metrics.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread app/Domain/Tickets/Services/Tickets.php Outdated
Comment on lines +4003 to +4009
$due = is_object($ticket) ? ($ticket->dateToFinish ?? null) : ($ticket['dateToFinish'] ?? null);
if (! empty($due) && ! str_starts_with((string) $due, '0000-00-00')) {
$dueDate = dtHelper()->parseDbDateTime((string) $due)->setToUserTimezone()->format('Y-m-d');
if ($dueDate >= $weekStartDate && $dueDate <= $weekEndDate) {
$summary->dueThisWeek++;
}
}
Comment on lines +4011 to +4017
$modified = is_object($ticket) ? ($ticket->modified ?? null) : ($ticket['modified'] ?? null);
if (! empty($modified) && ! str_starts_with((string) $modified, '0000-00-00')) {
$modifiedDate = dtHelper()->parseDbDateTime((string) $modified);
if ($summary->lastUpdated === null || $modifiedDate->greaterThan($summary->lastUpdated)) {
$summary->lastUpdated = $modifiedDate;
}
}
Comment thread app/Views/Templates/components/subjectSwitcher.blade.php
- subjectSwitcher: escape $parent/$separator ({!! !!} → {{ }}). A shared
  component echoing a crumb label unescaped is an XSS footgun; the only
  caller passes __() (plain text), so escaping is a zero-behavior-change
  hardening. $current was already escaped.
- getBoardSummary: guard date parsing with dtHelper()->isValidDateString()
  instead of only filtering 0000-00-00. parseDbDateTime() throws on the
  1969-12-31 sentinel too, which would have broken the board header on any
  ticket carrying that stamp. Added a regression test.
- Remove orphaned @throws BindingResolutionException docblock above
  getBoardSummary (it no longer throws).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GYSPNgLdUwEnxgYqTxMc85
Copilot AI review requested due to automatic review settings July 22, 2026 00:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 17 out of 17 changed files in this pull request and generated 3 comments.

Comment on lines +3975 to +3981
* @param array $groupedTickets Grouped tickets as returned by getAllGrouped()
* (each group holds its rows under 'items').
*
* @api
*/
public function getBoardSummary(array $groupedTickets): BoardSummary
{
Comment on lines +4000 to +4009
$due = is_object($ticket) ? ($ticket->dateToFinish ?? null) : ($ticket['dateToFinish'] ?? null);
// isValidDateString() is exactly what parseDbDateTime() guards on
// (rejects empty, 0000-00-00, and the 1969-12-31 sentinel), so this
// can never throw on a sentinel/invalid value.
if (dtHelper()->isValidDateString($due !== null ? (string) $due : null)) {
$dueDate = dtHelper()->parseDbDateTime((string) $due)->setToUserTimezone()->format('Y-m-d');
if ($dueDate >= $weekStartDate && $dueDate <= $weekEndDate) {
$summary->dueThisWeek++;
}
}
Comment on lines +4011 to +4017
$modified = is_object($ticket) ? ($ticket->modified ?? null) : ($ticket['modified'] ?? null);
if (dtHelper()->isValidDateString($modified !== null ? (string) $modified : null)) {
$modifiedDate = dtHelper()->parseDbDateTime((string) $modified);
if ($summary->lastUpdated === null || $modifiedDate->greaterThan($summary->lastUpdated)) {
$summary->lastUpdated = $modifiedDate;
}
}
@marcelfolaron

Copy link
Copy Markdown
Collaborator

Status: triaged / needs visual review · Priority: P2 (broad CSS blast radius) · Next action: eyeball the nav across themes + confirm no dist/ committed, then merge #3680 before #3681 · Owner: @marcelfolaron / @broskees

Automated maintainer first-response (advisory only — not an approval; merge authority stays with @marcelfolaron / @broskees). First review, at open (gloriafolaron, created 2026-07-21, 11 commits cherry-picked off integrate/report-chrome).

1. Intent: The reusable chrome/nav design system + its first application on the Tasks board — pageheader becomes a glass card, the global .tabs horizontal nav gets a gradient bar with a WCAG-driven conditional scrim (--nav-scrim computed in header.blade.php from accent luminance: 0 for dark accents, 0.22 for light, so tab contrast stays AA on every theme), an extracted <x-global::subjectSwitcher> breadcrumb, plus a Tasks board one-line header with a new BoardSummary model + getBoardSummary() service (32 tests).

2. Change requests:

  1. ⚠ CR Documentation #1 — this touches global nav CSS, so the risk is cross-theme/cross-page regression, not correctness. nav.css, structure, style, dropdowns, and header.blade.php are app-wide surfaces. The --nav-scrim luminance branch is the thing to verify by eye: confirm AA contrast on a light accent (scrim 0.22 path) and a dark accent (scrim 0 path), on both a page with .tabs-actions and one without (the :not(:has(.tabs-actions)) hug rule). A :has() selector also silently no-ops on older engines — confirm the intended browser baseline still renders acceptably without it.
  2. ⚠ CR Gitignore #2 — confirm no built dist/ is committed. PR body says "No dist/ committed — CI builds assets." Verify the diff carries only source (.css/.blade.php/PHP), not public/dist/*.min.css — a stray built artifact here would fight the mix pipeline and every other in-flight CSS PR.
  3. ◐ CR Create Email Notification Class (including user settings to opt out) #3 — merge-order with design(reports): mirror the nav treatment on the stakeholder deck (Option C) #3681. design(reports): mirror the nav treatment on the stakeholder deck (Option C) #3681 (deck nav mirror) has a soft dependency on this: it reads --nav-scrim and falls back to 0 standalone. Land design(chrome): nav-bar redesign — glass pageheader, gradient tabs, Tasks board summary #3680 first so the deck gets the scrim on light themes; design(reports): mirror the nav treatment on the stakeholder deck (Option C) #3681 is safe in either order but this ordering is cleaner.
  4. BoardSummary as a model + service with 32 tests is the right shape for the board-summary metrics — behavior change carries tests, gate met on that half.

3. Acceptance checklist (must pass before merge):

  1. Visual pass: nav AA-contrast on light + dark accents, with and without .tabs-actions; :has() baseline confirmed (CR Documentation #1).
  2. Diff carries source only, no committed dist/ (CR Gitignore #2); merged before design(reports): mirror the nav treatment on the stakeholder deck (Option C) #3681 (CR Create Email Notification Class (including user settings to opt out) #3).
  3. CI green on this head: Pint + PHPStan + the 32 board-summary tests + npx mix compiles.

4. CI status: ⚠️ Couldn't read check-runs this cycle — confirm green in the UI. Given the CSS blast radius, treat a failed mix build or any skipped visual check as blocking.

Advisory only — not an approval, not marking ready, not merging. Merge authority stays with @marcelfolaron / @broskees.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants