Skip to content

BB Snippets: design#

Status: proposal, 2026-11-07. Replaces Simple Custom CSS and JS PRO 4.43 (SilkyPress, GPLv3) on coalesce.io and any site that runs BB Cache and BB Migrate. Written from a read of the Pro plugin's code (feature inventory, 2026-11-07) and from what the coalesce work ran into between October and November 2026.

1. Goals#

  1. Drop-in. Deactivate Custom CSS & JS Pro, activate BB Snippets, and every published snippet prints the same code on the same pages. No export, no import, no re-saving.
  2. Reversible. Deactivate BB Snippets, reactivate the Pro plugin, and the site is as it was. BB Snippets keeps the Pro plugin's own data current while it runs, so a rollback needs no admin save.
  3. Every existing feature, minus the ones nobody uses that execute PHP from the database (section 6).
  4. No silent failures. A snippet that is published and active either prints where its rules say, or the admin says why not.
  5. Built to work with BB Cache and BB Migrate, rather than worked around by them.

2. What hurt, and the fix for each#

# Pain point (where we hit it) BB Snippets
1 The URL-rules index (custom-css-js-urls) is rebuilt only by a save on the edit screen. Snippets created by WP-CLI, BB Migrate, import, revision restore, Quick Edit or scheduled publishing never print until someone saves any snippet in the admin (blog highlighting snippet 17721, 2026-11-06). One compiled manifest, rebuilt on every change path: transition_post_status, save_post without a nonce, meta add/update/delete on our keys, delete, trash/untrash, revision restore, import, CLI. A cheap integrity stamp (count + max post_modified) is checked on admin and CLI loads, and the manifest rebuilds itself if it disagrees.
2 CSS/JS print from files in uploads/custom-css-js, not the database. A missing or unwritable folder, a DB moved without uploads, or a revision restore leaves output silently empty or stale. The database is the source of truth. Files are a derived cache with content-hashed names ({key}.{hash8}.css). A missing file is regenerated on the fly, and the snippet prints inline from the database meanwhile. wp bb-snippets doctor and a health panel report drift.
3 About 4,100 files in the folder for about 150 live snippets: every admin save writes a file for the new revision, and changed languages, slugs and previews are never cleaned up. Revisions never write files. Every rebuild garbage-collects files that are not in the manifest.
4 External files get ?v=rand(1,10000), regenerated for every snippet on any rebuild: one save busts every snippet in browsers, Cloudflare and BB Cache's combined bundles. The version is the content hash. A file's URL changes only when its own content does.
5 Snippet IDs differ between live, staging and local (BB Migrate matches them by slug), yet file names, the theme's residual swap and BB Cache's inline list all key on the ID. Each snippet gets a stable key (its slug, unique, editable). File names, handles and CLI use the key; IDs stay internal.
6 Version drift between sites: staging's 4.29 cannot print the "Homepage" rule, so the homepage stats band was wrong there. The rule engine is ours, versioned with the plugin, and deployed like BB Cache (zip or BB Migrate file push).
7 Snippets are echoed raw at wp_head/wp_footer priority 30, outside WordPress's asset queue, so BB Cache can only manage them by matching href substrings, and the theme swaps residual copies by regex over the HTML. Snippets are registered as real styles and scripts (bb-snippet-{key} handles, wp_add_inline_style / wp_add_inline_script for inline ones). BB Cache's combine, defer, critical-inline and preload see them like any other asset; the theme can dequeue or replace one by handle. A filter bb_snippets_content replaces the regex swap.
8 Any snippet mentioning jQuery( enqueues jQuery on every page, and per-page snippets call it synchronously, so jQuery stays render-blocking (DEPLOY-LOG, 2026-11-03). Dependencies and loading strategy per snippet: jquery (detected, including bare $(, and editable), other handles, and defer / async / footer / inline / module. jQuery loads only on pages where a snippet that needs it prints.
9 Staging carried dev-only snippets that printed on top of the real ones (home3, accordion-test, 2026-11-04). Environments per snippet: all, or any of production / staging / development / local, from wp_get_environment_type().
10 Temporary campaign snippets pile up ("temp homev3", "Summit 26 …", "TEMP Platform"); nothing says which still match a page. Optional expiry date, owner and notes. The list flags expired snippets, snippets whose rules match no URL in the sitemap, and snippets untouched for six months.
11 Editing a snippet does not purge the page cache, and BB Cache inlines page snippets into cached HTML, so pages keep the old code until a manual purge. On any snippet change: purge the URLs its rules can reach (from the sitemap), or everything when the rules are broad. Uses BB Cache's bb_cache_purge_url / bb_cache_purge_all, which already mirror to Cloudflare.
12 "Where on site" rules: equal-to is sensitive to trailing slashes and query strings; rules are paths, so a page whose slug changes silently loses its snippet; the debug tab tests rules differently from the front end. Paths are normalised (trailing slash, query ignored unless the rule includes one). New rule types match by post ID, post type, template, term and archive, which survive slug changes. A "Test a URL" tool runs the exact front-end matcher.
13 wp-conditional rules are eval()'d on every request, including admin-ajax and login; a rule containing the word "return" anywhere silently removes the snippet everywhere. A safe parser for conditional tags with literal arguments (is_page, is_single, is_singular, is_front_page, is_home, is_archive, is_category, is_tag, is_tax, has_term, is_post_type_archive, is_search, is_404, is_user_logged_in, and arrays of IDs or slugs). All three conditionals in use today parse. Anything else is flagged in the admin, and eval stays only behind a constant.
14 stripslashes on import and the SASS cascade eats backslashes (content: "\f101", regexes); non-English locales strip every /* */ comment from JS; the html5 option strips type="…" anywhere inside the code. Content is stored and written byte for byte. The only transforms are the ones a snippet opts into: minify and SASS/LESS.
15 Snippets live only in the database: not in git, not reviewable, and moved between sites as posts. Optional file-backed snippets: a snippets/ folder in the theme (or a configured path) with a short header per file. They show in the list read-only, travel with theme deploys and BB Migrate file pushes, and can be promoted to or from database snippets.
16 Security: activate/deactivate, permalink and preview AJAX check a nonce but no capability; licence calls use sslverify => false; anonymous visitors with a preview id get the preview. Capability checks on every endpoint, REST routes with permission callbacks, previews tied to the logged-in user, no licence server.
17 Cost per front-end request: two autoloaded options (the URL index is 18.5 KB on live), a loop over 223 contains values with lowercase and urldecode, eval of conditionals, one file_get_contents per inline snippet, and an output buffer on every admin page. The manifest is a compiled PHP array file in uploads (opcache-friendly), with rules grouped by type for direct lookups. Inline content comes from the manifest, not per-request file reads. No admin-wide buffering.
18 Updates come from SilkyPress's EDD licence server, the updater runs on every admin request, and the licence is bound to the domain. Releases from the BB Snippets repo, the same route as BB Cache and BB Migrate. No licence.

3. Data model (compatible by design)#

BB Snippets reads and writes the Pro plugin's own records, so either plugin can run on the same data.

  • Post type custom-css-js, registered with the same arguments and capabilities (custom_css cap set and the "Web Designer" role), plus show_in_rest for the REST routes.
  • options (serialized), urls (JSON), _active, _slug: read and written exactly as the Pro plugin does, including its defaults, so a snippet saved in BB Snippets is valid for the Pro plugin and the other way round.
  • New fields go in one new meta key, _bb_snippet (JSON), which the Pro plugin ignores: key, env[], deps[], strategy, critical (inline / defer / auto), expires, owner, notes, new rule types.
  • The Pro plugin's derived options (custom-css-js-tree, custom-css-js-urls) and its {ID}.css|js files are kept current while BB Snippets runs (a compatibility writer), so reactivating the Pro plugin is instant. This writer can be switched off once the Pro plugin is gone for good.
  • BB Snippets' own state: the manifest file uploads/bb-snippets/manifest.php, hashed asset files beside it, and one small option for the integrity stamp and settings.

4. Feature parity#

Everything the Pro plugin does today, by area. Items marked later are in use nowhere on coalesce.io, so they move to a later phase; nothing is dropped except the two PHP-executing features in section 6.

  • Languages and placement: CSS, JS and HTML; header, footer, wp_body_open; front end, admin, login page, block editor; internal (inline) or external (file); priority 1–10 with the same ordering.
  • Rules: all nine rule types (all, first-page, contains, not-contains, equal-to, not-equal-to, begins-with, ends-by, wp-conditional) with the same union-then-subtract semantics and the same quirk that a snippet with only negative rules prints nowhere (kept for parity, flagged in the admin), plus the new rule types above.
  • Active toggle from the list, the row action and the edit screen.
  • Minify for CSS and JS.
  • Editor: WordPress core's code editor (CodeMirror with CSS, JS and HTML linting), fullscreen, the same shortcuts, cursor position remembered per snippet. The 44 bundled CodeMirror themes become one light and one dark theme.
  • Revisions: core revisions with the options copied, a side-by-side compare, and restore that rebuilds output.
  • Preview of unsaved changes on a chosen URL, for the logged-in editor only.
  • List screen: active, type, name, author, published, modified, options icons; sorting and the language filter.
  • Settings: comment removal, preview duration, the Web Designer role, editor options.
  • Debug: replaced by the "Test a URL" tool and wp bb-snippets doctor.
  • later: SASS and LESS with partials and @import (scssphp, less.php); HTML device targeting (desktop/mobile by User-Agent, which is unsafe behind a page cache, so it ships with a warning and a BB Cache vary hint); [ccj] shortcodes for HTML snippets without PHP execution; multisite network snippets.

5. Integration#

BB Cache - Snippets are ordinary registered assets, so combine, minify, defer, critical inlining and preload apply without special cases. The critical_inline setting's /custom-css-js substring becomes a per-snippet critical field that BB Cache reads through a filter. - Snippet changes purge what they affect (section 2, row 11). - File URLs are built from the upload base URL per request, scheme-less on both sides, so the http-vs-https mismatch that broke BB Cache's image lookups on staging cannot happen here.

BB Migrate - A snippets scope: export and import by key, with the manifest and files rebuilt on the receiving site in the same request. This replaces BB Migrate's snippets_regenerate() and snippets_url_tree(), which re-implement the Pro plugin's private rebuild today. - pages push keeps working, because the post type and meta are unchanged; the receiving site's change hooks rebuild.

Theme (mt-coalesce) - The residual swap moves from an HTML regex to the bb_snippets_content filter. - The A/B flag ?coa_snippets=residual keeps working through the same filter.

6. Deliberately not carried over#

  • [ccj] shortcode bodies run as PHP (eval, with extract() of shortcode attributes and {$var} substitution). Not used on coalesce.io, and anyone who can place a shortcode can reach it. Shortcodes come back later as plain HTML with escaped attribute substitution.
  • eval() of conditional rules: replaced by the safe parser; eval only with define( 'BB_SNIPPETS_EVAL_CONDITIONALS', true ).

7. Phases#

  1. Runtime and data (done, 0.1.0, 2026-11-07). As built: external files stay at uploads/custom-css-js/{ID}.{ext} in the old plugin's format with a content-checksum ?v=, because the theme's residual swap and BB Cache's inline list match those paths; hashed file names and registered-asset output move to phase 3. Each manifest build is a new file (wp-content/cache/bb-snippets/manifest-{hash}.php), since opcache would otherwise keep serving an old one. Gate result on the local mirror: 323 pages identical to the old plugin except four classes of old-plugin bugs (stale files after DB edits, live URLs left in files after a search-replace, two backslashes dropped by stripslashes, bundle names); the ?coa_snippets=residual render identical; CLI create, raw meta, raw SQL, conditional, environment and expiry paths verified. Original scope: Post type, meta compatibility, manifest and rebuild on every change path, hashed files and garbage collection, the full rule engine with the safe conditional parser, asset registration, compatibility writer, WP-CLI (list, get, create, update, activate, deactivate, rebuild, doctor, test-url, export, import). Gate: on local, with the Pro plugin swapped out, the snippet output of every published URL (drafts/snippet-migration/ab-urls.txt) is identical in content and order, and vrt/style-diff.js shows no difference.
  2. Admin (done, 0.2.0, 2026-11-07). List, editor, rules UI, active toggle, revisions, preview, Test a URL, settings, health panel. Verified in a browser on the local mirror; parity unchanged.
  3. Integrations (done, 0.2.0 and 0.3.0, 2026-11-08). BB Cache targeted purge (plus a BB Cache fix: it no longer purges everything when a non-public post is saved), BB Migrate regeneration through BB Snippets, environments, expiry, the bb_snippets_content filter; per-snippet critical flag (data-bb-critical, read by BB Cache); registered-asset output as an opt-in mode (verified: same code at the same place on all 323 pages; inline JS stays classic because WordPress's -js-after ids escape BB Cache's delay rules); theme file snippets with a header format, DB-wins precedence and BB Migrate's bb_migrate_file_changed action. Not built: a separate BB Migrate snippets scope: page packages and file pushes already carry both kinds, and the receiving side rebuilds by itself.
  4. Later. Done in 0.2.0–0.3.0: SASS/LESS (same compiler versions as the Pro plugin), HTML device targeting, shortcodes without PHP, docs site (MkDocs, snippets.bbase.dev, not deployed). Open: multisite network snippets (not used), release workflow and a GitHub repository.

Rollout per site: local (A/B against the Pro plugin) → local B (BB Migrate round trip) → staging → live, with the Pro plugin kept installed but inactive until BB Snippets has run on live for a while.