Skip to content

[6.x] Design updates - #19648

Open
brianjhanson wants to merge 166 commits into
6.xfrom
design-updates
Open

brianjhanson wants to merge 166 commits into
6.xfrom
design-updates

Conversation

@brianjhanson

@brianjhanson brianjhanson commented Sep 16, 2026

Copy link
Copy Markdown
Contributor
  • The page shell layout has been rearranged and cleaned up.
  • Pages can narrow their content column with contentMaxWidth and center it with centerContent. Settings and form pages now use both.
  • You can debug the slots on the page via a ?debug-slots query param. That will stick until you make another request to turn them off (?debug-slots=0)
  • The legacy CP stylesheet is no longer loaded on Inertia pages.

Navigation

  • The whole nav tree is built on the server and sent once. Element sources hang off Entries, Assets and Users, grouped under their headings.
  • The branch you're on expands in place, and every other branch opens in a flyout. Flyouts share hover timing and fit the space below them.
  • Selection marks the source you're on rather than the index beside it, and matches query strings as well as paths.
  • A collapse item sits at the bottom of the sidebar. When the sidebar floats on small screens, it layers above sticky page content.
  • Breadcrumbs repeat the secondary nav's trail. Each source crumb has a switcher that matches the nav, including on an entry's edit page.
  • The bare assets index redirects to its first source.

Element editor and indexes

  • ElementEditor handles full pages as well as slideouts.
  • Details tabs can be opened and closed from code, and each tab has a close button.
  • New Entry, New User and Upload files have been morved into the element index toolbar.

Components and tokens

  • craft-button has a flush option that offsets its padding, and spaces an icon from its label when the variant contains plain. Otherwise it does nothing.
  • Popovers open in the browser's top layer, so sticky page content can't cover them, and take the theme's text color.
  • Menu group headings are styled like nav headings.
  • New semantic z-index tokens (--c-layer-*) and Tailwind utilities for surfaces, border colors, spacing and layers. A Storybook page documents the utilities.
  • Quiet borders are translucent, with one variable to adjust the amount.
  • Admin tables run full width on a shared --c-container-padding gutter.
  • The CP language's writing direction is shared through Inertia and set as dir on the root element.

Housekeeping

  • Removed husky and lint-staged from the legacy package. Its prepare script ran husky outside the repository root, where it could have redirected git hooks away from vp's.

brianjhanson and others added 30 commits September 2, 2026 15:46
`craft-action-item` was the only one of its neighbours whose shadow root
never picked up the box-sizing reset from `styles/host.styles.ts`, and a
shadow root doesn't inherit the page's. So `.action-item` was content-box:
its `width: 100%` sat inside its own inline padding and overflowed whatever
contained it by twice the spacing. In a menu that's past `.popover-pane`'s
max-width, which `overflow: auto` then drew as a scrollbar.

`craft-nav-item` never showed it because its host isn't `display: contents`
and its inner box isn't `width: 100%`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AhkE6EKcrbrFvL1WZ22A9z
Bound per item, the handler only ever covered the items present when the
overlay was set up, so anything a consumer added afterwards stayed open on
click. Slot-based mode is exactly where that happens — a framework
rendering the list swaps items in long after setup.

`event.target` retargets to the `craft-action-item` host on its way out of
the shadow root, so `closest()` still finds the item.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AhkE6EKcrbrFvL1WZ22A9z
A nav entry, a breadcrumb and a menu item are the same thing drawn three
ways, but they were three shapes: a `NavItem` class, hand-written crumb
arrays, and hand-written action-menu arrays. They spelled the same field
differently — `NavItem.url` against the crumbs' `href` — and supported
different subsets of the same ideas, which is how the breadcrumb switcher
came to lose the groups the sources sidebar shows.

`Cp\Data\ActionItem` carries the whole vocabulary and `NavItem` extends it
without adding anything, keeping the name the navigation and plugins use.
Everything is optional: a crumb often has no link, a group heading isn't a
destination, and `NavItem.url` being required had already forced a `'#'`
placeholder in `EditUserScreens`.

