Context
Saddle's admin UI was migrated to @plugpress/ui. v0.6.0 is now tagged/pushed and verified a clean, non-breaking bump for Saddle (all 34 imported symbols resolve; the flat Tabs/FilterTabs/Steps APIs and every pp-* class Saddle's SCSS targets survive; changes are additive — forwardRef/asChild/defaultChecked + a WCAG 2.2 AA pass).
This is a design-refresh bundled onto the v0.6.0 bump:
- Full-width header — make the header span the wp-admin content area edge-to-edge (like Waggle/Knovia) instead of sitting boxed inside the 1008px column.
- Home stat tiles — turn Home's ad-hoc counts into DS
StatCard/StatGrid tiles.
- Accessibility — add
SkipLink + #pp-main (pairs with the new header).
Decisions: header = full-bleed the existing two-row TopBar (NOT DS AppShell/AppNav) — keep the tone-colored status pill + Pause prominent and keep DS Tabs keyboard nav.
Key finding: Saddle already zeroes the wp-admin gutter (body.toplevel_page_saddle { #wpcontent { padding-inline-start: 0 } .wrap { margin: 0 } }). The only thing keeping the header boxed-in is .saddle-app { max-width: 1008px; margin: 0 auto } (style.scss ~96–97). Moving that constraint down to the content column makes the header full-width — no PHP breakout, no negative margins. (This is the structural difference between Saddle and Waggle: Waggle's app root has no max-width; the width lives on AppContent.)
Step 0 — Bump to v0.6.0
package.json pin → github:plugpressco/plugpress-ui#v0.6.0; force re-resolve the git-tag install; verify installed version is 0.6.0.
- Rebuild; confirm externals unchanged in
admin/build/index.asset.php and no google-fonts in built CSS. Non-breaking is pre-verified — no code changes expected from the bump alone.
Step 1 — Full-bleed header (CSS-led, one small JSX change each)
admin/src/components/TopBar.jsx — wrap the existing __row + Tabs in a centered .saddle-top__inner so the bar is full-bleed but its contents align with the body column. No behavior change (status pill, Pause, DS Tabs unchanged).
admin/src/App.jsx — move ForeignNotices + notices + .saddle-panel into a <div className="saddle-content" id="pp-main"> wrapper; TopBar stays outside it (full-bleed).
admin/src/style.scss:
.saddle-app (main view): remove max-width: 1008px; margin: 0 auto + horizontal/top padding.
.saddle-app--setup: add max-width: 620px; margin: 0 auto; padding: 28px 24px 72px (onboarding stays centered-narrow).
.saddle-top: full-bleed bar — background: var(--s-surface), border-bottom: 1px solid var(--s-border), horizontal padding, position: sticky; top: var(--wp-admin--admin-bar--height, 32px); z-index: 20.
.saddle-top__inner (new): max-width: 1008px; margin: 0 auto (centers header content to the body column).
.saddle-content (new): max-width: 1008px; margin: 0 auto; padding: 28px 24px 72px — where the 1008 column now lives.
- Leave the existing
body.toplevel_page_saddle gutter reset untouched.
Gotchas (handled by the above): sticky offset under the admin bar via --wp-admin--admin-bar--height (tracks the 32→46px change ≤782px); folded admin menu handled because #wpcontent's left edge is WP-controlled; no !important needed.
Step 2 — Home stat tiles (admin/src/components/Home.jsx)
Import StatCard, StatGrid. Insert a StatGrid between the Hero and the existing CardGrid, from data already in the component (tier→levelFor, clients, activity):
- Connected apps —
value={clients.length}, plug/connect icon.
- Access level —
value={level.title}, tone = safe→success / active→warning.
- Recent activity —
value={activity?.entries.length ?? 0}, sub = "logged changes"; blocked-count optional as a delta with deltaTone="danger".
Keep the existing "Recent activity" list card below. pp-stat/pp-stat-grid CSS ships in the DS bundle — no new SCSS.
Step 3 — Accessibility: SkipLink + #pp-main
admin/src/App.jsx: render <SkipLink /> as the first child of the main .pp-app view (before <TopBar>). Default href="#pp-main" targets the id="pp-main" on .saddle-content. DS .pp-skip-link CSS handles visually-hidden-until-focused. No SCSS.
Verification
npm run build green; admin/build/index.asset.php still lists react/react-dom/wp-element/wp-i18n/wp-api-fetch; grep -c fonts.googleapis admin/build/*.css → 0.
composer test still green (300).
- wp-playground smoke test (
npx @wp-playground/cli server --auto-mount --login): header spans full content width, sticky under the admin bar, inner content aligned to the 1008px column; content + notices centered at 1008px; onboarding still 620px; Home renders 3 pp-stat tiles; Tab surfaces "Skip to content" → focus moves to #pp-main; status pill + Pause + DS Tabs behave as before.
- Keyboard-only +
prefers-reduced-motion pass; folded admin menu — header still tracks the content edge.
Notes / deferred
- Bundle onto the still-uncommitted UI-migration working tree (or commit migration first, then this) — decide at commit time.
- Deferred (not this issue): DS
AppShell/AppNav wholesale adoption; FeatureCard for the Permissions lanes; Brandmark/BrandProvider app-wide (React-only — does not replace the PHP menu-icon single-source assets/brand/mark.svg, keep that as source of truth); replacing the hand-rolled wizard with DS Wizard.
- Update
STATUS.md when implemented.
🤖 Filed from a Claude Code planning session.
Context
Saddle's admin UI was migrated to
@plugpress/ui. v0.6.0 is now tagged/pushed and verified a clean, non-breaking bump for Saddle (all 34 imported symbols resolve; the flatTabs/FilterTabs/StepsAPIs and everypp-*class Saddle's SCSS targets survive; changes are additive — forwardRef/asChild/defaultChecked + a WCAG 2.2 AA pass).This is a design-refresh bundled onto the v0.6.0 bump:
StatCard/StatGridtiles.SkipLink+#pp-main(pairs with the new header).Decisions: header = full-bleed the existing two-row TopBar (NOT DS AppShell/AppNav) — keep the tone-colored status pill + Pause prominent and keep DS
Tabskeyboard nav.Key finding: Saddle already zeroes the wp-admin gutter (
body.toplevel_page_saddle { #wpcontent { padding-inline-start: 0 } .wrap { margin: 0 } }). The only thing keeping the header boxed-in is.saddle-app { max-width: 1008px; margin: 0 auto }(style.scss ~96–97). Moving that constraint down to the content column makes the header full-width — no PHP breakout, no negative margins. (This is the structural difference between Saddle and Waggle: Waggle's app root has no max-width; the width lives onAppContent.)Step 0 — Bump to v0.6.0
package.jsonpin →github:plugpressco/plugpress-ui#v0.6.0; force re-resolve the git-tag install; verify installed version is0.6.0.admin/build/index.asset.phpand no google-fonts in built CSS. Non-breaking is pre-verified — no code changes expected from the bump alone.Step 1 — Full-bleed header (CSS-led, one small JSX change each)
admin/src/components/TopBar.jsx— wrap the existing__row+Tabsin a centered.saddle-top__innerso the bar is full-bleed but its contents align with the body column. No behavior change (status pill, Pause, DSTabsunchanged).admin/src/App.jsx— moveForeignNotices+ notices +.saddle-panelinto a<div className="saddle-content" id="pp-main">wrapper;TopBarstays outside it (full-bleed).admin/src/style.scss:.saddle-app(main view): removemax-width: 1008px; margin: 0 auto+ horizontal/top padding..saddle-app--setup: addmax-width: 620px; margin: 0 auto; padding: 28px 24px 72px(onboarding stays centered-narrow)..saddle-top: full-bleed bar —background: var(--s-surface),border-bottom: 1px solid var(--s-border), horizontal padding,position: sticky; top: var(--wp-admin--admin-bar--height, 32px); z-index: 20..saddle-top__inner(new):max-width: 1008px; margin: 0 auto(centers header content to the body column)..saddle-content(new):max-width: 1008px; margin: 0 auto; padding: 28px 24px 72px— where the 1008 column now lives.body.toplevel_page_saddlegutter reset untouched.Gotchas (handled by the above): sticky offset under the admin bar via
--wp-admin--admin-bar--height(tracks the 32→46px change ≤782px); folded admin menu handled because#wpcontent's left edge is WP-controlled; no!importantneeded.Step 2 — Home stat tiles (
admin/src/components/Home.jsx)Import
StatCard, StatGrid. Insert aStatGridbetween theHeroand the existingCardGrid, from data already in the component (tier→levelFor,clients,activity):value={clients.length}, plug/connect icon.value={level.title},tone= safe→success / active→warning.value={activity?.entries.length ?? 0},sub= "logged changes"; blocked-count optional as adeltawithdeltaTone="danger".Keep the existing "Recent activity" list card below.
pp-stat/pp-stat-gridCSS ships in the DS bundle — no new SCSS.Step 3 — Accessibility: SkipLink + #pp-main
admin/src/App.jsx: render<SkipLink />as the first child of the main.pp-appview (before<TopBar>). Defaulthref="#pp-main"targets theid="pp-main"on.saddle-content. DS.pp-skip-linkCSS handles visually-hidden-until-focused. No SCSS.Verification
npm run buildgreen;admin/build/index.asset.phpstill listsreact/react-dom/wp-element/wp-i18n/wp-api-fetch;grep -c fonts.googleapis admin/build/*.css→ 0.composer teststill green (300).npx @wp-playground/cli server --auto-mount --login): header spans full content width, sticky under the admin bar, inner content aligned to the 1008px column; content + notices centered at 1008px; onboarding still 620px; Home renders 3pp-stattiles; Tab surfaces "Skip to content" → focus moves to#pp-main; status pill + Pause + DS Tabs behave as before.prefers-reduced-motionpass; folded admin menu — header still tracks the content edge.Notes / deferred
AppShell/AppNavwholesale adoption;FeatureCardfor the Permissions lanes;Brandmark/BrandProviderapp-wide (React-only — does not replace the PHP menu-icon single-sourceassets/brand/mark.svg, keep that as source of truth); replacing the hand-rolled wizard with DSWizard.STATUS.mdwhen implemented.🤖 Filed from a Claude Code planning session.