Slipstream
v2.5.21Updated: 22/04/2026
v2.5.21
— **Owners can now reach package-registered settings pages.** Package panels render with URLs like `admin.php?page=ss-menu-highlights-settings` — clicking them as an owner used to 403 because the packages registered those `add_submenu_page()` calls with `’manage_options’` as the capability (admin-only). WP’s menu-level cap check runs before the render function so owners never saw the page content.
– **Fix:** `filter_owner_capabilities()` now grants `is_slipstream_owner` to administrators too (it was already a persistent role cap on `owner` via `add_role()`; admins got it at runtime via the cap filter). This makes `is_slipstream_owner` the documented capability for panel-linked settings pages that both roles need to reach. No separate OR-check required.
– **Package pattern** — packages registering their settings page:
“`php
add_submenu_page(
null, // hidden slot (accessed via Slipstream dashboard)
‘My Package Settings’,
‘My Package’,
‘is_slipstream_owner’, // accessible to owners + admins
‘ss-my-package-settings’,
‘my_package_render_settings’
);
“`
`manage_options` should only be used when a page is legitimately admin-only (e.g. Slipstream Package Manager, Developer Config).
– **Updated 5 in-tree packages** to the new pattern: `menu-highlights`, `pdf-menus`, `quick-bar`, `smooth-slide`, `site-stats`. External plugins that register panel-linked admin pages with `manage_options` should update similarly.
v2.5.20
— **Two new extension points for “+ Add New” flows.** The 2.5.15 add-new routing always forced an auto-draft + Content Editor redirect — fine for most CPTs but wrong for ones with their own creation UI (popup modals, wizards, etc.). Plugins can now override or tune both the destination and the initial status.
– **Filter `slipstream_new_post_url`** — fires inside `reroute_new_post_for_owner` BEFORE the auto-draft is created. If the filter returns a non-empty URL string, Slipstream redirects there directly and skips draft creation entirely. Plugins use this to route “+ Add New” to their own popup UI:
“`php
add_filter(‘slipstream_new_post_url’, function($url, $post_type) {
if ($post_type === ‘make_faq’) {
return admin_url(‘edit.php?post_type=make_faq&make_faq_new=1’);
}
return $url;
}, 10, 2);
“`
Plugin JS then watches for the marker param on page load and opens the popup. No draft left behind.
– **Filter `slipstream_new_post_initial_status`** — fires inside `handle_content_editor_save` when promoting an auto-draft on its first save. Default `’draft’` so users explicitly publish via the row-level status toggle, but CPTs where immediate-publish makes sense (FAQs, announcements, simple reference content with no draft workflow) can opt in:
“`php
add_filter(‘slipstream_new_post_initial_status’, function($status, $post_type) {
return $post_type === ‘make_faq’ ? ‘publish’ : $status;
}, 10, 2);
“`
– **Declarative sugar on `register_post_type_for_owner`.** Both filters have convenience args on the helper so packages can declare new-post behaviour at registration time:
“`php
$registry->register_post_type_for_owner(‘announcement’, array(
‘name’ => ‘Announcements’,
‘initial_status’ => ‘publish’, // auto-publish on first save
‘new_post_url’ => admin_url(‘edit.php?post_type=announcement&new=1’), // route to popup instead of CE
));
“`
Under the hood these register the filters for this one post type. Plugins with more dynamic needs (per-context status, URL variations) still use the filters directly.
v2.5.19
— **Reusable modal CSS kit** at `assets/css/modal.css` — enqueued on every admin screen via `router.php::enqueue_styles`. Previously the modal styles (`.slip-modal-backdrop`, `.slip-modal-panel`, `.slip-modal-header`, `.slip-modal-title`, `.slip-modal-close`, `.slip-modal-body`, `.slip-modal-footer`, `.slip-modal-error`, `.slip-btn`, `.slip-btn-primary`, `.slip-btn-secondary`) lived inside `meta-seo.css` and only loaded on meta-seo screens. Third-party plugins that want to use the Slipstream aesthetic for their own popups (e.g. make-faq’s quick-edit) can now reference these classes directly in their markup without enqueueing anything extra. The `meta-seo.css` originals stay in place — they override-equal (same values) with no visual difference.
– **Added `.slip-modal-panel–wide`** modifier (max-width 720px) for richer forms. Default panel stays at 520px.
– **Added `.slip-modal-footer-status`** for status/loading areas pinned left in the footer (margin-right: auto), plus a small `.slip-modal-loading` spinner plugins can drop in during AJAX waits.
– **Added `.slip-toolbar`** for inline formatting toolbars attached above textareas (bold/italic/link-style rows) — matches Slipstream’s palette instead of raw WP admin chrome.
– **Input coverage broadened.** Modal input styling now applies to `[type=”url”]`, `[type=”email”]`, `[type=”number”]` in addition to `text` + `textarea`.
– **Design principle.** Plugins get a drop-in CSS kit; plugins keep their own JS/markup/logic. Slipstream doesn’t try to own their form flow — just gives them the visual vocabulary. `window.slipstreamModal` remains available (from meta-seo.js) for plugins that want a fully-managed popup, but using the raw classes is the zero-friction path for plugins with their own AJAX + form shape.
v2.5.18
— **Framework-first: extension points for third-party plugins.** Previously owners’ chrome (row actions cluster, Content Editor dropdown) was hardcoded. Plugins that wanted to add their own actions — e.g. the `make-faq` plugin’s popup edit UI — had no clean hook. Three filter-based extension APIs so packages can add/remove/replace surfaces without touching Slipstream core:
– **Row actions → `slipstream_row_actions` filter.** `render_actions_cell()` now builds an associative array keyed by slug (`seo`, `featured_image`, `view`, `status`, `edit`), sorts by `priority`, and calls each item’s `render` callable. Plugin use:
“`php
add_filter(‘slipstream_row_actions’, function($actions, $post) {
if ($post->post_type === ‘make_faq’) {
unset($actions[‘view’]); // remove a default
$actions[‘quick_edit’] = [ // add a custom
‘priority’ => 50,
‘render’ => fn($p) => ‘‘,
];
unset($actions[‘edit’]); // override default Edit
}
return $actions;
}, 10, 2);
“`
`render` closures take `$post`, return HTML string (or `”` to skip). Each closure is responsible for its own escaping.
– **Default View action respects `publicly_queryable`.** For CPTs registered with `publicly_queryable => false` (FAQs, lookup tables, etc.) the default `view` action’s render short-circuits — `get_permalink()` would return a 404’ing URL anyway. Plugins that want a “view” target for non-queryable types replace the item via the filter above.
– **Content Editor dropdown → `slipstream_content_editor_dropdown` filter.** The split-save `⋮` menu items (“View page”, “Page settings”) are now an associative array keyed by slug with `priority`, `label`, `icon`, `url`, `target`, `attrs`, or a raw `html` override. Renders as `` if `url` set, `
– **Internal cleanup.** Extracted `render_featured_image_action()` as a public method on `Slipstream_Owner_Visibility` so plugins that completely override the row-actions render can call it directly when composing their own order. Built-in `render` callbacks are static closures — no hidden state, safe to invoke from anywhere.
– **Design principle.** Plugin code stays plugin-side. Slipstream owns: the shell chrome, the list-row layout primitive, the Content Editor surface. Plugins own: their custom edit UIs (popup modals, wizards, whatever), their own data model, their own JS. The filter APIs let them reach into Slipstream’s chrome without forking it.
v2.5.17
— **Robustness pass for third-party plugin integrations** (triggered by `make-faq` drift). Three orthogonal fixes that make Slipstream behave correctly for any well-behaved plugin that adds a CPT:
– **List-view columns: hook both filter variants.** WP fires TWO filters on every admin list screen — `manage_{post_type}_posts_columns` AND `manage_edit-{post_type}_columns` — sequentially with the same data. Slipstream only hooked the first. Plugins (make-faq, WooCommerce, Gravity Forms, etc.) commonly hook the second; their filter ran AFTER our priority-999 replacement and silently re-added their custom columns (answer excerpt, categories, tags, custom actions column). On the live FAQ list that meant owners saw a 5-column raw WP list instead of the clean `slip_actions` cluster. Now hooks both filters at priority 999 — we’re the last word regardless of which one the plugin targeted.
– **`register_post_type_for_owner()` now auto-registers the type as owner-visible.** The helper granted caps + registered a panel + opted in to status toggle, but didn’t add the post type to `owner_visible_post_types`. So plugins using the helper missed features keyed on that list: “+ Add New” routing to Content Editor, the welcome modal, list-view column replacement for non-public CPTs. Added a direct push into `owner_visible_post_types` at the top of the method (separate from the cap-grant branching so `all_caps => false` view-only mode still works correctly).
– **post_content editor in Content Editor for `editor`-supporting CPTs.** FAQs and blog posts use `post_content` as their primary body — previously invisible to owners because Content Editor only rendered ACF groups + Slipstream Content Fields. Added a new `.slip-ce-card–post-content` card that calls `wp_editor()` with a stripped-down toolbar (bold, italic, bullets, numbered, link, unlink, removeformat) — no media buttons, no menubar, no statusbar. Renders BEFORE Slipstream Content Fields + ACF groups so the primary body sits at the top. Save handler picks up `$_POST[‘slip_post_content’]` via `wp_update_post([‘post_content’ => wp_kses_post(…)])` so revisions + standard save hooks fire.
– Per-post-type label: defaults to “Content”, `make_faq` labelled “Answer” (small quality-of-life tweak given the context). Filter `slipstream_post_content_label` lets packages override.
– Filter `slipstream_render_post_content_editor` (default: true when type supports editor) lets packages opt OUT — e.g. a page CPT using Divi should hide the editor since Divi owns layout: `add_filter(‘slipstream_render_post_content_editor’, fn($r, $t) => $t === ‘page’ ? false : $r, 10, 2);`
– CSS pass in `acf-owner.css` tones down wp_editor’s default chrome (borders, toolbar backgrounds, button borders) so it reads as a field inside our card, not a second admin surface.
v2.5.16
— **Welcome modal on new-post creation.** Landing on an auto-draft in the Content Editor without naming it first made it too easy to skip the title — the chrome shows “(no title)” and the post becomes hard to identify in the list. Added a blocking onboarding modal that appears when `post_status === ‘auto-draft’` and title is empty: “New [Singular]” heading, “Give your [singular] a name to get started” copy, a single text input, and a Continue button. Uses the post type’s `singular_name` label so it reads naturally for any CPT (Team Member, Health Service, Page, …). Package authors get the right copy for free.
– Submit (Enter or Continue) saves the title via `POST /wp/v2/{rest_base}/{id}` — same endpoint the inline title editor uses. The chrome bar’s title updates in place, the modal slides out, and the post’s auto-draft status flips to `draft` on the user’s first Save inside the editor (per 2.5.15’s save-handler logic). Cancel button returns to the post type’s list view; Escape key same. Auto-draft stays in the DB and is cleaned up by WP’s cron if they abandoned without saving.
– Continue button is disabled while the input is empty so users can’t submit a blank title. Network failure keeps the modal open for retry. Body scroll is locked while the modal is up. Backdrop uses `backdrop-filter: blur(6px) saturate(140%)` — the layout behind is softened rather than blanked out, which feels less jarring for a transient prompt.
v2.5.15
— **Owner-visible post types: list-view consistency.** `register_list_view_hooks()` previously only looped `get_post_types([‘public’ => true])` — team-member-style CPTs that register with `public => false` (single-post pages gated behind a setting) skipped the `replace_columns` filter entirely, so owners saw raw WP columns (Title + ss_status + Date) instead of the clean `slip_actions` cluster. Now the loop unions `public` types with `Slipstream_Registry::get_owner_visible_post_types()`, so every owner-visible CPT gets the same row layout regardless of its public flag.
– **Inline publish/draft toggle.** Post types registered via `status_toggle_post_types` previously rendered a full toggle + label in a dedicated `ss_status` column — stripped by `replace_columns` for owners. Surfaced the toggle inside the row actions cell (between View and Edit) as a compact slider with no text label. State is clear from colour; tooltip/aria-label exposes “Published — click to unpublish” / “Draft — click to publish”. Reuses the existing `slipstream_toggle_post_status` AJAX + `.ss-status-toggle-input` markup so existing JS in `all-admin.js` handles it unchanged.
– **”+ Add New” routes owners to the Content Editor.** Native `post-new.php` showed a different UI than the Content Editor (WP’s “Untitled Post” field + Save Draft/Trash/Publish buttons, and the Classic Editor WYSIWYG for CPTs supporting `editor`). Hooked `load-post-new.php` — if the current user is owner-only and the target post type is owner-visible, `get_default_post_to_edit($pt, true)` returns (reusing) an auto-draft for that user and redirects to the Content Editor with `post_id=N`. First save in Content Editor promotes `auto-draft → draft` so WP’s cleanup cron doesn’t nuke it prematurely. Admins untouched — they still hit native `post-new.php`.
– `get_default_post_to_edit` reuses an existing unsaved auto-draft for the current user (WP core behaviour), so spam-clicking “+ Add New” doesn’t spawn abandoned drafts.
v2.5.14
— **Grid featured-image flow — one fewer click.** Clicking a row’s featured-image thumbnail used to open an intermediate confirmation modal (with preview + “Choose image” / “Remove” buttons) that then opened the media picker. Now the thumb click goes **straight to the WP media picker**. Selecting an image saves immediately via the existing REST `featured_media` call. Closing the picker without selecting is a no-op. The intermediate modal is gone entirely for this flow.
– **Remove via corner X badge.** For rows that have an image, a small circular X sits in the top-right corner of the thumb (rendered server-side by `owner-visibility.php`, wrapped in `.slip-row-fi-wrap`). Click it and the image is removed (`featured_media: 0`) without opening the picker.
– **Desktop:** badge is `opacity: 0` by default, revealed on wrapper `:hover` / `:focus-within`. Hover tint goes slate-800 → rose-600 so it reads as destructive.
– **Mobile / touch:** `@media (hover: none)` — badge is always visible at `opacity: 0.92`. Tap target is the 16px circle with a 2px white border so it reads clearly against any thumbnail.
– **In-place DOM updates.** `updateRowThumb()` promotes a standalone empty button into the wrapped-with-X shape when an image is added, and demotes back when removed. No page reload needed after either action.
v2.5.13
— **Search on mobile auto-flips to the menu view.** Tapping the search icon while the mobile slider was on the Dashboard view opened the search popup but the menu panels (the thing being filtered) stayed hidden — useless. Now the search icon checks `.slipstream-mobile-slider.show-dashboard` and, if present, fires a synthetic click on `[data-slip-show-menu]` to flip back to the menu before expanding the popup. Reuses the existing document-level toggle delegation so the slider animation and segmented-toggle aria states stay in sync. Desktop unchanged (no `show-dashboard` state — both columns always visible, no flip needed).
v2.5.11
— **Columns touch on desktop.** Removed `md:gap-8` from the `.slipstream-mobile-slider` — sidebar and dashboard columns now sit flush against each other. The tinted sidebar bg provides the visual separation; no gap needed.
– **Fix: mobile search button didn’t open the popup.** The bar search-binding JS was parked inside the welcome-widgets IIFE (right-column render), which executes inline as the HTML parser hits it. At that point the mobile bottom bar hadn’t been rendered yet (it’s a sibling of the slider, rendered after the slider closes). So `querySelectorAll(‘.slip-menu-bottom’)` only found the desktop bar, and the mobile bar’s icon had no click handler. Extracted the binding into its own `` block placed AFTER both bars are in the DOM. Mobile search now expands correctly.
- **User-prescribed CSS fixes applied** (all adopted verbatim or as close as practical):
- `main#slipstream-main { padding: 0 }` — defensive against any WP admin padding leaking in.
- `.slip-sidebar-scroll { padding: 0 10px }` — 10px of horizontal breathing room around the panels list inside the tinted sidebar.
- `.panel-item.panel-item-subtle { padding: 0 !important }` — subtle panel rows (logout / switch-back) no longer get unwanted outer padding from generic admin CSS.
- `.slip-menu-bottom-actions { justify-content: space-between }` on both bars. Desktop: search left, frontend right. Mobile: search left, toggle centre, frontend right (unchanged behaviour).
- `.slip-search-expanded { border-radius: 10px 10px 0 0 }` — popup has rounded top corners only, flat bottom. Reads as an extension sprouting from the bar, not a detached floating pill.
- `input.slipstream-sidebar-search { padding: 5px 12px; border-radius: 7px; background: #eceef4 }` — slim capsule input that matches the sidebar tint, so the input reads as part of the column rather than a standalone field.
v2.5.10
— **Left gap on panel cards removed.** Panels list was `px-2` — 8px of sidebar tint showed through on the left of each white group card. Changed to `px-0` so cards go flush to the column edge.
– **Mobile bar is now TRULY pinned to the viewport bottom.** The previous setup put the bar inside the sidebar column; when the mobile slider translated to show the dashboard (`transform: translateX(-100%)`), the bar went with it — not actually fixed. Root cause is the CSS “containing-block” rule: any `position: fixed` descendant of a transformed ancestor is trapped by that transform. Fixed by rendering two bars:
– **Desktop bar** stays inside `#slipstream-sidebar` as a `flex-shrink: 0` child — pinned at the bottom of the sidebar column via flex layout.
– **Mobile bar** is rendered as a **sibling of `.slipstream-mobile-slider`** (outside the transformed container). CSS `position: fixed; bottom: 0; left: 0; right: 0; z-index: 1000`. `padding-bottom: calc(10px + env(safe-area-inset-bottom, 0))` respects iOS home-indicator safe area. Both columns’ scroll areas get `padding-bottom: 88px` on mobile so content never hides behind the fixed bar.
– **Menu | Dashboard segmented toggle.** Replaced the individual dashboard icon button with a proper segmented control in the mobile bar. Reuses existing `data-slip-show-menu` / `data-slip-show-dashboard` handlers — new `syncToggle()` keeps the active segment in sync with the slider’s `.show-dashboard` class. Click anywhere on either segment switches views. Active segment: white bg, dark slate-900 text. Inactive: transparent, 70% white text, hover to full white. No more `.slip-dash-cta` gradient button at the top of the panels list — the toggle replaces it.
– **Bar switched from indigo brand to slate-900 (dark).** User-suggested move for richer toggle state contrast. White active segments pop cleanly against dark. Icon-button surface went from `rgba(255,255,255,0.16)` on indigo to `rgba(255,255,255,0.08)` on dark (lower opacity reads better on dark). Active search state: full white button with dark icon — clearly distinct.
– **Search popup flush with bar.** Previous 8px gap between the popup and the bar revealed sidebar tint — looked disconnected. Moved to `bottom: 100%` so the popup sits directly above the bar. Shadow now emphasises the lift (`0 -8px 28px`). Subtle `translateY(6px)` → `0` slide-up animation preserved.
– **JS plumbing** — search handlers iterate `.slipstream-sidebar-search` (class, not ID) so both bar inputs filter the panel list identically. Removed fragile `byId` references; use `qsa(‘.slip-menu-bottom’)` for search expand/collapse binding. Slider click handler promoted from `slider.addEventListener` to `document.addEventListener` since toggle buttons now live outside the slider.
v2.5.9
— **Leaned into the pinned-bar direction:**
– **Main padding removed.** `#slipstream-main` dropped `px-4 md:px-0` — columns now reach the viewport edge on mobile.
– **Sidebar gets its own subtle bg tint** (`#eceef4`) so it reads as a distinct region from the dashboard content column (which sits on the shell’s warmer `bg-gray-100`). Small enough to not fight the palette, strong enough to signal “this side is the menu.”
– **Bottom bar recoloured** to the brand indigo (`var(–slip-brand)` = `#4f46e5`), icons flipped to white-on-translucent-white chips (`rgba(255,255,255,0.16)` default, `0.28` hover, `0.32` active-search state). Distinct surface that reads as the column’s system-action zone.
– **Search pops OUT the top of the bar.** Previously the expanded input covered the icon row inside the bar; now it’s a floating white popup (`position: absolute; bottom: calc(100% + 8px)`) that slides up with a soft shadow and a subtle translateY. Icons stay visible + tappable while searching. Close button went from a text “Close” to a tight 28px X chip inside the popup.
– **Dashboard shortcut added to the bottom bar (mobile-only).** Grid icon button, reuses `data-slip-show-dashboard` → slides to the dashboard panel via existing JS. The big gradient `.slip-dash-cta` at the top of the panels list is `display: none`’d on mobile (≤767px) since the bottom-bar icon replaces it. Desktop unchanged — dashboard is already visible in the right column, no shortcut needed.
– Shared `.slip-bottom-btn` class introduced for all three bar icon buttons (search, frontend, dashboard) — consistent sizing (36×36), surface, hover/focus/active states. Replaces the previous `.slip-search-iconbtn` / `.slip-frontend-btn` split where the two had duplicated rules.
v2.5.8
— **Sidebar: search + open-frontend live in a pinned bottom bar.** The search bar at the top of the sidebar left dead space above the first panel even at the tightened 52px min-height — felt like the panel list was floating mid-column. Flipped the architecture: panels list now starts at the top of the sidebar (pt-4, no search above), and a new `.slip-menu-bottom` bar sits pinned at the bottom of the sidebar column with the search icon + a new **open-frontend-in-new-tab** shortcut (external-link icon, `href=”home_url(‘/’)”` + `target=”_blank” rel=”noopener noreferrer”`). Search still expands over the bar when tapped; Close collapses back. Hover/focus states match the existing icon-button surface.
– **Desktop scroll re-plumbed.** Sidebar was previously `overflow-y: auto` on its own, with an override forcing the inner wrapper’s overflow visible — that made the whole column scroll as one. Swapped so the sidebar itself is `overflow: hidden` + flex-column and the inner `.slip-sidebar-scroll` wrapper owns the overflow. Bottom bar stays visible no matter how long the panel list gets because it’s a `flex-shrink: 0` sibling outside the scroll context.
– Search JS rewired: `slip-menu-top` → `slip-menu-bottom` id, `is-expanded` class toggles on the bar instead of the old top wrapper. Action row fades out when expanded so the search input has the full bar width.
– Mobile layout untouched — sidebar is full-width, content flows, bottom bar sits at the end of the content naturally.
v2.5.7
— **Back button: press-and-hold reveals “Back to dashboard” shortcut.** One-step back is the common case from the Content Editor, but returning all the way to the dashboard was a multi-click slog (back → list → dashboard). Tap still does one-step back (default `href` navigation preserved). Hold the back button for 500ms and a small popover appears anchored below it — a pill with a home icon and “Back to dashboard” text. Clicking the pill jumps straight to `admin_url()`. Click-outside or Escape dismisses. Light haptic (`navigator.vibrate(8)`) fires on mobile when the threshold is crossed so the gesture feels tactile. Title attribute hints at the gesture for desktop discoverability (“Back · hold for Dashboard”). Logic is self-contained inline JS in `shell.php` — no new asset file.
– **Dashboard: tightened the empty space under the search bar.** `.slip-menu-top` had `min-height: 68px` with `padding: 16px 16px 14px` — a 36px search icon in a 68px container left ~32px of dead vertical space before the first panel. Reduced to `min-height: 52px` and `padding: 12px 16px 4px`. Expanded-search positioning (`inset`) adjusted to match so the focused-search input sits flush with the new chrome height.
v2.5.6
— **Tabs: traditional underline style.** Wizard pattern from 2.5.5 was the wrong call — user wanted just clean recognisable tabs, not a step-by-step flow. Stripped the wizard entirely (progress bar, “Step X of N” meta, Previous/Next footer, numbered pills) and went with the most universally recognised tab idiom: horizontal row of labels with a bottom underline indicator on the active tab (GitHub repo tabs / Twitter profile tabs / iOS Settings). 13px semibold, indigo underline on active, slate on inactive, smooth colour transitions. Border-bottom on the tab-wrap gives a continuous rule that the active underline visually punches through. Horizontally scrollable on mobile with hidden scrollbars + touch momentum; tight padding below 480px. Tiny JS hook (`scrollActiveTabIntoView`) re-registered on `acf.addAction(‘ready’ / ‘append’ / ‘refresh’)` that smooth-scrolls a tapped tab into view if it was off-screen when clicked.
– Removed: `.slip-wizard-*` CSS block, `buildWizard` / `initAcfWizards` / `registerAcfWizardHooks` JS, per-card `.slip-wizard-progress` and `.slip-wizard-footer` injection. No wizard-state data attrs on cards.
– ACF’s native tab JS does all the work of showing/hiding fields for active tab — we just restyle.
v2.5.4
— **Content Editor: one less wrapper layer.** Previous 2.5.3 layout was `shell → single white canvas → sections separated by hairlines`. Dropped the outer canvas entirely. Each content group is now its own floating white card on the shell bg (16px gap mobile / 20px desktop between cards, 18/20px radius, subtle 1px border + tiny shadow). Cleaner hierarchy — one logical group = one visible surface. Matches the card patterns already used elsewhere in the dashboard (package panels, profile card).
– **Card title promoted.** Now 15/16px dark-slate with a hairline underline inside the card, not a micro-caps label. Reads as a proper heading — easier for owners to orient themselves when a card has many fields.
– **Tab UX reimagined — flattened to stacked sections.** Previous ACF pill tabs (e.g. Hero / We help with / Our approach / Benefits / CTA / Gallery on a service page) hid content behind clicks that beginner owners don’t know to try. New behaviour: hide the ACF-generated tab navigator entirely, force every field visible, promote the tab label itself to an inline sub-section divider (13px bold slate-900 with a hairline top-border and generous top-padding). Everything scrollable in one flow — the owner sees the entire page’s editable surface at a glance.
– JS hook (`content-editor-inline.js → flattenAcfTabs`) runs on `acf.addAction(‘ready’ / ‘append’ / ‘refresh’, …)` so the flattening survives re-renders from repeater row add/remove. Removes `.acf-tab-wrap` and clears any inline `display: none` ACF’s tabs.js applied to hidden fields. CSS belt-and-braces with `display: block !important` on `.acf-field` for cases where JS lags.
– Added `acf-input` to the Content Editor JS deps when ACF is present, so `window.acf` is guaranteed defined before our JS runs. Graceful fallback when ACF isn’t installed.
v2.5.3
— **Fix: blank screen after saving Content Editor.** The save ran inside `render_content_editor_page()` which is the admin_menu page callback — by the time that callback fires, admin.php has already emitted ``, so `wp_safe_redirect()` hit the “headers already sent” condition, silently failed, and the subsequent `exit` killed rendering mid-page. User saw a blank screen; refresh showed the saved state + the success toast (because the transient was set before the dead redirect). Moved save processing to a new `handle_content_editor_save()` method wired to `load-{content_editor_hook}`, which fires BEFORE any output. Clean redirect, no blank state.
– **Content Editor UI audit — reduced wrappers from 3 layers to 1.** Previous structure was: outer shell → white card (2.5rem radius) → header strip (“Page Content / ASSIGNED FIELDS”) → content area padding → per-group slate box (2rem radius) → per-field border. Three nested rounded containers with compounding padding. Now: outer shell → single white canvas (`.slip-ce-canvas`) → flat sections (`.slip-ce-section`) separated by hairline dividers → fields/ACF render directly.
– **Dropped the “Page Content / Assigned Fields” header strip.** It added visual chrome without conveying anything the user didn’t already know from arriving on the page.
– **Mobile-first spacing:** canvas padding drops from 32px → 20px on mobile, radius 24px → 20px. Section separators use border-top + margin instead of wrapping boxes. Fields stack cleanly with `padding: 14px 0`.
– **ACF tab styling rebuilt for mobile.** Old tabs were fixed pills with `flex-wrap` — 8 tabs wrapped to 3 rows on small screens. Now horizontally scrollable (`overflow-x: auto` + hidden scrollbars, touch-friendly), pill group stays inline. Font size dropped 14 → 12px, padding tightened.
– **ACF fields reskinned to match Content Fields typography.** Same label style (11px bold uppercase slate-500), same input chrome (12px radius, 1px slate-200 border, indigo focus ring with soft shadow). Removed ACF’s default heavy per-field borders — now hairline top-border only. Repeater table lightened: smaller radius, cleaner thead, flatter action buttons.
– **`acf-owner.css` selectors extended to `#slipstream-content-editor`.** Previously scoped only to `#slipstream-content` (the owner-shell wrapper on native post.php screens) — meaning every ACF style rule missed on the Content Editor page, leaving raw ACF chrome. Now every rule is dual-scoped.
– `.ss-modern-admin-wrap` dropped from the Content Editor wrapper (unused selector hooking into nothing). `ss-field-item` / deep utility-class chains replaced with stable `.slip-ce-field` / `.slip-ce-input` / etc. named classes.
v2.5.2
— **Fix: “Leave site? Changes you made may not be saved” warning when saving the Content Editor.** The Save button in the shell chrome row called `form.submit()` directly, which does NOT dispatch the form’s `submit` event. ACF attaches its dirty-tracking cleanup (clears the beforeunload warning) and its field-finalisation logic (serialises repeater rows, image IDs, etc.) to that event — none of which fired when the form was submitted programmatically. The browser then kept the “unsaved changes” warning active even as the POST was going out, and some ACF fields risked being submitted in a not-yet-finalised state.
– Switched to `form.requestSubmit()` which dispatches the submit event so ACF (and any other library listening) runs its pre-submit handlers. Fallback to `.submit()` kept for the tiny sliver of browsers without `requestSubmit` support (~3% globally). Applied in two places: the chrome-row Save button (`templates/shell.php` line ~695) and the auto-save button fallback path (`templates/shell.php` line ~865).
v2.5.1
— **Fix: native ACF field groups now render in the Content Editor.** This was the ACF bug surfaced by the 2.5.0 user-switching work. The Content Editor only rendered Slipstream’s own “Content Fields” primitive (slim text/image/select inputs that packages register via the registry) — it was never wired to render native ACF field groups attached to a post type via standard `post_type == X` location rules. Packages like `health-service` that register full ACF groups (tabs, repeaters, WYSIWYG, image picker, conditional logic) appeared empty to owners, showing the “No fields assigned” fallback. Admins routed to `post.php` saw the fields normally, which is why the bug hid for so long.
– `render_content_editor_page()` now: (1) calls `acf_enqueue_scripts()` so repeater/tabs/wysiwyg/image JS loads, (2) emits `acf_form_data()` inside the form for nonce + field tracking, (3) queries `acf_get_field_groups( array(‘post_id’ => $post_id) )` and renders each group via `acf_render_fields()` below the Content Fields section, (4) calls `acf_save_post( $post_id )` in the save handler so all ACF field-type save pipelines (attachment handling, repeater serialization, `acf/save_post` hooks) run. Group title used as the section heading; wrapped in the same `bg-slate-50 rounded-[2rem]` chrome as Content Fields for visual consistency.
– Content Fields and ACF groups coexist — both render on pages/posts that use either or both. “No fields assigned” fallback only shows when neither is present.
v2.5.0
— **Real user switching replaces view-as simulation.** The old `?slipstream_owner_view=1` toggle made admins “preview” owner mode by flipping a user meta flag. That was leaky by construction: `is_owner()` (drives shell/buffer) returned true for simulating admins, but `current_user_is_owner_only()` (drives owner-list CSS, list hooks, post-state hiding) had to exclude admins to avoid blowing up regular admin WP screens — so polish work diverged between real owners and previewing admins. Every hook added post-2.4 widened the gap.
– New `Slipstream_User_Switching` class (`includes/user-switching.php`) implements cookie-based session swap modelled on John Blackbourn’s User Switching plugin: admin clicks “View as [owner]”, their auth cookies are cleared, target’s auth cookies are set, an HMAC-signed origin cookie tracks the admin’s identity for switch-back. Keyed with `NONCE_KEY + NONCE_SALT` so rotating WP’s nonce salts invalidates every switched session. 4-hour TTL, httpOnly, SameSite=Lax, admin-cap-guarded, nonce-per-action, audit-logged via `Slipstream_Activity_Log`. No chain-switching (switch-from-switched returns to origin first). No URL params — cookie only.
– **”Slipstream View” submenu → “View as User”.** Renders a list of owner users with avatar/name/email and a “View as [name]” button per row. Lives under Slipstream → View as User.
– **”Exit Slipstream View” button → “Switch back to [Admin Name]”.** Same slot in the dashboard profile panel; visibility keyed on the origin cookie rather than user-meta flag. Indigo accent (action, not warning — switching back isn’t an error).
– Dual-mode predicates collapsed. `router.php::is_owner()` is now a pure role check with one exception (the Content Editor screen, which admins also use). `owner-visibility.php::current_user_is_owner_only()` untouched — the two predicates now mean the same thing for real user sessions. Removed: `?slipstream_owner_view`, `?slipstream_preview`, `slipstream_owner_view_enabled` user meta (becomes inert dead data — left in DB for now, cleaned on next major).
– Removed the `slipstream-is-admin` body-class injector — it only existed so CSS could tell real owners from simulating admins. No longer meaningful.
v2.4.48
— Fix: “Divi” post-state still showing despite the filter. Divi (and other builders) hook `display_post_states` at later priorities, so my priority-10 filter returned an empty array that they then re-populated. Bumped to priority 999 so the strip runs last.
v2.4.47
— Fix: live filter on the owner list was visually broken. The previous `display: flex !important` on list rows (added when I switched rows to a flex layout for full-width cards) beat the JS filter’s inline `style.display = ‘none’`. Dropped the `!important` from `display` — selector specificity wins over the browser’s tr-default `display: table-row` without it. Added a backup rule for `tr[style*=”display: none”]` that reinstates hidden state in case anything else interferes.
v2.4.45
— Fix: Edit CTA missing on the Pages list for owners. The `page` post type was in the default `owner_visible_post_types` array but its capabilities (`edit_pages`, `edit_published_pages`, etc.) were never granted — `get_edit_post_link()`’s early `current_user_can(‘edit_post’, $id)` check returned null before the routing filter could run. Registry now explicitly calls `add_owner_capability_all(‘page’)` in its base init so the core ‘page’ post type is cap-granted to match its visibility flag.
v2.4.44
— Fix: slug auto-regen wasn’t firing on title change. Switched from the generic `wp_insert_post_data` filter to `rest_pre_insert_{post_type}` — targets REST saves directly, fires on each public REST-visible post type, and modifies the prepared post object before `wp_update_post` runs.
– Temporary diagnostic: `GET /wp-json/slipstream/v1/slug-debug` (admin-only) returns the list of post types the filter is attached to + the last-seen title/slug/id trace. To be removed once the fix is verified.
v2.4.43
— Feature: **slug auto-regeneration** on title change. New `Slipstream_Slug_Manager` hooks `wp_insert_post_data` — when a title changes and the existing slug looks auto-generated (matches `sanitize_title($old_title)` or its `-N` collision suffix), the slug is regenerated from the new title. Hand-customised slugs are preserved. Works transparently for REST saves from the inline title editor.
– Feature: **editable slug field in the Page settings modal**. Monospace input + live URL preview. Saves on blur/Enter via `POST /wp/v2/{type}/{id} {slug: …}`. Preview updates reactively as the owner types. Uniqueness handled server-side by `wp_unique_post_slug` — if WP adjusts the slug to avoid collision, the input re-syncs to what got saved.
– Title save now captures the server-returned `slug` / `link` and updates the cached data attrs on the Content Editor root, so re-opening the Page settings modal reflects the auto-regenerated slug.
v2.4.42
— Content Editor chrome refinement based on split-save preference:
– **Split-save is now the only action control** — separate cog + view icons removed from desktop. Split works at every viewport size.
– **Moved into the chrome row** so it sits vertically inline with the back arrow. Sub-toolbar below the chrome is gone on Content Editor.
– **Border radius 10px** (not fully pill) for a tighter, deliberate look.
– **Menu half (⋮) visually differentiated** — `rgba(0,0,0,0.22)` tint over the indigo so it reads as a distinct target. Hover deepens to `0.32`.
– Dropdown repositioned to anchor off the chrome row.
v2.4.41
— Content Editor: featured image + page settings now live behind a cog button in the sub-toolbar. Card body contains only the assigned content fields — focus preserved.
– Cog opens the shared `slipstreamModal` with a thumbnail + “Choose image…” / “Remove” controls and a read-only URL preview. Saves via `POST /wp/v2/{rest_base}/{id}` — the modal re-renders in place as the thumbnail updates.
– Grid quick-edit: list views for post types that support thumbnails get a new row icon (`.slip-row-fi`) next to the SEO icon. Tap to open the same modal primitive; swap the image without entering the Content Editor. Two surfaces for the same action — solves the “cog is hard to find” concern because the grid is the natural bulk-edit surface.
– Mobile split-save pattern: on viewports ≤767px the cog + view icons collapse into a vertical-3-dot menu attached to the Save button (`.slip-save-split` + `.slip-save-dropdown`). Saves one row of vertical chrome on mobile; keeps all actions inline with back/title.
– Desktop layout unchanged (cog + view + save as separate pills). Visibility handled with `.slip-desktop-only` / `.slip-mobile-only` classes keyed off the 767px breakpoint.
– `render_content_editor_page()` no longer renders its own featured-image row — that feature now lives entirely in the cog modal.
– `meta-seo.php` enqueue + modal-skeleton renderer extended to recognise the `slipstream-content-editor` screen, so the shared modal works on all three contexts (list / post edit / content editor).
v2.4.40
— Fix: SVGs in the chrome bar rendered at browser-default (300×150) on the Content Editor because `owner-list.css` — which carries the size constraints — was only enqueued on `edit.php`. Widened the enqueue to include `post.php`, `post-new.php`, and any `slipstream-content-editor` screen.
– Defense in depth: all chrome-bar SVGs now carry explicit `width=”16″` / `height=”14″` attributes so they render correctly even if the stylesheet fails to load. Added a blanket `max-width/height: 24px` safety net to `.slip-page-chrome svg` / `.slip-list-toolbar svg` in the always-loaded `shell.css`.
– List-specific JS (`owner-list.js`, the live filter) still gated to `edit.php` only — it’s not needed elsewhere.
v2.4.39
— Content Editor joins the unified chrome pattern.
– **Back button routes correctly** — Content Editor now goes one level up to the post type’s list (`edit.php?post_type=X`) instead of jumping straight to Dashboard.
– **Chrome bar** shows the post title as the click-to-edit heading (using the existing `data-slip-title` JS). View-page icon + Save Changes button moved to the `.slip-list-toolbar` sub-toolbar.
– Admin-UI’s duplicate top-chrome block removed from `render_content_editor_page()`; the shell provides the chrome now.
– Featured-image trigger retains its spot at the top of the content card but now has a small “Featured image · click to change” label next to it.
– `content-editor-inline.js` searches for `[data-slip-title]` in both the shell chrome and the content card so the click-to-edit works after the DOM move.
v2.4.38
— Unified chrome pattern (variant A from mockup-05): back button + title only, no bottom border. Actions separated out.
– **List view**: `.slip-page-chrome` shows `[←] Plural`. `.slip-list-toolbar` sits below the chrome with the filter input (left) and “+ New {singular}” button (right) — just above the list rows, not part of the header.
– **Post edit**: same chrome pattern with `[←] {post_title}`. `.slip-list-toolbar.slip-toolbar-right` below holds the Save button. Back arrow goes to the post type’s list (`edit.php?post_type=X`) — no skipping levels.
– **Back = one level up, always**: list → dashboard, post edit → list. Removed the previous “always go to dashboard” fallback.
– Removed `border-bottom` from `.slip-page-chrome` — blends cleanly into the page.
– Added `.slip-save-btn` styling — filled indigo pill with soft glow, matches the dashboard CTA aesthetic.
– Native WP chrome (H1, screen-meta, page-title-action) hidden on post edit too, not just list view.
v2.4.36
— Shell v2: dropped Fraunces serif in favour of Inter + system-font stack. All `.slip-display` / hero / widget titles now use Inter. Cleaner, closer to iOS.
– Fix: list-view rows now truly full-width via flex layout. Replaced the table-cell card trickery (which rendered different widths per row) with `tr { display: flex }` + title column `flex: 1 1 auto` + actions column `margin-left: auto`. Native table header row hidden. One consistent layout regardless of which columns WP or a package adds.
– New: breadcrumb chip pattern replacing the circular back button + separate title. `[← Health Services]` reads as one clickable pill (arrow in an indigo-tinted circle, label inside the white chip). Hover state tints the whole chip indigo. Extendable for deeper levels with `.slip-crumb-sep` + `.slip-current-title` markup once post-edit routes are wired.
v2.4.35
— List view control bar (shell v2). On `edit.php` screens the page header is now a single blurred sticky bar with: back button + post-type title (Fraunces display) + filter input + “+ New {singular}” button. Replaces the previous floating back button + leaked native H1/Add-New combo. Title pulls from the post type’s labels.
– Fix: **Save button leak** onto list views. When the URL had no `page` param, `strpos($panel[‘url’], ”)` was returning `0` (not `false`), matching every panel’s `save_mapping` and rendering the Save button. Guarded with an explicit `$current_plugin_page !== ”` check.
– Native WP chrome on list views hidden: native H1 / `.wp-heading-inline` / `.page-title-action` / `#screen-meta` hidden (replaced by our control bar).
– List rows span 100% width of the content wrap with no outer card framing. Each row renders as its own pill-shaped card with 8px vertical spacing between rows; hover tints indigo.
– Filter input moved from JS-injected position to the control bar. `owner-list.js` now binds to `#slip-list-filter` if present, falls back to injecting its own for older screens.
v2.4.34
— Shell v2 polish: three fixes from live feedback.
– **`–slip-warm-bg` updated to `#f6f4f6`** to match the actual deployed shell background. The `.slip-tabstrip-wrap` fade gradients auto-pick up the new value, so the edge masks now blend correctly instead of leaving a visible white gradient against the grey bg.
– **Sidebar `min-width: 320px`** on desktop (≥768px) so the menu never shrinks below a readable size.
– **Independent column scrolling on desktop**: shell is pinned to `100vh` with `overflow: hidden`; menu column and dashboard column each scroll inside their own bounds. Mobile behaviour unchanged (body scrolls, slider transforms between panels).
v2.4.33
— Shell v2 corrections from first deploy:
– **Right column no longer has top padding** — hero now flows to the top edge of the content column (was `py-6`; changed to `md:px-6 md:pb-6 pb-6`).
– **`.slip-hero-greet` forced white** — fights any inherited h1 colour from the admin stylesheet.
– **`.slip-hero-back` mobile-only** (≤767px). On desktop, no back arrow on the hero (slider pattern isn’t active there).
– **`.slip-dash-cta` mobile-only and matches group-card width** — removed the `16px` horizontal margin that was indenting it inside the menu list; it now aligns flush with the menu cards.
v2.4.32
— **Shell v2 design ported** from `design/mockups/mockup-04.html` into the live plugin. Owner dashboard now uses the warm-editorial aesthetic (cream bg, Fraunces display serif + Inter body) with the full-bleed indigo gradient hero, circular back button, big-stat row, and horizontal-scroll tab chips.
– Menu panel: **shrunken search icon** that animates open into a full search input with Close. Dashboard CTA is now a gradient indigo button (replaces the white panel-item styled trigger). Layout scoped via a new `.slip-shell-v2` wrapper class so styles don’t bleed to admin pages outside the shell.
– Dashboard hero: new `slipstream_hero_stat_value` / `slipstream_hero_stat_label` / `slipstream_hero_stat_trend` filters let packages hook a live metric into the big stat row. Defaults ship with placeholder values.
– Tabs: replaced the glass segmented control with horizontal-scroll pill chips (handles 10+ tabs). Tab scroll uses minimum-needed behaviour — no more over-centering clipping the end chips.
– **New dashboard helpers** in `includes/dash-helpers.php`: `slipstream_dash_hero_widget()`, `slipstream_dash_glance_card()`, `slipstream_dash_glance_grid()`, `slipstream_dash_panel_label()`, `slipstream_dash_week_chart()`. Packages can use these to build multi-panel tab content (hero widget + “at a glance” supporting cards) without reinventing the markup.
– Motion: tab swap triggers count-up on any numeric widget title (≥20). Hero stat counts up on first paint. Slider + tab scroll + count-up all share the same cubic-bezier easing for a cohesive feel.
– Google Fonts (Fraunces + Inter) loaded via `` inside the shell template, only when the shell renders.
v2.4.31
— Mobile dashboard redesign per live DevTools mockup:
– **Removed the “‹ Menu” back bar** above the welcome card. Replaced with a small white-on-translucent **× close button** pinned to the top-right of the welcome card itself. Mobile only; returns the slider to the sidebar menu.
– **Tabs moved out of the welcome card** and into their own row below it. Each tab is an **independent pill button** — active is indigo-filled white-text, inactive is light grey (#e0e0e0) slate-text. Border-radius rounded bottom only (0 0 10px 10px) so the strip reads as tabs hanging from the card above. `margin-top: -1px` tucks the flat tops against the card’s bottom edge.
– **Sliding indicator removed** entirely — was fighting the per-tab styling the mockup called for. `ss-welcome-tab-indicator` div gone; JS simplified to just toggle active/inactive classes.
– Opening Hours tab settings still use the old sliding-indicator pattern (unchanged) since they’re on a white background, not the indigo hero.
v2.4.31
— Mobile dashboard redesign per live DevTools mockup:
– **Removed the “‹ Menu” back bar** above the welcome card. Replaced with a small white-on-translucent **× close button** pinned to the top-right of the welcome card itself. Mobile only; returns the slider to the sidebar menu.
– **Tabs moved out of the welcome card** and into their own row below it. Each tab is an **independent pill button** — active is indigo-filled white-text, inactive is light grey (#e0e0e0) slate-text. Border-radius rounded bottom only (0 0 10px 10px) so the strip reads as tabs hanging from the card above. `margin-top: -1px` tucks the flat tops against the card’s bottom edge.
– **Sliding indicator removed** entirely — was fighting the per-tab styling the mockup called for. `ss-welcome-tab-indicator` div gone; JS simplified to just toggle active/inactive classes.
– Opening Hours tab settings still use the old sliding-indicator pattern (unchanged) since they’re on a white background, not the indigo hero.
v2.4.30
— Fix: Dashboard trigger moved inside `#slipstream-panels-list` as its first item (was previously a sibling of the list container). Shares the exact same scroll flow and positioning as every other menu panel — “first one, same behaviour”. Matches the structure the user verified via DevTools inspector.
– Fix: Double scroll on mobile. `#slipstream-sidebar > .flex-1.overflow-y-auto` has `overflow-y-auto max-h-screen`, creating a second scroll inside the page scroll. Mobile now overrides both to `overflow: visible` / `max-height: none` so only the page body scrolls.
v2.4.29
— Mobile fixes from live annotation pass on cafe site:
– **Search bar icon alignment**: reverted the asymmetric top/bottom padding I added for sticky — the search icon’s `top: 50%` was referencing the full padded container box, pushing it above the input’s visual centre. Back to symmetric padding; icon centres correctly.
– **Dashboard trigger** keeps the indigo filled CTA design but no longer sticky — scrolls with the rest of the menu to match the other panels’ positioning/scroll behaviour. Still always the first item under the search bar.
v2.4.28
— Mobile dashboard polish (first-impression work):
– **Sticky header**: search bar + Dashboard trigger now stay pinned to the viewport top as the menu scrolls — both always accessible.
– **Dashboard trigger restyled** as a filled indigo (`#4f46e5`) CTA with white text, white-alpha icon badge, soft indigo glow; caret nudges right on hover, gentle scale-down on press.
– Full `28px` bottom margin on the Dashboard trigger to create clear separation from the USER section.
– Slider transition now matches the tab-indicator easing (`cubic-bezier(0.4, 0, 0.2, 1)` @ 360ms) so menu ↔ dashboard slides feel of a piece with other motion.
– Welcome-card tab redesign:
– Removed the separate mobile dropdown. Single responsive **glass segmented control** at every viewport — translucent white track, solid white indicator, indigo active text. Reads as premium iOS-style on the indigo hero.
– Indicator slide uses the same 320ms cubic-bezier easing for a consistent feel.
– Simplified JS: no more dropdown state, outside-click, Escape, or scroll-into-view. Indicator re-positions on load / resize via `requestAnimationFrame`.
v2.4.27
— Mobile UX: dashboard now behaves as a two-panel slider below 768px. The menu (sidebar) is the default view; a new “Dashboard” trigger sits directly under the search bar and slides the view to the dashboard panel. A small “‹ Menu” button at the top of the dashboard panel slides back to the menu. Desktop layout unchanged.
– Implementation: CSS-only transforms on the existing two-column flex container (no extra wrapper divs). Small inline JS toggles a `.show-dashboard` class on click.
v2.4.25
— **Critical fix: memory exhaustion from capability-filter recursion.** The `filter_owner_capabilities` filter (introduced 2.4.17) called `is_super_admin( $user->ID )`, which internally calls `$user->has_cap( ‘delete_users’ )`, which re-triggers the `user_has_cap` filter chain → infinite recursion → 1.4GB memory exhausted → fatal error in `wp-includes/class-wp-user.php:790`. Similarly `Slipstream_Owner_Visibility::current_user_is_owner_only()` called `user_can()`, with the same re-entry risk.
– Fix: both methods now use **role checks only** (no `is_super_admin`, no `user_can`, no `current_user_can`). Belt-and-braces: added a static `$in_filter` re-entry guard on `filter_owner_capabilities` so any future cap-check accidentally introduced into the registry callbacks still won’t crash the site.
– Behavior unchanged for normal admins and owners — administrator role check is sufficient for the dynamic-cap grant (super admins in multisite typically have the administrator role too).
v2.4.24
— Feature (2.5.0 step 7): Content Editor gains inline title + featured image controls. Click the H1 title → it swaps to an input → Enter or blur saves via the WP REST API. Click the featured-image thumbnail (or placeholder) → WP media library opens → selection saves automatically. No “Save” click needed — same auto-save vocabulary as the SEO bulk editor. A small status indicator fades through saving/saved/error states.
– Saves go through `POST /wp/v2/{rest_base}/{id}` (WP native REST), which means capability checks + post-type-specific validation come for free. No new REST endpoints added.
– Controls only render when the post type supports `title` / `thumbnail` respectively — data-only CPTs won’t show affordances they can’t use.
– New assets: `assets/css/content-editor-inline.css` + `assets/js/content-editor-inline.js`. Media library (`wp.media`) enqueued on Content Editor screens only.
v2.4.20
— Feature (2.5.0 step 3): Custom Pages grid retired. The Slipstream “Pages” panel now points to `edit.php?post_type=page` (native WP list), with owner-view filtering layered on top. `render_pages_list_page()` is now a one-line redirect for bookmark compat.
– Feature: Owner-view Pages list filtered to **only show pages with assigned content fields** (manual via Developer Config or auto-detected via content bridge) — keeps the scope protection the retired grid provided.
– Feature: `get_edit_post_link` filter routes owner’s edit links to the Content Editor (`admin.php?page=slipstream-content-editor&post_id=X`) while admins continue to hit native `post.php`. One code path per role, swapped at the link-generation layer.
– Cleanup: Removed the legacy custom-grid render (~90 lines of Tailwind HTML + merge logic) and the Tailwind-load reference for the now-dead `admin_page_slipstream-pages` screen.
v2.4.18
— Feature (foundation, 2.5.0 step 1): `Slipstream_Owner_Visibility` — per-post “hide from owner view” flag. Postmeta key `_slip_owner_hidden`; `pre_get_posts` filter excludes flagged posts from owner list queries; admins see all posts with a “Hidden from Owner” badge and a row action to toggle. A small “Slipstream” metabox on the post edit screen mirrors the same toggle. Cross-post-type — works for any public post type.
– Admins always see everything (including flagged posts) regardless of owner-view mode, so they can manage the flag. Owners never see flagged posts at all.
– First landing piece of the owner-view overhaul scoped in `docs/owner-view-audit.md`.
v2.4.16
— UX: Prev/Next pagination and Site-wide defaults buttons restyled in the Slipstream aesthetic — dropped WP’s native `.button` class in favor of `.slip-btn` variants. Pagination uses the neutral secondary style; the defaults button gets a subtle indigo-tint treatment to read as a call-to-action in the header without competing with primary modal actions.
– Added generic disabled + focus-visible states to `.slip-btn` so pagination buttons properly grey out on the first/last page.
v2.4.15
— Fix: Pages tab was missing from the SEO Bulk Editor. Root cause — WP core registers the built-in `page` post type with `publicly_queryable = false` (pages use rewrites, not query vars), which tripped the auto-exclusion introduced in 2.4.12. Built-in types now bypass the `publicly_queryable` check and rely on `public = true` alone; the check still applies to custom post types (so ACF-registered data-only CPTs stay excluded).
– Change: Attachments are now excluded by default (per-media-item SEO is noisy). Restore via the filter if needed: `add_filter( ‘slipstream_meta_seo_excluded_post_types’, fn() => [] );`.
– Fix: “Site-wide defaults” button did nothing on the bulk editor page. The modal HTML skeleton was only rendered on `edit.php` / `post.php` screens; now also rendered on the bulk editor page so `slipstreamModal.open()` has a target.
– Hardening: `autocomplete=”off”` on filter inputs so browsers don’t restore stale filter state across reloads.
v2.4.14
— UX: Bulk editor description now calls out merge-tag support inline with sample tags (`{{post_title}}`, `##site_name##`) so admins discover the dynamic-value pattern without leaving the page.
– UX: Post-type tabs redesigned as classic folder-style tabs docked into a wrapping content panel. Filters and table now share a single white panel connected to the active tab — clearly reads as “tabbed view” rather than an unframed list of buttons. Hover tints indigo; active tab fills white and visually bridges into the panel.
– Table chrome simplified (no double-border inside the panel); added subtle row hover for scan-readability.
v2.4.13
— UX: Default fallback section moved from an inline panel to a “Site-wide defaults” button inline with the page heading. Click opens the defaults form inside the existing modal primitive. The table now sits higher on the page. Auto-save on blur continues to work because the document-level listeners catch the dynamically-injected inputs.
– Cleanup: Removed now-unused inline defaults markup and its styles.
v2.4.11
— Feature: Post types can opt out of Meta SEO via the `slipstream_meta_seo_excluded_post_types` filter. Excluded types disappear from the bulk editor tabs + list query, the list-table “SEO” column, and the post edit screen pill. For data-only CPTs (e.g. “Opening Hours”) that aren’t rendered to the frontend.
– Usage (from a package’s `index.php`):
“`php
add_filter( ‘slipstream_meta_seo_excluded_post_types’, function ( $excluded ) {
$excluded[] = ‘opening_hours’;
return $excluded;
} );
“`
v2.4.9
— Feature: SEO Bulk Editor refactored for live filtering. Post-type select replaced with tabs; status/search/missing-only filter the list without page reload; default status is now “Published”.
– Feature: New REST endpoint `GET /slipstream/v1/meta-seo/list` (capability: `manage_options`) backs the live editor.
– Fix: “Cannot load slipstream-meta-seo-bulk” on filter submit — eliminated structurally by removing the GET form in favor of live REST fetches.
– Fix: On the post edit screen, the whole pill (icon + label) is now a single clickable button, not just the icon.
v2.4.8
— Feature: Meta SEO saves now write to the Activity Log — only when a value actually changes (avoids log spam from auto-save idle blurs). Separate entries for per-post updates vs. site-wide default changes.
– Polish: Mobile refinements below 768px — 44px touch targets on modal close/buttons, 16px input font (prevents iOS zoom), larger list-table icon, bulk editor rows stack into single-column cards, filter bar stacks vertically with full-width controls.
– Meta SEO foundation complete. 2.5.0 will be tagged as the shippable release.
v2.4.7
— Feature: Admin-only SEO Bulk Editor (Slipstream → SEO Bulk Editor). Pinned default-fallback row at the top (site-wide fallback via `slipstream_meta_seo_defaults`), filterable paginated table of all public posts with inline Meta Title + Meta Description inputs. Auto-save on blur via the existing REST endpoints. Filters: post type, status, “only missing meta”, search. 50 rows/page.
– Responsive: defaults row collapses to a single column below 900px; modal remains full-screen on mobile.
v2.3.60
–
v2.3.59
–
v2.3.59
–
v2.3.58
–
v2.3.57
–
v2.3.56
–
v2.3.55
–
v2.3.54
–
v2.3.53
–
v2.3.52
–
v2.3.51
–
v2.3.50
–
v2.3.49
–
v2.3.48
–
v2.3.47
–
v2.3.46
–
v2.3.45
–
v2.3.43
–
v2.3.42
–
v2.3.41
–
v2.3.40
–
v2.3.40
–
v2.3.39
–
v2.3.38
–
v2.3.37
–
v2.3.36
–
v2.3.35
–
v2.3.34
–
v2.3.33
–
v2.3.32
–
v2.3.31
–
v2.3.30
–
v2.3.29
–
v2.3.28
–
v2.3.28
–
v2.3.27
–
v2.3.26
–
v2.3.25
–
v2.3.24
–
v2.3.23
–
v2.3.22
–
v2.3.21
–
v2.3.20
–
v2.3.19
–
v2.3.20
–
v2.3.19
–
v2.3.18
–
v2.3.17
–
v2.3.16
–
v2.3.15
–
v2.3.14
–
v2.3.13
— UI: Removed text from Merge Tags in the admin bar, replaced with a standalone indigo icon (#4f46e5) for a cleaner look and better visibility.
– UI: Updated Merge Tags copy-to-clipboard feedback in the admin bar to use a color shift (indigo to emerald) since the text label has been removed.
v2.3.12
–
v2.3.11
–
v2.3.10
— Feature: Added `slipstream-is-admin` body class to the admin screen for administrators. This allows for conditional styling (e.g., showing `.admin-only` elements) that remains hidden for standard site owners.
– UI: Updated `acf-owner.css` to automatically show `.admin-only` elements when an administrator is viewing the Slipstream interface.
v2.3.10
–
v2.3.9
— UI: Expanded owner view CSS to support post types in “Extended view” (using `.table-view-excerpt`).
v2.3.8
— Feature: Expanded Merge Tag syntax to support `##tag_slug##` in addition to `{{tag_slug}}`. This provides a safe alternative for URL fields and contexts where curly braces are sanitized (e.g., Divi).
– UI: Added a “Syntax Help” tab to the Merge Tag popup to educate users on the different syntax options and formatting.
– Documentation: Updated Framework Brief with new merge tag syntax and callback registration best practices.
v2.3.7
— Feature: Implemented a robust Callback Registration system in `Slipstream_Registry`.
– Feature: Added `register_callback` and `get_callbacks` to support universal and context-aware function sharing between packages.
– Documentation: Updated Framework Brief with Callback System details.
v2.3.6
— Deployment: Updated `deploy_core.php` to auto-detect version from `slipstream.php` and auto-extract the latest changelog entry from `CHANGELOG.md`.
– Deployment: Argument parsing in `deploy_core.php` refactored to support parameter-less execution.
v2.3.6
— Deployment: Updated `deploy_core.php` to auto-detect version from `slipstream.php` and auto-extract the latest changelog entry from `CHANGELOG.md`.
– Deployment: Argument parsing in `deploy_core.php` refactored to support parameter-less execution.
v2.3.6
— Deployment: Updated `deploy_core.php` to auto-detect version from `slipstream.php` and auto-extract the latest changelog entry from `CHANGELOG.md`.
– Deployment: Argument parsing in `deploy_core.php` refactored to support parameter-less execution.
v2.3.6
— Deployment: Updated `deploy_core.php` to auto-detect version from `slipstream.php` and auto-extract the latest changelog entry from `CHANGELOG.md`.
– Deployment: Argument parsing in `deploy_core.php` refactored to support parameter-less execution.
v2.3.6
— Deployment: Updated `deploy_core.php` to auto-detect version from `slipstream.php` and auto-extract the latest changelog entry from `CHANGELOG.md`.
– Deployment: Argument parsing in `deploy_core.php` refactored to support parameter-less execution.
v2.3.6
— Deployment: Updated `deploy_core.php` to auto-detect version from `slipstream.php` and auto-extract the latest changelog entry from `CHANGELOG.md`.
– Deployment: Argument parsing in `deploy_core.php` refactored to support parameter-less execution.
v2.3.3
–
v2.3.2
–
v2.3.1
–
v2.3.0
–
v2.2.9
–
v2.2.8
–
v2.2.7
–
v2.2.6
–
v2.2.5
–
v2.2.4
–
v2.2.3
–
v2.2.2
–
v2.2.1
–
v2.2.0
–
v2.1.9
–
v2.1.8
–
v2.1.7
–
v2.1.6
–
v2.1.5
–
v2.1.4
–
v2.1.3
–
v2.1.2
–
v2.1.1
–
v2.1.0
–
v2.0.9
–
v2.0.8
–
v2.0.7
–
v2.0.6
–
v2.0.5
–
v2.0.4
–
v2.0.3
–
v2.0.2
–
v2.0.1
–
v2.0.0
–
v1.9.9
–
v1.9.8
–
v1.9.7
–
v1.9.6
–
v1.9.5
–
v1.9.4
–
v1.9.3
–
v1.9.2
–
v1.9.1
–
v1.9.0
–
v1.8.9
–
v1.8.9
–
v1.8.8
–
v1.8.7
–
v1.8.6
–
v1.8.5
–
v1.8.4
–
v1.8.3
–
v1.8.2
–
v1.8.1
–
v1.8.0
–
v1.7.9
–
v1.7.9
–
v1.7.8
-General maintenance and deployment.
v1.7.7
-Tutorials Dashboard UI improvements and Site Stats cleanup.
v1.7.6
-Dashboard UI improvements: Multi-tab Welcome Card, integrated Stats tab, stacked Tutorials, and real-time search filter.
v1.7.5
-Integrated video playback into Dashboard Welcome Card, hid Tutorials panel from Owner sidebar, and improved tutorial management.
v1.7.4
-Removed Overview, fixed tab hover color, updated Site Tutorials UI, and added Site Stats package.
v1.7.3
–
v1.7.2
-Fixed styling of Developer Config toggles and updated Team Members package (v1.5.0).
v1.7.1
-Improved Core Version Requirement handling with new table columns and non-blocking uploads.
v1.7.0
-Updated Team Members package (v1.4.0) with menu_icon and Core requirement (1.7.0).
v1.6.7
-Fixed Package Manager allowing incompatible packages to be installed/updated without warnings and added strict core version enforcement.
v1.6.6
-Updated Team Members package (v1.3.0) with new dashicon and core version requirement (1.6.6+).
v1.6.5
-Implemented Core Version Requirements for packages and enhanced Developer Config UI.
v1.6.4
-Integrated package updates for Health Service (v1.3.8) and Team Members (v1.1.0). Fixed Service Process module text padding and enhanced Team Members package.
v1.6.3
-Enhanced Merge Tag Scanner with Divi Theme Builder template identification and updated framework-demo example.
v1.6.2
–
v1.6.1
–
v1.6.0
–
v1.5.9
–
v1.5.8
-Added third-party integration support with new hooks and registry helpers.
v1.5.7
-Updated CSS for admin UI.
v1.5.6
-Slipstream v1.5.6: Added post status toggle framework and updated Health Services package.
v1.5.5
-Removed icon from logout panel item. Moved Demo_overview widget to framework-demo package and added active check.
v1.5.4
-Fixed search functionality to correctly filter all panel item types including toggle and subtle styles. Hardened search JS to prevent runtime errors.
v1.5.3
-Slipstream v1.5.3: Updated health-service package to version 1.2.2.
v1.5.2
–
v1.5.1
–
v1.5.0
–
v1.4.9
–
v1.4.8
–
v1.4.7
–
v1.4.6
–
v1.4.5
–
v1.4.4
–
v1.4.3
–
v1.4.2
–
v1.4.1
–
v1.4.0
–
v1.3.3
–
v1.3.2
–
v1.3.1
–
v1.3.0
–
v1.2.9
–
v1.2.8
–
v1.2.8
-Added changelogs to all example packages and synchronized versions.
v1.2.7
-Extended package system with changelog support. Packages can now have a changelog.md file or changelog field in manifest.json, viewable in the package manager via a megaphone icon.
v1.2.6
-Added table column helpers, default 100 posts per page, and enhanced admin UI overrides.
v1.2.5
-Incremented version to 1.2.5 and synchronized codebase.
v1.2.4
-Incremented version to 1.2.4 and synchronized codebase.
v1.2.3
-Loaded all-admin.css on the menu edit screen and updated version to 1.2.3.
v1.2.2
-Deployed user changes to the Menu Handler and incremented version.
v1.2.1
-Enhanced Dynamic Loader Settings UI and changed default behavior to load all matching posts unless limited.
v1.2.0
-Moved Menu Handler initialization earlier to ensure save_custom_fields hook is registered before WordPress processes menu updates.
v1.1.9
-Added detailed error logging to Menu Handler to diagnose Dynamic Menu Item addition issues.
v1.1.8
-Fixed PHP notice/error in save_custom_fields that prevented adding menu items.
v1.1.7
-Fixed Dynamic Post Loader ‘Add to Menu’ issue and improved UI/Performance.
v1.1.6
-Refactored Dynamic Menu system to use a single flexible placeholder with Post Type and Taxonomy filters, performance caching, and improved modal UI.
v1.1.5
-Improved Dynamic Menu system with specialized ‘Dynamic’ item label, custom CSS/JS support, and a dedicated configuration modal.
v1.1.4
-Officially integrated get_queried_object_id() for robust merge tag resolution on single posts and templates.
v1.1.3
-Robust fix for Divi Theme Builder merge tags using et_theme_builder_get_queried_object_id().
v1.1.2
-Fixed merge tags being blank when used in Divi Theme Builder templates by improving post ID detection.
v1.1.1
-Moved Health Services ACF styling to core acf-overrides.css and updated package to v1.0.8.
v1.1.0
-Renamed Dynamic Menu metabox to ‘Dynamic’, fixed infinite spinner issue, and updated Health Services package.
v1.0.9
-Improved Dynamic Menu metabox reliability and UX; Updated Health Services package instructions.
v1.0.8
-Fixed dynamic menu metabox reliability, added configuration options for dynamic menu items, and updated Health Services package with a menu helper tab.
v1.0.7
-Implemented core dynamic menu framework and updated Health Services package (v1.0.4) to use it.
v1.0.6
-Unified profile and security into a single custom tabbed interface.
v1.0.5
-Remove SLIPSTREAM_VERSION definition and rely on plugin header
v1.0.4
-Consolidated versioning logic in build script
v1.0.3
-Fixed versioning logic and regex corruption
v1.0.2
-Use SLIPSTREAM_VERSION constant and add Composer autoloader
v1.0.1
-Move documentation to .documents folder