Skip to content

feat(ui): align fonts, design tokens, navigation, and page shell with jentic-webapp#408

Open
Manuel-Jentic wants to merge 7 commits into
mainfrom
feature/ui-align-nav-fonts-tokens
Open

feat(ui): align fonts, design tokens, navigation, and page shell with jentic-webapp#408
Manuel-Jentic wants to merge 7 commits into
mainfrom
feature/ui-align-nav-fonts-tokens

Conversation

@Manuel-Jentic
Copy link
Copy Markdown
Contributor

Summary

First PR in the UI overhaul. Aligns Mini's chrome with jentic-webapp / @jentic/frontend-theme while introducing reusable foundations the rest of the overhaul will build on. Nothing here touches the Agent Centre work.

Seven atomic commits, each independently buildable + testable:

  1. refactor(theme) — palette rewritten as space-separated HSL triplets so Tailwind 4 opacity modifiers work; --font-sans → Nunito Sans, --font-heading → Sora; adds semantic token families (btn-primary-*, dropdown-*, nav-*, table-header-bg, card-border, etc.) ready for future component adoption.
  2. chore(deps) — adds framer-motion for the new layout-id morphing animation on active nav items.
  3. feat(logo) — ports the exact wordmark + glyph SVG paths from @jentic/frontend-ui's SvgLogo; switches to explicit width/height props; "Mini" badge is now aria-hidden to avoid SR double-announcement.
  4. feat(menu) — new shared overlay primitive components/ui/Menu.tsx (useDismissable hook + MenuPanel + MenuSeparator + menuItemClass) used by every dropdown / sheet from now on. One source of truth for outside-click + Escape + inset-pill item styling.
  5. feat(navbar) — drops the left sidebar and mobile drawer. Replaces them with:
    • TopNavbar (fixed h-12, logo + tabs + pending pill + user menu)
    • NavTabs (ResizeObserver-driven overflow into a "More" dropdown; active pill morphs via layoutId="activeNavTab" spring matching webapp)
    • BottomNavbar (md:hidden; 4 tiles + More sheet; Escape/backdrop dismiss)
    • UserMenu (3 clear zones: identity header → external links → destructive Log out; every item is an inset rounded pill)
    • navbar.constants.ts single NAV_ITEMS source of truth
      All three dropdowns consume the Menu primitive from PR feat: OAuth Broker management UI #4.
  6. feat(page-shell) — new PageShell component with wide / reading / form width presets and a spacing override; migrates all 14 in-Layout pages so widths stop drifting page-by-page. Loading/error branches of WorkflowDetailPage + AgentsPage are wrapped too so the layout no longer jumps between skeleton and content.
  7. docs(ui)CLAUDE.md + BUILD_REPORT.md describe the new tokens, nav chrome, Menu primitive, and PageShell.

What's NOT in this PR

  • Agent Centre (notification/control panel) work — separate branch.
  • Adoption of the new --btn-primary-* / dropdown-* tokens by Button / DataTable / Menu — they're wired in @theme for future PRs to consume.
  • Visual parity for .text-link, .card-hover, scrollbar, and masks with the webapp's plugin utilities — intentionally simplified for Mini.
  • BottomNavbar pill inset tuning to match webapp pixel-for-pixel — kept Mini's denser tiles.

Test plan

Automated (all run by Vitest browser harness on Chromium via npm run test):

  • Menu.test.tsx — outside-click, inside-click ignored, Escape, idle vs active classNames, align prop, MenuSeparator.
  • NavTabs.test.tsx — every label rendered, active route styling, overflow into "More" when constrained, outside-click + item-click both close the dropdown.
  • UserMenu.test.tsx — opens on avatar click, Escape closes, outside-click closes, version displayed above API docs link (the specific ordering requested), update banner shown when newer version, Log out is an inset pill.
  • BottomNavbar.test.tsx — primary tiles + More tile render, More opens the sheet, overflow items appear, close button works, active tile is highlighted.
  • PageShell.test.tsxwide / reading / form variant className mapping, default space-y-6, spacing override, className passthrough.
  • Layout.test.tsx rewritten for the new chrome (top + bottom navbar, user menu interactions).
  • Logo.test.tsx updated to assert default 77x24 + explicit width / height props.