`href` is the spelling, since it maps straight onto HTML. `url` stays a
real alias rather than a break — a setter, a getter, and a constructor key
— because plugins return these from `getCpNavItem()` and the Twig-rendered
screens read `item.url`, where a Component throws on an unknown property
instead of returning null. Only `href` is serialized.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AhkE6EKcrbrFvL1WZ22A9z
Crumbs were loose arrays in 33 files, which is how two of them came to be
keyed `url` while `Breadcrumbs.vue` reads `href` — nothing normalized
between the two, so the Utilities and Asset Transformers screens rendered
their first crumbs as plain text that led nowhere. Naming the shape settles
it; `CpScreenResponse::crumbs()` still takes arrays and configures them.

The source switcher now keeps the headings the sources sidebar shows,
grouping the sources under them rather than flattening into an ungrouped
run beside a grouped list. `ContentIndexControllerTest` pins the two
against each other by outline so they can't drift apart again.

`UserEditViewModel` loses a hop that re-resolved a `url` key which had
stopped existing — a no-op for core and quietly wrong for anything else.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AhkE6EKcrbrFvL1WZ22A9z
`utilities/Show.vue` filled `SecondaryNav`'s default slot with its own
markup and passed no items. The nav's collapsed action menu is built from
the items, so below the large breakpoint it listed nothing at all.

The controller sends the nav as data now and the page draws none of it,
which also means the breadcrumbs pick the current utility up with a
switcher. Its crumbs move to `ActionItem` with the rest, and its first
crumb gets the `t()` it was missing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AhkE6EKcrbrFvL1WZ22A9z
`ActionList` turns `ActionItem` descriptors into real controls, as buttons
where there's room and as menu items where there isn't. `craft-button` and
`craft-action-item` both take the same declarative action/feedback
primitives, so one descriptor behaves the same in either shape.

`ActionMenu` now renders through it rather than handing `craft-action-menu`
an array to build itself. That's what lets a link action be a `CpLink` and
make an Inertia visit — the element's own `href` renders a plain anchor in
its shadow root, so every link in every menu was a full page load. Link
actions that should stay that way say `external`, which is why
`NewEntryButton` sets it: its own primary button does `window.location`.

Two details the switch turned up. The menu's content wrapper is ours on
purpose: `craft-popover` auto-wraps unslotted children into a container it
makes once and appends, so anything Vue added afterwards landed outside it
and never reached the slot. And binding `:variant="undefined"` doesn't mean
"leave it alone" — Vue assigns it as a property and Lit reflects the
result, so an unset variant became `variant=""` instead of the element's
own default; only set props are passed now.

The tests that broke were asserting on `craft-action-menu.actions` — the
plumbing rather than the output — and now read the rendered items.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AhkE6EKcrbrFvL1WZ22A9z
`subnav-actions` was a slot, so a page filling it had to write the control
twice — once as a button for the expanded nav, once as an item for the
menu the nav collapses into. They're one thing, so they're described once
now and travel through `useAppLayout`, which already carries action
descriptors and already handles reactivity, per-navigation reset and the
slideout scoping.

The nav's own items feed the collapsed menu through the same mapper, so a
group survives as a heading over its children rather than as a link to the
`#` its URL usually is. Items carry their icons and badges through both
renderings, and the collapsed menu honours `external`, which it ignored.

Nav and controls are one list rather than two, so the checkmark gutter is
decided once — split up, the labels wouldn't line up across the rule.

The slot stays for anything descriptors can't express.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AhkE6EKcrbrFvL1WZ22A9z
The nav already says where you are, but only while it's on screen — and it
collapses below the large breakpoint. The trail joins the crumbs now, each
level carrying the same switcher menu an element index's source crumb has,
so you can move between siblings without opening the nav.

On an index screen the derived crumb replaces the one the controller wrote
by hand, since they name the same thing; where the trail ends somewhere
else — a user's account screens end in a chip for the user — it adds the
level that was never named at all.

