feat(ui): align fonts, design tokens, navigation, and page shell with jentic-webapp#408
feat(ui): align fonts, design tokens, navigation, and page shell with jentic-webapp#408Manuel-Jentic wants to merge 7 commits into
Conversation
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>
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>
9025e1e to
b82fecb
Compare
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>
b82fecb to
2f76968
Compare
There was a problem hiding this comment.
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 Sansbody +Soraheading swap. - Navigation: removes sidebar + mobile drawer; adds
TopNavbar+NavTabs(with ResizeObserver overflow + framer-motion active pill),BottomNavbar(mobile),UserMenu, and a sharedMenuprimitive (useDismissable,MenuPanel,MenuSeparator,menuItemClass). - New
PageShell(wide/reading/formwidths) adopted by all 14 in-Layout pages; logo SVG paths ported from@jentic/frontend-ui'sSvgLogo; 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.
|
✅ [Orchestrator] @Killian-Jentic @Manuel-Jentic — Agent review concluded approved. Ready for human review. |
There was a problem hiding this comment.
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 docsis rendered withexternal, which makesAppLinkdefault totarget="_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>
| 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" |
Summary
First PR in the UI overhaul. Aligns Mini's chrome with
jentic-webapp/@jentic/frontend-themewhile 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:
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.chore(deps)— addsframer-motionfor the new layout-id morphing animation on active nav items.feat(logo)— ports the exact wordmark + glyph SVG paths from@jentic/frontend-ui'sSvgLogo; switches to explicitwidth/heightprops; "Mini" badge is nowaria-hiddento avoid SR double-announcement.feat(menu)— new shared overlay primitivecomponents/ui/Menu.tsx(useDismissablehook +MenuPanel+MenuSeparator+menuItemClass) used by every dropdown / sheet from now on. One source of truth for outside-click + Escape + inset-pill item styling.feat(navbar)— drops the left sidebar and mobile drawer. Replaces them with:TopNavbar(fixedh-12, logo + tabs + pending pill + user menu)NavTabs(ResizeObserver-driven overflow into a "More" dropdown; active pill morphs vialayoutId="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.tssingleNAV_ITEMSsource of truthAll three dropdowns consume the
Menuprimitive from PR feat: OAuth Broker management UI #4.feat(page-shell)— newPageShellcomponent withwide/reading/formwidth presets and aspacingoverride; migrates all 14 in-Layout pages so widths stop drifting page-by-page. Loading/error branches ofWorkflowDetailPage+AgentsPageare wrapped too so the layout no longer jumps between skeleton and content.docs(ui)—CLAUDE.md+BUILD_REPORT.mddescribe the new tokens, nav chrome, Menu primitive, and PageShell.What's NOT in this PR
--btn-primary-*/dropdown-*tokens byButton/DataTable/Menu— they're wired in@themefor future PRs to consume..text-link,.card-hover, scrollbar, and masks with the webapp's plugin utilities — intentionally simplified for Mini.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.tsx—wide/reading/formvariant className mapping, defaultspace-y-6, spacing override, className passthrough.Layout.test.tsxrewritten for the new chrome (top + bottom navbar, user menu interactions).Logo.test.tsxupdated to assert default77x24+ explicitwidth/heightprops.Manual:
docker compose up, theui_node_modulesDocker volume may be stale (noframer-motion); reviewers running Docker shoulddocker compose -f compose.yml -f compose.dev.yml down -v && uponce. Local dev server (npm run devinui/) is unaffected.Layout— verify content fillsmax-w-screen-2xlon lists/dashboards,max-w-4xlon detail pages,max-w-2xlon the credential form, with consistent vertical rhythm.Static checks
npx tsc --noEmit→ 0 errors.npx eslint src/→ 0 errors (149 pre-existingany/!warnings, unchanged by this PR).Made with Cursor