Manual:

  • On docker compose up, the ui_node_modules Docker volume may be stale (no framer-motion); reviewers running Docker should docker compose -f compose.yml -f compose.dev.yml down -v && up once. Local dev server (npm run dev in ui/) is unaffected.
  • Resize the viewport — verify the More dropdown anchors next to the last visible tab and never clips off-screen.
  • Open the user menu, hover Log out — confirm the hover background is fully rounded (no flat corners against the panel edge).
  • Visit every route under Layout — verify content fills max-w-screen-2xl on lists/dashboards, max-w-4xl on detail pages, max-w-2xl on the credential form, with consistent vertical rhythm.

Static checks

  • npx tsc --noEmit → 0 errors.
  • npx eslint src/ → 0 errors (149 pre-existing any / ! warnings, unchanged by this PR).

Made with Cursor

Manuel-Jentic and others added 2 commits May 18, 2026 13:04
Mini's theme now mirrors the @jentic/frontend-theme conventions:

- All HSL palette values are stored as space-separated triplets in :root
  so Tailwind 4 utility opacity modifiers (e.g. bg-primary/10) work
  through the hsl(var(--x)) mapping in `@theme inline`.
- Font roles flip to match the webapp: Sora becomes --font-heading,
  Nunito Sans becomes --font-sans, Geist Mono stays --font-mono.
- Adds semantic token families ready for adoption by Button, DataTable,
  and dropdown chrome: btn-primary-*, btn-secondary-*, table-header-bg,
  card-border, dropdown-*, nav-text, nav-hover-bg.
- Adds global scrollbar styling and small utility classes (.text-link,
  .card-hover, .mask-fade-x).

No component code changes here — the new tokens are picked up by the
navbar / page-shell / Menu work in the follow-up commits on this branch.

Co-authored-by: Cursor <cursoragent@cursor.com>
The new NavTabs / BottomNavbar use `motion.span` with `layoutId` so the
active tab/tile pill morphs between routes with a spring transition that
matches jentic-webapp (stiffness 500, damping 35). The package is added
as a runtime dependency since the chrome is shipped to users.

Co-authored-by: Cursor <cursoragent@cursor.com>
@Manuel-Jentic Manuel-Jentic self-assigned this May 18, 2026
@Manuel-Jentic Manuel-Jentic added documentation Improvements or additions to documentation enhancement New feature or request labels May 18, 2026
Manuel-Jentic and others added 2 commits May 18, 2026 13:21
JenticLogo now uses the exact wordmark + glyph SVG paths from
@jentic/frontend-ui's SvgLogo, with a Mini-only badge for branding.

API changes:

- Replaces the previous `className`-driven sizing with explicit `width`
  and `height` props (defaulting to 77x24, the navbar size).