Both the switcher and the nav's own collapsed menu come from
`navItemActions`, so they show the same items, the same groups and the same
mark on the current one. They were drifting apart precisely because they
were two renderings.

`BreadcrumbItem` moves to `@/common/types` so plain modules can build
crumbs; a `.ts` file can't import a type out of an SFC.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AhkE6EKcrbrFvL1WZ22A9z
Breadcrumbs move onto their own row under the toolbar rather than sitting
beside the sidebar toggle, and `SystemInfo` moves out of the top bar into a
sidebar header alongside a close button — so the toggle no longer lives in
the sidebar footer.

Below the large breakpoint the top bar's toggle becomes a `bars` button and
`SystemInfo` drops out.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AhkE6EKcrbrFvL1WZ22A9z
The rename to `href` missed the expanded nav list, which kept binding
`item.url` — so every item in the docked sidebar linked nowhere while the
collapsed menu, which goes through `navItemActions`, was fine.

Nothing caught it: the expanded list is its own rendering and no test
asserted its links, and `craft-nav-item.href` is a non-reflecting property,
so it fails quietly rather than rendering an obviously empty attribute. The
new test reads the property and fails against the old binding.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AhkE6EKcrbrFvL1WZ22A9z
The sources sidebar was its own component with its own markup, so it never
collapsed into an action menu the way every other nav does, and its list and
the breadcrumb switcher beside it were two derivations of the same thing.

The behaviour that made it its own component moves to
`useElementSourceActions`, which describes the sources as action items
carrying it: selecting one is a partial visit that leaves the source list
and publishable sections alone and keeps the list's scroll and state, it
activates before the round-trip so the selection doesn't lag the pointer, it
prefetches on mousedown, it hands off to an `indexVisitor` where there is
one, and folder sources keep the attributes an asset drag needs.

`SecondaryNav` now takes those descriptors rather than `NavItem[]` — one
array rendered as a list and as the menu it collapses into, with `NavEntry`
drawing an entry either way. Callers with a `NavItem[]` map it at the
boundary with `navItemActions()`.

`ElementSources` stays for the selector modal, which has no page chrome to
hang a nav on, but renders the same descriptors over the same composable
instead of repeating the logic. `Customize sources` becomes a nav action.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AhkE6EKcrbrFvL1WZ22A9z
`useElementIndexPage` hands back a reactive object, not refs — every other
reader says `elementIndex.viewModes`, not `.value`. The sources getter I
added reached through `.value`, so it read `undefined.sources` and threw in
setup, leaving the page as nothing behind the layout's own bars.

Typecheck let it past, so the guard is a test that mounts the page with the
reactive shape the composable really returns and asserts the sources reach
the nav. It fails against the `.value` access.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AhkE6EKcrbrFvL1WZ22A9z
Same miss as the secondary nav: the rename to `href` left `MainNav` binding
`item.url`, so every top-level and subnav item rendered with no link and
resolved to the current page. Clicking one reloaded where you already were.

The test fixtures still said `url` too, and nothing asserted an href, so it
passed throughout. It now checks each item links where it says it does, and
fails against the old binding.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AhkE6EKcrbrFvL1WZ22A9z
Loading the entries index showed the sources as plain text: `craft-nav-item`
only renders an interactive anchor when it has an href, and describing a
source as a button left it without one. Unfocusable, no role, no keyboard.

They carry the source URL again, with the click intercepted for the partial
visit — which is what the component they replaced did, and said so.

Routed straight to the element rather than through `CpLink` when the
descriptor brings its own `onClick`: `CpLink` took its props from Inertia's
`Link`, which declares `onClick`, so the handler was consumed as a prop and
never reached the DOM.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AhkE6EKcrbrFvL1WZ22A9z
It declared Inertia's whole prop surface through `InertiaLinkProps` but
forwarded only `href`, so `method`, `data`, `headers`, `replace`,
`preserveScroll`, `only`, `onClick` and the rest were consumed as props and
silently never arrived. `prefetch` was hardcoded to `click`, so a caller
couldn't turn it off either.

