A responsive Gutenberg block collection with dark mode support and save-time CSS generation. Ships the Container block as the canonical architecture reference for all future blocks.
- Version: 1.0.5
- Requires: WordPress 6.4+, PHP 7.4+
- License: GPLv3
The plugin works like any WordPress plugin. Two cases:
The build/ directory already contains compiled assets, so just:
- Place the
flexa-block/folder inwp-content/plugins/. - Go to WordPress Admin → Plugins → Flexa Block → Activate.
- Open the editor (post/page or Site Editor), insert the Container block (Flexa group).
Install dependencies and rebuild assets before activating - see section 2.
Environment note: this project runs on Local by Flywheel. Local bundles PHP/MySQL but does not expose
php/composeron the system PATH.npmcommands work normally;composer/phpunitcommands need extra setup - see section 3.
Requires: Node.js (≥ 18 recommended) and npm.
# From the plugin directory: wp-content/plugins/flexa-block
npm install # install dependencies (first time only)
npm run build # production build → outputs to build/
npm run start # dev build + watch (rebuilds on src/ changes)Other commands:
npm run format # auto-format code to WordPress standards
npm run lint:js # run ESLint
npm run lint:css # run Stylelint
npm run typecheck # TypeScript type check (tsc --noEmit, no output files)
npm run check # typecheck + JS testsSource is TypeScript (
.tsx/.ts). Babel (via@wordpress/scripts) strips types at build time sobuild/still produces plain.js; type checking is a separate step withnpm run typecheck. Attribute types live insrc/types.ts; WP package declarations are loosely typed insrc/global.d.ts.
Built assets are placed in build/blocks/container/ and loaded by PHP at runtime (only on pages that actually use the block).
The plugin has two test layers:
| Layer | What it tests | Tool | Ready to run? |
|---|---|---|---|
| JS | Editor utils: responsive cascade, value formatting (effective, withUnit, parseUnit, spacingShorthand) |
Jest (via @wordpress/scripts) |
✅ Yes (Node already available) |
| PHP | CSS generation core: CSS_Builder, CSS_Helpers, Container_CSS (selectors, responsive, dark mode) |
PHPUnit | ⚙️ Requires PHP + Composer |
npm install # if not already installed
npm test # run all JS tests
npm run test:watch # watch modeTest files live alongside source: src/**/*.test.ts (e.g. src/utils/index.test.ts). They are excluded from tsc (run by Jest instead), so @types/jest is not required.
PHP tests do not require a WordPress install or MySQL. tests/test-init.php stubs the necessary WordPress functions so tests run in isolation, very fast.
The machine has PHP 8.2 + Composer installed globally (at %USERPROFILE%\php, added to PATH). Run directly:
composer install # install PHPUnit + PHPStan into vendor/ (first time only)
composer test # run all PHP tests (= phpunit)
vendor/bin/phpunit # or call phpunit directly
vendor/bin/phpunit --filter test_box_shadow_rendered # run a single testBeyond tests, the plugin uses strict typing (declare(strict_types=1) in every PHP file) and static analysis via PHPStan (level 5, with WordPress stubs). PHPStan reads code without executing it, catching type errors, wrong parameter types, and missing array keys early.
composer analyse # run PHPStan (configured in phpstan.neon.dist)
composer check # run PHPStan + PHPUnit togetherNote: PHP is installed at
%USERPROFILE%\php(separate from Local's bundled PHP). Extension config is at%USERPROFILE%\php\php.ini. If your terminal was open before the install, open a new terminal to pick up the updated PATH.
Tests are designed for reuse:
CssBuilderTestandCssHelpersTestcover the shared core all blocks use - no need to rewrite them.- For a new block: copy
tests/php/ContainerCssTest.php, extendCssTestCase, use the built-ingenCss()+assertCssHas()helpers, then swap in the new generator and attributes. Add onerequire_oncefor the new generator totests/test-init.php.
flexa-block/
├── flexa-block.php # Plugin entry point
├── includes/ # PHP backend (namespace Flexa\Block)
│ ├── class-css-builder.php # Fluent CSS builder + media queries + minify
│ ├── class-css-helpers.php # Attributes → CSS (spacing, border, background, shadow, dark)
│ ├── class-css-generator-service.php
│ ├── class-dark-mode-settings.php
│ ├── class-global-styles.php # Design tokens (:root --flexa-* + dark overrides)
│ ├── admin/
│ │ └── class-admin.php # Settings page + REST API + menu
│ └── css-generators/
│ └── class-container-css.php # CSS generator for the Container block
├── src/ # Frontend/editor source (React + TypeScript)
│ ├── types.ts # Attribute types (shared across all blocks)
│ ├── global.d.ts # Ambient declarations: @wordpress/* (loose), *.scss, window.flexaBlock*
│ ├── admin/ # React app for the settings page (index.tsx)
│ ├── blocks/container/ # Container block (block.json, edit.tsx, render.php, view.ts, …)
│ ├── components/ # Shared panels & controls (.tsx)
│ ├── hooks/ · utils/ # Hooks + utils (includes utils/index.test.ts)
├── build/ # Compiled assets (generated by npm)
├── tsconfig.json # TypeScript config (strict, tsc --noEmit)
├── tests/ # PHP tests
│ ├── test-init.php # PHPUnit bootstrap (defines ABSPATH + stubs WP functions)
│ ├── phpstan-bootstrap.php # Declares plugin constants for PHPStan
│ ├── CssTestCase.php # Shared base class for block CSS tests
│ └── php/ # CssBuilderTest, CssHelpersTest, ContainerCssTest
├── composer.json # Dev deps: phpunit, phpstan
├── phpunit.xml.dist # PHPUnit config
├── phpstan.neon.dist # PHPStan config (level 5 + WP stubs)
├── package.json # JS build/lint/test scripts
├── README.md # This file
└── readme.txt # WordPress.org readme
- Save-time CSS: when a post or template is saved, the plugin parses the content, generates CSS once, and stores it in the
_flexa_block_csspost meta. The frontend prints this inline CSS only on pages that actually use the block - no CSS is generated on each page load. - Responsive cascade: attributes follow a
desktop / tablet / mobilestructure; tablet inherits from desktop, mobile inherits from tablet (cascade down). Breakpoints: tabletmax-width: 1024px, mobilemax-width: 767px. - Dark mode: each color is a
{ light, dark }pair. Dark CSS is emitted via@media (prefers-color-scheme: dark)and/or[data-theme="dark"], depending on theDark_Mode_Settingsconfiguration. - Design tokens (global styles):
Global_Stylesprints a shared set of CSS variables on:root(--flexa-color-*,--flexa-space-*,--flexa-font-size-*,--flexa-radius-*) plus a dark override block. Blocks referencevar(--flexa-...)for consistency. Customizable via theflexa_block_design_tokens(light) andflexa_block_design_tokens_dark(dark) filters. - Lazy background images: enable the "Lazy load image" toggle in the Background panel;
view.jsusesIntersectionObserverto load the image only as it approaches the viewport (the URL is gated behind the.flexa-bg-loadedclass).
Go to WordPress Admin → Flexa Block to:
- Enable/disable dark mode and choose the activation method (
prefers-color-scheme/[data-theme="dark"]). - Enable CSS specificity boost (prepends
bodyto selectors to reliably override theme styles). - Enable/disable individual blocks (core blocks are always on).
Settings are stored in the flexa_block_settings option via REST (/wp-json/flexa-block/v1/settings, requires manage_options capability).
CSS is generated at save time and cached in the _flexa_block_css post meta (alongside _flexa_block_css_version). If the frontend shows no CSS or stale CSS:
- Re-open the post and click Update - this is the most reliable way to regenerate CSS.
- CSS is only printed on pages that actually contain the block - verify the block is still in the content.
- After a plugin version update, old CSS is automatically treated as expired (
needs_regeneration()compares versions) and regenerated on the next save. - To clear the cache manually: delete the
_flexa_block_cssand_flexa_block_css_versionpost meta entries for that post (or simply re-save the post).
Each block's CSS generation is isolated: if a generator throws an error, the plugin skips that block and continues generating CSS for the rest - without breaking the page or blocking the save.
To see which block failed, enable WP_DEBUG in wp-config.php:
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );Errors are written to wp-content/debug.log in the format:
[Flexa Block] CSS generation failed for block "flexa/...": <error message> in <file>:<line>
See the conditions in How it works: the block must have a Dark color value set, the post must have been saved, and the dark state must be active (@media (prefers-color-scheme: dark) when the OS is in dark mode, or [data-theme="dark"] present on the page).