style(sidebar): add resource menu return row and refine dark UI details

- Add a main-menu row that reopens the resource menu, with a placeholder row
- Show only the other view as an icon in the view switcher
- Remove the active tile glow and the collapsed Search frame
- Tint the dark command palette purple and whiten focused-row details
- Soften the toast edge and give the toast icon tile 3D depth
- Document the decisions in DESIGN.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andras Bacsai
2026-10-22 14:02:18 +02:00
co-authored by Claude Opus 5.5
parent 59928035b7
commit dff2674466
16 changed files with 183 additions and 43 deletions
+79 -14
View File
@@ -35,8 +35,8 @@ change.
- **One accent.** Coolify purple in both themes (`#6b16ed`, hover `#7317ff`,
pressed `#5a12c7`, depth `#4a0fa3`, light text and icons `#a391ff`). Yellow
is only for real warnings. The dark sidebar, including the logo cell, is
purple-black (`#120e1a`) with a faint brand wash. The page canvas stays
neutral.
purple-black (`#120e1a`) with a faint brand wash. The dark command palette
has a light purple tint. The page canvas stays neutral.
- **Quiet text.** Sentence case. A clear text ladder: primary, dim, faint.
No em dashes in UI copy.
@@ -142,7 +142,8 @@ Rules for the tokens:
`--ui-menu-bg`, never a translucent value, or the content behind shows
through.
- Keep the page canvas, cards, menus, and logs pure neutral (chroma 0). The
dark sidebar and the logo cell above it are the tinted surface.
dark sidebar, the logo cell above it, and the dark command palette are the
tinted surfaces.
- **Dark lightness level.** The canvas is at 14.48% and the header panel at
19.13%. To
change the level, move every dark surface and border token by the same
@@ -232,7 +233,7 @@ Class names below are examples. Keep the values and structure.
border-color: #8a4dff;
background: linear-gradient(180deg, var(--ui-accent-top), var(--ui-accent-bottom));
--ui-depth-color: var(--ui-accent-edge);
box-shadow: 0 2px 0 var(--ui-accent-edge), 0 0 16px rgb(115 23 255 / 0.50);
box-shadow: 0 2px 0 var(--ui-accent-edge); /* no glow: the row clips it */
}
```
@@ -242,6 +243,17 @@ Class names below are examples. Keep the values and structure.
hidden until the row is hovered, focused, or current.
- Group rows under section labels (11px in light mode; 10.5px uppercase muted
lavender in dark mode), and separate groups with a 1px divider.
- Do not put a glow (a blurred `box-shadow`) on a tile. The current row clips
it, so it shows as a dim square behind the tile.
- In the collapsed sidebar, Search is a tile like the other items: no row fill
and no frame around it. Only the expanded sidebar shows Search as an input.
- A menu that replaces another menu in place starts with a back row: a
centered label and a 12px chevron on the side of the direction. The back row
points left (`‹ Dashboard`). The row that opens the resource menu again from
the main menu points right (`adonisjs ›`) and shows the resource name.
- Keep the menu items in the same place on every page. When a page has no
resource menu, keep an empty row of the same height where the resource row
would be.
### Card (outer box, title row, well)
@@ -357,6 +369,17 @@ Class names below are examples. Keep the values and structure.
- Grid view tiles of a collection are cards without a well. In dark mode they
are `--ui-panel`, the same as the header bar.
### View switcher
- A segmented control (`.view-toggle`) on the right of a collection toolbar.
- Table and grid buttons are icon-only (30px square, 14px icon). The name is in
`title` and `aria-label`.
- Show only the view that is not current. In table view the control shows the
grid icon; in grid view, the list icon. Do not show a selected pill for the
current view.
- Text links stay labelled: the `Canvas` link with its "Try it" badge, and the
`List view` link that is the only way back from a canvas.
### Status badge
```css
@@ -388,6 +411,22 @@ Class names below are examples. Keep the values and structure.
- Toasts are compact cards (max width 26rem) with a status icon tile, a title,
an optional description, and a dismiss button. They close after 4 seconds
and pause on hover.
- The toast edge blends into the toast background. In dark mode it is
`rgb(255 255 255 / 0.05)`, not the control border.
- The status icon tile is 3D, like a navigation tile: a 1px border and a 2px
bottom edge in the status color (`currentColor` at 22% and 30%).
### Command palette
- In dark mode the palette has a light brand tint: the shell is
`color-mix(in oklab, #6b16ed 10%, <elevated>)` with a
`rgb(163 145 255 / 0.22)` ring, and the results body is 6% purple with a
`rgb(163 145 255 / 0.12)` ring. The search icon is `#a391ff`. The
placeholder is muted lavender.
- On the selected (focused) row in dark mode, the parts on a purple fill are
white: the quick-command and type badges, the icon tile glyph (for example
the plus of a create row), and the chevron. Purple text on a purple fill is
not readable.
### Logs and code
@@ -468,8 +507,8 @@ The interface is compact and product-focused:
- outline Reicon glyphs through `<x-reicon>`;
- the Coolify purple brand accent in both themes (`#6b16ed`); yellow only for
real warnings, such as "Update available";
- the active sidebar item is a purple gradient tile with a soft glow. The row
itself stays transparent;
- the active sidebar item is a purple gradient tile with a 2px depth edge and
no glow. The row itself stays transparent;
- sentence-case labels and headings;
- never use the em dash (`—`) in UI copy. Prefer a period, colon, comma, or
ASCII hyphen (`-`) for empty cells and separators.
@@ -596,10 +635,10 @@ value. The dark ladder is `--color-app` 10, `--color-panel` 20,
`--coollabs-elevated` 22, `--coollabs-base` 28,
`--coollabs-recessed` 34 (sRGB, r=g=b), which reads as distinct surfaces.
Temperature: the page canvas, cards, menus, listboxes, modals, logs, and the
command palette stay **pure neutral gray** (r=g=b). Do not tint those. The
dark sidebar (`--color-sidebar`, `#120e1a`) is the exception: it carries a
faint brand-purple wash. The light page canvas uses `bg-neutral-50` (not
Temperature: the page canvas, cards, menus, listboxes, modals, and logs stay
**pure neutral gray** (r=g=b). Do not tint those. The dark sidebar
(`--color-sidebar`, `#120e1a`) and the dark command palette are the exceptions:
they carry a faint brand-purple wash. The light page canvas uses `bg-neutral-50` (not
`bg-gray-50`, which is faintly cool) so it matches the neutral cards and chrome.
The only intentional color is the purple brand accent. Yellow is for warnings.
- **Layer cards** use the outer box and inset well from section 5.
@@ -608,7 +647,10 @@ The only intentional color is the purple brand accent. Yellow is for warnings.
Nested pages keep the full trail (project, environment, resource).
- **Pagination** is hidden when there is only one page (both arrows disabled).
- **Project cards** spell out their counts: "1 environment · 35 resources".
- **The view switcher** shows every view. The current one is a purple pill.
- **The view switcher** (`x-canvas-view-toggle`,
`x-shared-variables.view-controls`) shows only the other view, as an icon
button: the grid icon in table view, the list icon in grid view. The labelled
`Canvas` link and the `List view` link on canvas pages keep their text.
- **Tables and collection tiles** outside a layer card use the
`rounded-xl border … dark:bg-white/[0.05]` shell plus the crisp
`--coollabs-hairline` ring; in light they are `bg-white` with the ring and
@@ -635,7 +677,13 @@ color. Row-hover states keep the lighter `dark:hover:bg-white/[0.025]`.
- With the default tile items (not Appearance → Navigation items → Flat), each
icon sits in a 30px 3D tile (16px icon, 6px padding, 1px border) in a 36px row.
- With tile items, the row has no fill: the current item shows through its
accent tile (purple light, yellow dark) and its brighter label.
purple accent tile and its brighter label. The dark active tile has a 2px
depth edge and no glow, because the row (`overflow: hidden`) clips a glow
into a square.
- In the collapsed desktop sidebar, Search is a tile like the other items. The
row fill is removed in `@layer utilities` (the button has a layered
`!important` fill utility that an unlayered rule cannot beat), and the dark
tile uses the same colors as the other tiles.
- With Flat items, active rows are rounded pills (`rounded-md`) with a solid
neutral selected fill (`bg-black/5` light, `bg-white/6` dark) and no accent
rail. Hover rows use the same radius. Do not use accent-tinted gradients on nav
@@ -662,6 +710,16 @@ in-flow resource heading, or legacy `.navbar-main` tabs to one resource type.
Keep route-derived active state in Blade/Livewire. Do not rely only on Alpine
state because it can disappear after polling or a Livewire morph.
From `xl`, the resource menu replaces the main menu in the main sidebar
(`x-main-nav-menu`, `registerMainNavContext()`, `components/navbar.blade.php`).
Its back row (`‹ Dashboard`, `‹ Servers`, `‹ Service`) shows the menu one depth
lower, without a page load. The main menu then starts with a row that opens the
deepest resource menu again: the resource name with a right chevron
(`adonisjs ›`). Pass the name as `name` to `registerMainNavContext()` or
`<x-main-nav-menu :name>`; without a name the row says "Resource menu". Pages
with no resource menu keep an invisible row of the same height, so the main
menu items do not move between pages.
The global topbar owns the current resource identity, its compact status
badges, configuration warnings (`#configuration-warning-hud-slot`), and the
resource actions (`#resource-action-hud-slot`). If a resource is missing from
@@ -1169,7 +1227,11 @@ top-anchored overlay:
- create rows use a neutral plus tile that only picks up the accent when the
row is focused;
- type pills and quickcommand chips stay recessed; they tint with the accent
only on the focused row;
only on the focused row. In dark mode the text of the focused row's chips,
its icon tile glyph, and its chevron are white;
- in dark mode the shell, header, and results body carry the brand-purple
tint from Part 1 (rules after the dark `.view-toggle` rules in
`resources/css/app.css`). Light mode stays neutral;
- neutral thin scrollbar inside the results body (not brand-colored);
- create-resource modals opened from the palette reuse the standard
`application-settings-section` layer-card shell.
@@ -1186,7 +1248,10 @@ server → destination → project → environment create flow.
Current toast behavior:
- compact layered card, maximum width 26rem;
- Reicon status tile for success, info, warning, danger, or default;
- Reicon status tile for success, info, warning, danger, or default. The tile
(`.toast-icon`) is 3D: a border and a 2px bottom edge in the status color;
- a faint edge (`.toast-card`, dark `rgb(255 255 255 / 0.05)`) that blends into
the toast background;
- title plus optional description;
- dismiss and copy-details actions;
- normally up to four stacked notifications, without evicting persistent notices;
+3 -2
View File
@@ -4626,11 +4626,12 @@ function wireNavigate(): string
* Registers a resource menu that the main sidebar shows in place of its main menu on wide screens
* (see components/navbar.blade.php). The menu teleports itself into #main-nav-context-{depth}.
* Back on a menu shows the menu one depth lower, and Back at depth 0 shows the main menu.
* The main menu then shows a row (labelled with $name) that opens the deepest resource menu again.
*/
function registerMainNavContext(int $depth, string $label, string $backTitle): void
function registerMainNavContext(int $depth, string $label, string $backTitle, ?string $name = null): void
{
$levels = request()->attributes->get('mainNavContext', []);
$levels[$depth] = ['label' => $label, 'backTitle' => $backTitle];
$levels[$depth] = ['label' => $label, 'backTitle' => $backTitle, 'name' => $name];
ksort($levels);
request()->attributes->set('mainNavContext', $levels);
+58 -1
View File
@@ -5576,6 +5576,12 @@ html:where(:not([data-nav-items="flat"])) .menu-subitem {
background-color: oklch(100% 0 0 / 0.07) !important;
}
/* The collapsed desktop rail shows Search as a tile only, without the row fill. */
html .app-sidebar nav.sidebar-collapsed .menu-search.menu-item,
html .app-sidebar nav.sidebar-collapsed .menu-search.menu-item:hover {
background-color: transparent !important;
}
@media (max-width: 1023.98px) {
html:where(:not([data-nav-items="flat"])) nav.sidebar-collapsed .menu-search.menu-item {
background-color: var(--color-neutral-100) !important;
@@ -5599,7 +5605,7 @@ html.dark:where(:not([data-nav-items="flat"])) :is(.menu-item-active, .menu-subi
border-color: #8a4dff !important;
background: linear-gradient(180deg, var(--color-brand-hover) 0%, var(--color-brand-press) 100%) !important;
--button-depth-color: var(--color-brand-depth) !important;
box-shadow: 0 2px 0 var(--color-brand-depth), 0 0 16px rgb(115 23 255 / 0.50) !important;
box-shadow: 0 2px 0 var(--color-brand-depth) !important;
}
html.dark:where(:not([data-nav-items="flat"])) :is(.menu-item-active, .menu-subitem-active):active .menu-item-icon:not(.text-error) {
@@ -6040,6 +6046,19 @@ html.dark :is(.app-sidebar, .mobile-sidebar-sheet) .menu-search kbd {
border-color: rgb(163 145 255 / 0.18) !important;
}
/* The collapsed rail shows Search as a tile like the other items: no row frame (the fill is
removed in the utilities layer above), the dark tile. */
html.dark .app-sidebar nav.sidebar-collapsed .menu-search {
box-shadow: none !important;
}
html.dark:where(:not([data-nav-items="flat"])) .app-sidebar nav.sidebar-collapsed .menu-search.menu-item .menu-item-icon {
border-color: rgb(163 145 255 / 0.13);
background: rgb(107 22 237 / 0.07);
color: #b9adff;
--button-depth-color: rgb(255 255 255 / 0.05);
}
html.dark :is(.app-sidebar, .mobile-sidebar-sheet) nav:not(.sidebar-collapsed) .nav-section {
font-family: var(--font-mono);
font-size: 10.5px;
@@ -6118,6 +6137,44 @@ html.dark .view-toggle .bg-coollabs {
color: #d9d0ff;
}
/* Toasts: a faint edge that blends into the toast background. */
html.dark .toast-card {
box-shadow: 0 0 0 1px rgb(255 255 255 / 0.05), var(--shadow-dropdown);
}
/* Toast icon tile: the 3D depth of the sidebar tiles, tinted with the icon color of the toast type. */
.toast-icon {
border: 1px solid color-mix(in oklab, currentColor 22%, transparent);
box-shadow: 0 2px 0 color-mix(in oklab, currentColor 30%, transparent);
}
/* Command palette: the purple brand tint of the dark shell. */
html.dark .command-palette {
background: color-mix(in oklab, #6b16ed 10%, var(--coollabs-elevated));
box-shadow: 0 0 0 1px rgb(163 145 255 / 0.22), var(--shadow-dropdown);
}
html.dark .command-palette-header {
background: transparent;
}
html.dark .command-palette-header-icon:not(.is-loading) {
color: var(--color-brand-light);
}
html.dark .command-palette-input::placeholder {
color: oklch(72% 0.05 300);
}
html.dark .command-palette-body {
background: color-mix(in oklab, #6b16ed 6%, var(--coollabs-base));
box-shadow: 0 0 0 1px rgb(163 145 255 / 0.12);
}
html.dark .command-palette-item:is(:focus, :focus-visible) :is(.command-palette-quickcommand, .command-palette-type-badge, .command-palette-item-chevron, .command-palette-item-icon) {
color: #fff;
}
html.dark .resource-glyph {
border-color: rgb(163 145 255 / 0.20);
background: rgb(107 22 237 / 0.10);
@@ -254,7 +254,7 @@
@php
// The main sidebar shows this menu on wide screens (see components/navbar.blade.php).
registerMainNavContext(0, 'Dashboard', 'Back to the main menu');
registerMainNavContext(0, 'Dashboard', 'Back to the main menu', $application->name);
@endphp
{{-- Rendered twice: in the page (below xl), and teleported into the main sidebar (xl and up).
@@ -6,8 +6,8 @@
// One list view (no table / grid choice): nothing to switch to on the list page.
$singleList = count($views) === 1;
@endphp
{{-- Table / grid / canvas switcher. On the list page, table and grid switch the
parent's Alpine viewMode; the canvas page shows one "List view" link back
{{-- Table / grid / canvas switcher. On the list page, an icon button switches the
parent's Alpine viewMode to the other view; the canvas page shows one "List view" link back
(?view= makes the list the remembered view again, see RemembersCanvasView).
Pages with one list view pass :views="['list' => [...]]". Without a
canvasUrl it is only a table / grid switcher. --}}
@@ -22,16 +22,13 @@
List view
</a>
@else
{{-- Every view stays visible. The current one is the selected pill. --}}
{{-- Only the views that are not current show, as icons: the button switches to that view. --}}
@unless ($singleList)
@foreach ($views as $mode => [$label, $icon])
<button type="button" x-on:click="setViewMode(@js($mode))"
class="{{ $canvasItem }}"
:class="viewMode === @js($mode) ? 'is-selected' : @js($idle)"
:aria-pressed="(viewMode === @js($mode)).toString()"
title="{{ $label }}">
<button type="button" x-on:click="setViewMode(@js($mode))" x-show="viewMode !== @js($mode)" x-cloak
class="{{ $canvasItem }} {{ $idle }} w-7.5 justify-center px-0!"
title="{{ $label }}" aria-label="{{ $label }}">
<x-reicon :name="$icon" class="size-3.5" />
{{ $label }}
</button>
@endforeach
@endunless
@@ -64,7 +64,7 @@
@php
// The main sidebar shows this menu on wide screens (see components/navbar.blade.php).
registerMainNavContext(0, 'Dashboard', 'Back to the main menu');
registerMainNavContext(0, 'Dashboard', 'Back to the main menu', $database->name);
@endphp
{{-- Rendered twice: in the page (below xl), and teleported into the main sidebar (xl and up).
@@ -1,7 +1,7 @@
@props(['backLabel', 'backTitle' => 'Back to the main menu', 'depth' => 0, 'navOnly' => false])
@props(['backLabel', 'backTitle' => 'Back to the main menu', 'depth' => 0, 'name' => null, 'navOnly' => false])
@php
// The main sidebar replaces its menu with this one on wide screens (see components/navbar.blade.php).
registerMainNavContext($depth, $backLabel, $backTitle);
registerMainNavContext($depth, $backLabel, $backTitle, $name);
@endphp
{{-- A page menu, rendered twice: in the page (below xl, CSS hides it on xl), and teleported into the
main sidebar (xl and up). The slot must not contain Livewire components (they would render twice).
@@ -178,6 +178,7 @@
// components/main-nav-menu.blade.php). Each one teleports itself into #main-nav-context-{depth};
// only the desktop sidebar has the slots.
$navContext = $desktop ? request()->attributes->get('mainNavContext', []) : [];
$navContextName = collect($navContext)->pluck('name')->filter()->last() ?? 'Resource menu';
@endphp
{{-- Wide screens on a resource page: the deepest resource menu replaces the main menu. Back shows the
menu one depth lower, and then the main menu (or its submenu), without leaving the page. --}}
@@ -255,6 +256,25 @@
@endforeach
<li x-show="!submenu" @if ($activeSubmenu) x-cloak @endif @class(['contents', 'xl:hidden' => $navContext]) :class="{ 'xl:hidden': navLevel !== -1 }">
<ul role="list" class="flex flex-col gap-y-0.5" :class="navSwap && 'nav-swap-' + navSwap">
@if ($navContext)
{{-- Wide screens: the main menu of a resource page opens the deepest resource menu again.
Other pages keep an empty row, so the menu items do not move between pages. --}}
<li class="mb-1 hidden xl:block">
<button type="button" @click="navLevel = {{ array_key_last($navContext) }}; navSwap = 'forward'"
title="Back to the {{ $navContextName }} menu" aria-label="Back to the {{ $navContextName }} menu"
class="menu-item menu-back bg-transparent! hover:bg-neutral-100! dark:hover:bg-white/[0.06]!" :class="collapsed && 'lg:justify-center lg:px-0'">
<span class="flex-1 truncate pl-6 text-center font-medium text-black dark:text-fg"
:class="collapsed && 'lg:hidden'">{{ $navContextName }}</span>
<span class="menu-back-icon flex shrink-0 items-center justify-center">
<x-reicon name="chevron-down" class="size-3 -rotate-90 text-coollabs dark:text-fg-dim" />
</span>
</button>
</li>
@elseif ($desktop)
<li class="mb-1 hidden xl:block" aria-hidden="true">
<span class="menu-item menu-back invisible"><span class="menu-back-icon"></span><span class="font-medium">&nbsp;</span></span>
</li>
@endif
@foreach ($navSections as $sectionLabel => $sectionItems)
{{-- The first section needs no title. --}}
@unless ($loop->first)
@@ -220,7 +220,7 @@
@php
// The main sidebar shows this menu on wide screens (see components/navbar.blade.php).
registerMainNavContext(0, 'Servers', 'Back to the main menu');
registerMainNavContext(0, 'Servers', 'Back to the main menu', $server->name);
@endphp
{{-- Rendered twice: in the page (below xl), and teleported into the main sidebar (xl and up).
@@ -51,7 +51,7 @@
->filter(fn ($items) => $items->isNotEmpty());
@endphp
<x-main-nav-menu back-label="Dashboard" :nav-only="$navOnly"
<x-main-nav-menu back-label="Dashboard" :name="$service->name" :nav-only="$navOnly"
data-settings-search-items="{{ json_encode(settingsSearchItems($groupedItems, $serviceRouteParameters)) }}">
<nav aria-label="Service settings"
class="grid grid-cols-2 gap-0.5 border-y border-neutral-200 py-3 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-1 xl:border-y-0 xl:py-0 dark:border-white/[0.06]">
@@ -13,16 +13,14 @@
</button>
</div>
{{-- Every view stays visible. The current one is the selected pill. --}}
{{-- Only the view that is not current shows, as an icon: the button switches to that view. --}}
<div class="view-toggle w-fit">
@foreach (['list' => ['List view', 'unordered-list'], 'grid' => ['Grid view', 'grid']] as $mode => [$viewLabel, $icon])
<button type="button" @click="viewMode = '{{ $mode }}'; localStorage.setItem('{{ $storageKey }}', '{{ $mode }}')"
title="{{ $viewLabel }}"
class="relative flex h-7.5 items-center gap-1.5 rounded-md px-2.5 text-[12px] font-medium transition-colors"
:class="viewMode === '{{ $mode }}' ? 'is-selected' : 'text-neutral-400 hover:bg-neutral-100 hover:text-black dark:hover:text-white'"
:aria-pressed="(viewMode === '{{ $mode }}').toString()">
x-show="viewMode !== '{{ $mode }}'" x-cloak
title="{{ $viewLabel }}" aria-label="{{ $viewLabel }}"
class="relative flex h-7.5 w-7.5 items-center justify-center rounded-md text-neutral-400 transition-colors hover:bg-neutral-100 hover:text-black dark:hover:text-white">
<x-reicon :name="$icon" class="size-3.5" />
{{ $viewLabel }}
</button>
@endforeach
</div>
+2 -2
View File
@@ -103,11 +103,11 @@
x-transition:leave-start="translate-y-0 opacity-100"
x-transition:leave-end="translate-y-1 opacity-0"
@mouseenter="pauseToast(toast)" @mouseleave="resumeToast(toast)"
class="surface-popover relative flex w-full items-start rounded-lg group"
class="surface-popover toast-card relative flex w-full items-start rounded-lg group"
:class="{ 'p-3.5 pr-20': !toast.html, 'p-0': toast.html }">
<template x-if="!toast.html">
<div class="flex min-w-0 items-start gap-3">
<div class="flex size-8 shrink-0 items-center justify-center rounded-lg"
<div class="toast-icon flex size-8 shrink-0 items-center justify-center rounded-lg"
:class="{
'bg-emerald-100 text-emerald-700 dark:bg-emerald-500/10 dark:text-emerald-400': toast.type === 'success',
'bg-coollabs/10 text-coollabs dark:bg-warning/10 dark:text-warning': toast.type === 'info',
@@ -22,7 +22,7 @@
])->filter();
@endphp
<x-main-nav-menu back-label="Destinations" back-title="Back to Integrations"
<x-main-nav-menu back-label="Destinations" back-title="Back to Integrations" :name="$destination->name"
data-settings-search-items="{{ json_encode(settingsSearchItems(['Destination' => $destinationMenuItems], $destinationRouteParameters)) }}">
<nav aria-label="Destination settings"
class="grid grid-cols-2 gap-0.5 border-y border-neutral-200 py-3 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-1 xl:border-y-0 xl:py-0 dark:border-white/[0.06]">
@@ -47,7 +47,7 @@
@if ($showSettingsSidebar)
<section class="application-settings-workspace mt-4 w-full max-w-none lg:mt-0">
<div class="grid min-w-0 gap-8 xl:grid-cols-[210px_minmax(0,1fr)] xl:gap-8">
<x-main-nav-menu back-label="Sources" back-title="Back to Integrations"
<x-main-nav-menu back-label="Sources" back-title="Back to Integrations" :name="$github_app->name ?: 'GitHub App'"
data-settings-search-items="{{ json_encode(settingsSearchItems(['GitHub App' => $settingsMenuItems], $githubAppRouteParameters)) }}">
<nav aria-label="GitHub App settings"
class="grid grid-cols-2 gap-0.5 border-y border-neutral-200 py-3 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-1 xl:border-y-0 xl:py-0 dark:border-white/[0.06]">
@@ -36,7 +36,7 @@
@if ($showSettingsSidebar)
<section class="application-settings-workspace mt-4 w-full max-w-none lg:mt-0">
<div class="grid min-w-0 gap-8 xl:grid-cols-[210px_minmax(0,1fr)] xl:gap-8">
<x-main-nav-menu back-label="S3 Storage" back-title="Back to Integrations"
<x-main-nav-menu back-label="S3 Storage" back-title="Back to Integrations" :name="$storage->name"
data-settings-search-items="{{ json_encode(settingsSearchItems(['S3 storage' => $settingsMenuItems], $storageRouteParameters)) }}">
<nav aria-label="S3 storage settings"
class="grid grid-cols-2 gap-0.5 border-y border-neutral-200 py-3 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-1 xl:border-y-0 xl:py-0 dark:border-white/[0.06]">
@@ -58,6 +58,7 @@ test('an application page teleports its menu into the main sidebar ', function (
->toContain('x-teleport="#main-nav-context-0"')
->toContain('application-settings-navigation in-main-nav')
->toMatch('#<button type="button" @click="navLevel = -1; navSwap = \'back\'"\s+title="Back to the main menu"#')
->toMatch('#@click="navLevel = 0; navSwap = \'forward\'"\s+title="Back to the Navbar Menu App menu"#')
->not->toContain('id="main-nav-context-1"')
->and(substr_count($html, 'id="main-nav-context-0"'))->toBe(1);
});
@@ -143,6 +144,7 @@ test('a database backup page keeps the database menu one back away in the main s
->toContain('x-teleport="#main-nav-context-1"')
->toMatch('#@click="navLevel = -1; navSwap = \'back\'"\s+title="Back to the main menu"#')
->toMatch('#@click="navLevel = 0; navSwap = \'back\'"\s+title="Back to the database menu"#')
->toMatch('#@click="navLevel = 1; navSwap = \'forward\'"\s+title="Back to the orders-db menu"#')
->and(substr_count($html, 'aria-label="Database settings"'))->toBe(1)
->and(substr_count($html, 'aria-label="Backup settings"'))->toBe(2);
});