It now declares only what it adds and lets the rest fall through `$attrs`.
Forwarding the declared props instead doesn't work: Vue casts an absent
Boolean prop to `false` rather than `undefined`, so `replace`,
`preserveScroll`, `instant` and friends would arrive as explicit `false` and
override Inertia's own defaults — and `prefetch` would arrive as `false`
rather than being unset.

The cost is that consumers no longer get prop-level types for Inertia's
options from `CpLink` itself; they reach `Link` unchecked.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AhkE6EKcrbrFvL1WZ22A9z
Two things stopped `match-invoker-width` doing anything visible. Lion sets
the inherited width on the overlay *wrapper*, and `.popover-pane` inside it
kept its own `max-width: 320px`, so a 464px invoker got a 320px panel. And
the pane's 1px border sat outside its width, because a shadow root doesn't
inherit the page's box-sizing reset — the same gap `craft-action-item` had.
Measured against a 464px invoker: 322px before, 464px after.

`placement`, `distance` and `match-invoker-width` also never took effect
unless they were set before the overlay was built: `_defineOverlayConfig()`
is read once when the controller is created, and `updated()` only pushed
`for`/`anchor` changes through. Setting `distance` at runtime moved the
panel by exactly nothing; it now moves it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AhkE6EKcrbrFvL1WZ22A9z
Restructures PageScreen around explicit `cp-page` / `cp-content` regions with
a sticky page footer, renames the layout custom properties from
`--content-layout-*` to `--cp-content-*`, and moves the top bar and sidebar
onto `useCpBreakpoints`. SecondaryNav switches to a popover-based disclosure,
and the element index sources nav picks up a gear icon.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AhkE6EKcrbrFvL1WZ22A9z
Completes 0295fdb, which renamed the slots everywhere except the two files
that define and fill them — so `ScreenSlots` declared `content-sidebar` while
the shell still rendered `<slot name="sidebar">` and read `slots.contentSidebar`.

Also settles that slot on kebab-case: `contentSidebar` never matched the
`content-sidebar` the template rendered, so `hasSidebar` was falling through to
its other conditions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AhkE6EKcrbrFvL1WZ22A9z
The nav came through `useCraftData()`, which hands back `page.props.craft` as
it stood at setup — a plain object, so a computed over it had no reactive
dependency and could never update. MainNav lives in the sidebar and never
remounts, so it kept highlighting whichever section you first landed on.

Reads `page.props.craft.nav` inside the computed instead, making Inertia's
new props object on each visit the dependency. The test drives through the
page prop rather than mocking the composable, so it fails against the old
binding.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AhkE6EKcrbrFvL1WZ22A9z
Condenses 18 comment blocks without dropping any of the reasoning: the
teleport-target constraint behind `v-show`, the `--details-width` name
collision with legacy `_cp.scss`, the `#details` 350px pin, and the
`inline-size` vs `size` containment note all survive.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AhkE6EKcrbrFvL1WZ22A9z
The flyout was tied to `icon-only`: collapsed to a rail an item has nowhere to
indent to, so its subnav moved into a popover. Depth has the same problem for a
different reason — a 220px column runs out of horizontal room long before the
nav runs out of levels — but a labelled item had no way to ask for it.

Adds `subnav-display="inline|flyout"`, with `icon-only` still forcing `flyout`,
and extracts the popover both paths now share. A flyout leaves nothing to
collapse, so it suppresses the disclosure toggle.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AhkE6EKcrbrFvL1WZ22A9z
brandonkelly and others added 23 commits September 18, 2026 06:39
build:cms can be used to only build the assets in the resources folder
A rail has nowhere to indent into, but the branch you were in still
opened its subnav in place while every other branch flew out. That ran
at two levels: the trail indented in the rail, and its children resumed
trail expansion once inside the flyout, so the open branch's flyout came
out a pre-expanded tree rather than the flat list every other item gets.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HY6JVxWjJjmZUMuSiBUW9p
A branch on the trail took a thin rule beside its icon while the page
itself took a filled row, so a rail marked the same selection two ways
depending on whether the item had a subnav.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HY6JVxWjJjmZUMuSiBUW9p
The six are defined in cp.css but read from twelve other files, the
legacy CP's SCSS among them — a var() that no longer resolves fails
silently, so the consumers move with the definitions.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HY6JVxWjJjmZUMuSiBUW9p
[6.x] Add safe area to main navigation
# Conflicts:
#	resources/js/common/components/CpLink.vue
@github-actions

Copy link
Copy Markdown

This PR had conflicts with 6.x. Claude attempted an automatic resolution and pushed a merge commit. Please review the merge commit carefully before approving/merging.

Claude's response

Merge completed and pushed.

Summary

There was one conflicted file, resources/js/common/components/CpLink.vue, with a single conflicting hunk — the render function inside CustomElementLink.

Both sides changed the same line for related but distinct reasons. design-updates (d38130b / bfaa30f) changed h(props.tag, attrs, …) to h(props.tag, {...attrs}, …) as part of letting CpLink pass everything through to Inertia's Link via $attrs. 6.x (dc04a61) made the same spread but additionally wrapped onClick/onMousedown/onMouseup in guardMouseHandler() so Ctrl/Command-clicks on custom-element links fall through to the browser instead of being swallowed by SPA navigation.

The incoming version is a strict superset of ours — it does the spread and adds the modifier-click guards — so I took it wholesale. Everything else on our side of the file (the narrowed defineProps that only declares href plus CpLink's own props, the explanatory comment about why Inertia's props must not be redeclared, and moving prefetch="click" into the v-bind so $attrs can override it) merged cleanly and is preserved. No # CONFLICT-REVIEW: comments were needed; the resolution wasn't ambiguous.

The other 35 files (CHANGELOG.md, translations, src/Cp/Settings.php, ElementPolicy.php, DeleteElementsController.php, tests, DeprecatedConcepts.php) auto-merged. I spot-checked the higher-risk ones: CHANGELOG.md and CpLink.test.ts are byte-identical to the incoming side and our branch had made no changes to either, so there's no silently-spliced content there.

One caveat worth noting for the reviewer: pnpm and node_modules aren't available in this environment, so I could not run the Vue/TypeScript test suite — including the new CpLink.test.ts case covering exactly this code path. The resolution is verified by inspection only, and CI should confirm it.

# Conflicts:
#	resources/js/pages/assets/Index.vue
@github-actions

Copy link
Copy Markdown

This PR had conflicts with 6.x. Claude attempted an automatic resolution and pushed a merge commit. Please review the merge commit carefully before approving/merging.

Claude's response

Merge resolved, committed as c877ffc, and pushed to design-updates.

Summary

There was one conflict, in resources/js/pages/assets/Index.vue, where both branches edited the same AssetUploadButton usage:

  • design-updates (256ad4a) moved the button out of the #actions slot into #toolbar-actions and gave it variant="primary", placing it beside the element list like "New Entry".
  • 6.x (bd5c055) kept it in #actions but added the :destination and :drop-zone props that make Assets index uploads join the application-wide queue so they survive Control Panel navigation.

These are independent changes to the same element rather than competing ones, so I composed them: the button stays in #toolbar-actions with variant="primary" and also receives :destination="uploadDestination" and :drop-zone="dropZone". All of 6.x's supporting script setup (uploadDestination, dropZone, useAssetUploadRefresh) merged cleanly and is unchanged. Nothing here was ambiguous enough to need a review comment, but the one thing worth a human glance is the visual result: the persistent-upload button now renders in the toolbar with primary styling, a combination neither branch rendered on its own.

I also spot-checked CHANGELOG.md, since it auto-merges deceptively — design-updates made no changes to it, and the merged file is byte-identical to 6.x's version, so nothing was spliced across sections. #toolbar-actions is a real slot on the current ElementIndexPage and is used the same way by the users and content index pages. Note that node_modules isn't installed in this checkout, so I couldn't run Prettier, the type check, or Vitest locally — CI will need to confirm those.

# Conflicts:
#	resources/js/modules/elements/components/NewEntryButton.vue
@github-actions

Copy link
Copy Markdown

This PR had conflicts with 6.x. Claude attempted an automatic resolution and pushed a merge commit. Please review the merge commit carefully before approving/merging.

Claude's response

Merge resolved, committed, and pushed.

Summary

One conflicted file: resources/js/modules/elements/components/NewEntryButton.vue.

The incoming side (6.x, commit 348678e, "Fix modifier-clicking the new entry button", PR #19669) replaced the button's @click handler — which did manual window.open() / window.location.href navigation — with a computed URL bound to :href, so the browser handles modifier- and middle-clicks natively. Our side (7bce839) had independently reworked the component: the old currentSection / menuSections / button-group structure was replaced by creatableTypes / menuItems, and the component moved into the index toolbar.

Because the two sides restructured the same code, git produced misaligned hunks that mixed 6.x's removed button-group markup into our template. Rather than picking a side, I kept our reworked structure and ported the incoming fix's intent onto it: the createOnly(event) click handler became an onlyUrl computed, and the single-entry-type button now uses :href="onlyUrl" instead of @click="createOnly". The 6.x button-group markup that appeared in the conflict hunk was dropped deliberately — it predates the conflict (it exists in the merge base) and our side intentionally removed it; it was not new work from the incoming branch. craft-button supports href and renders a real anchor, so the fix behaves the same in the new structure.

Nothing here struck me as ambiguous enough to need a CONFLICT-REVIEW marker, but the one judgment call worth a reviewer's eye is that dropped button-group markup — if 6.x's two-button "primary + section menu" layout was meant to survive the toolbar rework, it will need to be reintroduced in the new creatableTypes shape. CHANGELOG.md auto-merged cleanly (one added bullet for #19669, verified against both sides with no duplicated sections). I could not run prettier — node_modules isn't installed in this checkout — so formatting is matched by hand to the surrounding code rather than tool-verified.

# Conflicts:
#	resources/js/pages/settings/entry-types/Index.vue
#	resources/js/pages/settings/routes/Index.vue

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

This PR had conflicts with 6.x. Claude attempted an automatic resolution and pushed a merge commit. Please review the merge commit carefully before approving/merging.

Claude's response

Merge resolved, committed, and pushed.

Summary for PR review

Two files conflicted, both from the same collision: this branch's "Move pages onto the new content layout" work vs. 6.x's e7e71fa "Fix modifier-clicking remaining CP links" (#19669), which swapped Inertia <Link as="craft-button"> for the new CpLink component.

  • resources/js/pages/settings/entry-types/Index.vue — composed both sides: kept this branch's renamed layout slot (name="content-actions") and took 6.x's <CpLink appearance="button" variant="accent" icon="plus"> in place of the old <Link as="craft-button" variant="primary">. The variant="primary"variant="accent" change came along with CpLink, which matches how every other index page on this branch (fields, sections, assets, graphql, user groups) already calls CpLink. The Link import auto-merged away and is no longer referenced in the file.
  • resources/js/pages/settings/routes/Index.vue — two independent imports landed on the same line (CpContainer from this branch, CpLink from 6.x); kept both. The rest of the file auto-merged, keeping this branch's CpContainer-wrapped empty state and 6.x's CpLink edit button.

Nothing ambiguous enough to need a CONFLICT-REVIEW comment. One thing worth a glance: the empty-state "New route" button this branch added to routes/Index.vue still uses the <Link><craft-button> nesting pattern rather than CpLink — 6.x left the identical existing pattern in that file's actions slot untouched, so I left it as-is rather than expanding scope, but if #19669 intended to cover that shape too it would need a follow-up. CHANGELOG.md auto-merged with no conflict; I verified the result is byte-identical to the 6.x side, since this branch made no changelog edits.

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