- The "Mini" badge is now `aria-hidden` because the closest accessible
  ancestor (e.g. TopNavbar's home link) already announces "Jentic Mini
  home"; the badge would just be screen-reader noise.

LoginPage passes proportional dimensions (128x40) so the wordmark
doesn't stretch when scaled up for the auth screen.

Tests are updated to assert the new default width/height instead of
the removed `h-10` className.

Co-authored-by: Cursor <cursoragent@cursor.com>
Mini's nav chrome was about to grow three near-identical copies of
"close on outside click + Escape + absolutely-positioned popover with
inset rounded items": NavTabs (More dropdown), UserMenu, and the mobile
BottomNavbar sheet. This commit lifts the shared chrome into a small
overlay primitive in `components/ui/Menu.tsx`:

- `useDismissable(open, onClose)` — wires `mousedown` outside + Escape
  into a returned ref. Listeners only attach while `open` is true.
- `MenuPanel` — the absolutely-positioned popover shell with the padded
  outer card (so item hovers never touch the border) and `align` prop.
- `MenuSeparator` — inset hairline used to group items into sections.
- `menuItemClass(active?)` — canonical item className. Consume from
  whatever element the caller needs (AppLink, Button, native button).

A focused test suite covers outside-click, inside-click, Escape, and
the active vs idle styling.

The next commit replaces NavTabs / UserMenu / BottomNavbar's ad-hoc
dropdown wiring with these primitives.

Co-authored-by: Cursor <cursoragent@cursor.com>
@Manuel-Jentic Manuel-Jentic force-pushed the feature/ui-align-nav-fonts-tokens branch from 9025e1e to b82fecb Compare May 18, 2026 12:21
@Manuel-Jentic Manuel-Jentic requested a review from char0n May 18, 2026 12:24
Manuel-Jentic and others added 3 commits May 18, 2026 14:27
Drops the old left sidebar and the mobile drawer in favour of a
webapp-style fixed top navbar on desktop and a fixed bottom navbar on
mobile. The new chrome is composed of small focused components:

- `TopNavbar` — fixed `h-12` header with the JenticLogo, a vertical
  divider, the desktop `NavTabs`, the pending-requests pill (when
  applicable), and the `UserMenu` avatar.
- `NavTabs` — horizontal tabs with a `ResizeObserver`-driven
  measurement loop that pushes anything that doesn't fit into a "More"
  dropdown. The active tab uses framer-motion `layoutId="activeNavTab"`
  so the pill morphs smoothly between routes. The strip shrink-wraps
  to its content so "More" always sits next to the last visible tab
  instead of floating against the avatar.
- `BottomNavbar` — `md:hidden` bottom bar with 4 primary tiles + a
  More tile that opens a bottom sheet for overflow. Active tile uses
  the same spring on `layoutId="activeBottomNavTab"`. Sheet dismisses
  on backdrop tap, close button, or Escape (via the shared
  `useDismissable` hook).
- `UserMenu` — avatar dropdown structured as three clear zones:
  identity header (username + version subtitle), external links (API
  docs / jentic.com / update banner when applicable), and the
  destructive Log out action. Every item is an inset rounded pill so
  hover backgrounds never touch the panel border, including Log out.
- `navbar.constants.ts` — single `NAV_ITEMS` source of truth shared
  by NavTabs and BottomNavbar.

`Layout.tsx` shrinks to just `<TopNavbar /> + <main> + <BottomNavbar />`
with `pt-12 pb-20 md:pb-12` to clear the fixed chrome.

All three dropdowns consume the `Menu` primitive added in the previous
commit, so outside-click + Escape behaviour is identical everywhere.

Tests:

- `Layout.test.tsx` rewritten to assert the new chrome (top + bottom
  nav, user menu opens on click, version inside menu, pending pill).
- New `NavTabs.test.tsx`, `UserMenu.test.tsx`, `BottomNavbar.test.tsx`
  covering primary rendering, the overflow More dropdown, the mobile
  sheet, active-route highlighting, and the user menu's identity
  ordering ("version above the API docs link", as requested).

Co-authored-by: Cursor <cursoragent@cursor.com>
Before this commit every routed page in Mini improvised its own outer
wrapper (max-w-4xl, 5xl, 6xl, or sometimes nothing at all), and
AgentsPage doubled up on padding by adding p-6 inside Layout's already-
padded main. The result: nothing felt aligned and most pages wasted
30-50% of a modern monitor.

This commit introduces `PageShell`, the single wrapper every in-Layout
page should reach for, with three width presets:

- `wide` (default, max-w-screen-2xl) — dashboards, lists, tables
- `reading` (max-w-4xl) — detail pages with prose / sequential sections
- `form` (max-w-2xl) — single-column forms

PageShell also owns the vertical rhythm (default `space-y-6`,
overridable via `spacing` prop) so the spacing system stops drifting
page-by-page.

All 14 in-Layout pages migrated:

- wide:    Dashboard, Toolkits, Agents, Catalog, Search, Workflows,
           Credentials, Traces, Jobs, WorkflowDetail (local-workflow
           branch with the ArazzoUI diagram, uses `space-y-4`).
- reading: WorkflowDetail (catalog-preview branch), ToolkitDetail,
           TraceDetail, JobDetail.
- form:    CredentialForm.

Auth-only routes (Login, Setup, Approval) sit outside Layout by design
and intentionally bypass PageShell — they keep their own centred card.

Loading and error branches of WorkflowDetailPage and the loading branch
of AgentsPage are wrapped too, so the layout no longer jumps width
when transitioning between skeleton and content.

Includes PageShell.test.tsx exercising the variant -> className
mapping, default spacing, and the className passthrough.

Co-authored-by: Cursor <cursoragent@cursor.com>
Updates the two long-form agent guides to reflect the UI alignment
work that landed on this branch:

- CLAUDE.md: refreshes the Tech-stack / Navigation chrome / UI
  Component Library sections to describe the new top + bottom nav
  components, the framer-motion `layoutId` morphing animation, the
  shared `Menu` primitive (useDismissable + MenuPanel + MenuSeparator
  + menuItemClass), and the canonical `PageShell` page container with
  its `wide` / `reading` / `form` variants.
- BUILD_REPORT.md: adds a "Page container — PageShell" entry and
  corrects the prior write-up that claimed NavTabs used CSS-only
  transitions (it now uses framer-motion spring for the active pill).

No code changes.

Co-authored-by: Cursor <cursoragent@cursor.com>
@Manuel-Jentic Manuel-Jentic force-pushed the feature/ui-align-nav-fonts-tokens branch from b82fecb to 2f76968 Compare May 18, 2026 13:27
@char0n char0n requested a review from Copilot May 18, 2026 14:54
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR is the first phase of a UI overhaul that re-aligns jentic-mini's chrome with jentic-webapp / @jentic/frontend-theme. It rewrites the design tokens, switches the navigation chrome from a sidebar/drawer to a top+bottom nav, introduces shared Menu and PageShell primitives, and migrates all in-Layout pages to use the new shell.

Changes:

  • Theme rewrite: HSL triplet tokens (Tailwind 4 opacity modifiers work), new btn-*/dropdown-*/nav-*/table-*/card-border-* token families; Nunito Sans body + Sora heading swap.
  • Navigation: removes sidebar + mobile drawer; adds TopNavbar + NavTabs (with ResizeObserver overflow + framer-motion active pill), BottomNavbar (mobile), UserMenu, and a shared Menu primitive (useDismissable, MenuPanel, MenuSeparator, menuItemClass).
  • New PageShell (wide/reading/form widths) adopted by all 14 in-Layout pages; logo SVG paths ported from @jentic/frontend-ui's SvgLogo; docs (CLAUDE.md, BUILD_REPORT.md) and tests updated.

Reviewed changes

Copilot reviewed 36 out of 37 changed files in this pull request and generated no comments.

Show a summary per file
File Description
ui/src/index.css HSL-triplet token rewrite + new token families + scrollbar/utility classes
ui/src/components/layout/Layout.tsx Strips sidebar/drawer; mounts TopNavbar + BottomNavbar
ui/src/components/layout/TopNavbar.tsx New fixed top bar (logo, NavTabs, pending pill, UserMenu)
ui/src/components/layout/NavTabs.tsx New horizontal tabs with ResizeObserver overflow + framer-motion
ui/src/components/layout/BottomNavbar.tsx New mobile bottom bar + overflow sheet
ui/src/components/layout/UserMenu.tsx New avatar dropdown (username, version, links, logout)
ui/src/components/layout/PageShell.tsx New shared page container with width/spacing presets
ui/src/components/layout/navbar.constants.ts New NAV_ITEMS source of truth
ui/src/components/ui/Menu.tsx New shared dismissable menu primitive
ui/src/components/ui/Logo.tsx Logo paths ported from frontend-ui; width/height props
ui/src/pages/*.tsx (14 files) Migrated to PageShell width variants
ui/package.json + lock Adds framer-motion@^12.38.0
ui/src/tests/components/*.test.tsx New tests for Menu, NavTabs, BottomNavbar, UserMenu, PageShell + rewrites for Layout, Logo
ui/e2e/docker/prefix-mount.spec.ts Updated to interact with new UserMenu instead of sidebar
ui/BUILD_REPORT.md, .claude/CLAUDE.md Documentation of new chrome, tokens, Menu, PageShell
Files not reviewed (1)
  • ui/package-lock.json: Language not supported

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

@Killian-Jentic Killian-Jentic added the ai-review Trigger AI review agent label May 18, 2026
Copy link
Copy Markdown

@jentic-harness jentic-harness Bot left a comment

Choose a reason for hiding this comment

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

[ReviewAgent] Approved

@jentic-harness jentic-harness Bot removed the ai-review Trigger AI review agent label May 18, 2026
@jentic-harness
Copy link
Copy Markdown

✅ [Orchestrator] @Killian-Jentic @Manuel-Jentic — Agent review concluded approved. Ready for human review.

Copy link
Copy Markdown

Copilot AI left a comment

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 36 out of 37 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • ui/package-lock.json: Language not supported
Comments suppressed due to low confidence (1)

ui/src/components/layout/UserMenu.tsx:89

  • API docs is rendered with external, which makes AppLink default to target="_blank". The visible text no longer indicates that it opens a new tab (the old sidebar used an explicit aria-label/title for this). Please add accessible hinting (e.g. aria-label/title like "API docs (opens in a new tab)"), or choose a same-tab navigation approach that still avoids basename double-application in prefix mounts.
					<AppLink
						href={apiUrl('/docs')}
						external
						role="menuitem"
						onClick={close}
						className={menuItemClass()}
					>
						<BookOpen className="h-4 w-4 shrink-0" />
						API docs
						<ExternalLink className="ml-auto h-3 w-3 shrink-0 opacity-60" />
					</AppLink>

Comment thread ui/src/index.css
Comment on lines +95 to +107
TOKENS — HSL triplets (no hsl() wrapper here).
Format: `H S% L%` so Tailwind can apply opacity modifiers: bg-primary/50.
All values match @jentic/frontend-theme/styles.css conventions.

Primary scale (higher = lighter — standard Tailwind convention):
--primary-50 → #F5F7F7 lightest
--primary-950 → #0E1A1D darkest
*/
:root {
/* Primary scale (lower = lighter, higher = darker — standard Tailwind convention).
Intentionally sparse: only shades used by semantic tokens are defined.
Intermediate stops (200, 400, 600, 800) can be added here if needed. */
--primary-50: hsl(180 11% 96%); /* #F5F7F7 - Lightest */
--primary-100: hsl(189 15% 91%); /* #E4EAEB */
--primary-300: hsl(183 29% 72%); /* #A3CACC */
--primary-500: hsl(185 18% 50%); /* #689296 */
--primary-700: hsl(186 28% 26%); /* #305256 */
--primary-850: hsl(192 38% 16%); /* #193238 */
--primary-900: hsl(192 36% 13%); /* #162629 - Card background */
--primary-950: hsl(192 35% 8%); /* #0E1A1D - Darkest */
/* ======================================================================
PRIMARY COLOR SCALE
Higher number = lighter (standard Tailwind convention).
====================================================================== */
aria-haspopup="menu"
aria-expanded={open}
aria-label="User menu"
className="bg-primary/80 text-primary-foreground hover:bg-primary/80 relative h-7 w-7 rounded-full p-0 text-xs font-semibold hover:opacity-80"
/>
<div
ref={sheetRef}
className="border-border bg-background pb-safe fixed inset-x-0 bottom-0 z-50 rounded-t-xl border-t md:hidden"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants