This guide covers the development workflow, creating custom components, and the hooks and filters available for extending the theme.
cd wp-content/themes/lyquix-child
bun run watchThis launches three parallel processes:
- Bun watches TypeScript and recompiles the Lyquix library
- Bun watches TypeScript and recompiles the Scripts library
- Gulp watches SCSS/PHP/config files, recompiles CSS, minifies JS, and triggers LiveReload
LiveReload automatically refreshes the browser when files change. It runs on port 35729 and is enabled by default (toggle in Theme Customizer > Theme Features > Enable LiveReload).
The LiveReload script is injected by \lqx\livereload\render() at the bottom of custom.php.
Set the debug level in Theme Customizer > JS > Enable lqx debug:
| Level | Output |
|---|---|
| 0 | No debug output |
| 1 | Errors only |
| 2 | Errors + Warnings |
| 3 | Errors + Warnings + Info |
On local environments (hostname ending in .test or WPCONFIG_ENVNAME=local), the theme automatically serves non-minified CSS and JS files for easier debugging.
php/custom/blocks/my-block/
├── block.json
├── default.php
└── default.tmpl.php
{
"name": "lqx/my-block",
"title": "My Custom Block",
"description": "A custom block",
"category": "lqx-content-blocks",
"icon": "admin-generic",
"acf": {
"mode": "preview",
"renderCallback": "my_block_render"
},
"supports": {
"anchor": true,
"className": true
}
}<?php
// php/custom/blocks/my-block/default.php
$settings = \lqx\blocks\get_settings($block);
$content = \lqx\blocks\get_content($block);
require \lqx\blocks\get_template($settings['processed']['block'], $settings['processed']['preset']);<?php
// php/custom/blocks/my-block/default.tmpl.php
$s = $settings['processed'];
?>
<div id="<?= $s['anchor'] ?: $s['hash'] ?>"
class="lqx-block-my-block <?= $s['class'] ?> <?= $s['style'] ?>">
<h2><?= $content['heading'] ?></h2>
<div class="content">
<?= $content['body'] ?>
</div>
</div>Create an ACF field group for your block with fields following the naming convention:
my-block_block_content- Content fieldsmy-block_block_global- Global settingsmy-block_block_user- User settings (style, preset)my-block_block_admin- Admin override settings
Export the field group JSON to acf-json/ for version control.
php/custom/modules/my-module/
├── my-module.php
├── default.php
└── default.tmpl.php
<?php
// php/custom/modules/my-module/my-module.php
namespace lqx\modules\my_module;
function render() {
require \lqx\modules\get_renderer('my-module');
}Add the render call in your custom.php:
<?php \lqx\modules\my_module\render(); ?>Create a PHP file in php/custom/templates/:
<?php
// php/custom/templates/page-services.php
?>
<div class="services-page">
<?php the_content(); ?>
<!-- Custom layout for services page -->
</div>This template is automatically used for a page with the slug "services".
<?php
// php/custom/templates/portfolio.php
// Used for all 'portfolio' custom post type single views
?>
<div class="portfolio-single">
<h1><?php the_title(); ?></h1>
<?php the_content(); ?>
</div><?php
// php/custom/templates/archive-portfolio.php
?>
<div class="portfolio-archive">
<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
<article>
<h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
<?php the_excerpt(); ?>
</article>
<?php endwhile; endif; ?>
</div>To add selectable page templates in the Gutenberg editor:
- Copy
page-templates/template.dist.phptopage-templates/my-template.php - Add the template header:
<?php
/**
* Template Name: My Custom Template
*/- Create a matching template file in
php/custom/templates/my-template.php
| Hook | File | Purpose |
|---|---|---|
after_setup_theme |
setup.php |
Theme initialization |
wp_enqueue_scripts |
css.php, js.php |
Asset enqueuing |
wp_head |
setup.php |
Global WordPress styles |
wp_footer |
css.php |
Critical CSS async loader |
admin_init |
setup.php |
Image sizes, user roles |
admin_menu |
blocks.php |
Reset Global Settings page |
admin_head |
setup.php |
Admin UI customizations |
admin_notices |
setup.php |
Required plugins alerts |
customize_register |
customizer.php |
Customizer settings |
acf/init |
blocks.php |
Block field display logic |
init |
blocks.php, layouts.php |
Block registration |
rest_api_init |
blocks.php, critical.php |
REST API endpoints |
add_meta_boxes |
setup.php |
Excerpt field repositioning |
| Filter | File | Purpose |
|---|---|---|
acf/load_field |
blocks.php, modules.php |
Dynamic field population |
acf/blocks/wrap_frontend_innerblocks |
layouts.php |
Disable inner block wrapping |
block_categories_all |
blocks.php, layouts.php |
Add block categories |
run_wptexturize |
setup.php |
Disable smart quotes |
upload_mimes |
setup.php |
Allow SVG uploads |
intermediate_image_sizes_advanced |
setup.php |
Custom image sizes |
intermediate_image_sizes |
setup.php |
Available sizes list |
auto_update_plugin |
setup.php |
Disable auto-updates |
auto_update_theme |
setup.php |
Disable auto-updates |
acf/settings/load_json |
blocks.php (child) |
ACF JSON load paths |
| Endpoint | Method | Purpose |
|---|---|---|
/lyquix/v3/get-options |
GET | Get block styles/presets options |
/lyquix/v3/search-posts |
GET | Search posts by block and setting |
/lyquix/v3/critical |
GET | Critical CSS configuration |
| Action | Purpose |
|---|---|
reset_global_settings |
Reset block/module global settings to defaults |
get_acf_field |
Retrieve ACF field values for editor display logic |
dismiss_required_plugins_alert |
Dismiss the required plugins notice |
The \lqx\util namespace provides helper functions:
| Function | Purpose |
|---|---|
validate_data() |
Comprehensive data validation with type coercion |
array_is_list() |
Check if an array is a sequential list (not associative) |
breadcrumbs() |
Generate breadcrumb navigation |
slugify() |
Convert strings to URL-friendly slugs |
minify_html() |
Minify HTML output |
parse_video_url() |
Extract video IDs from YouTube/Vimeo URLs |
When Enable Image Sizes is on (default), the theme configures:
| Size | Dimensions | Crop |
|---|---|---|
| thumbnail | 150x150 | Yes |
| xsmall | 320x320 | No |
| small | 640x640 | No |
| medium | 1280x1280 | No |
| large | 2560x2560 | No |
| xlarge | 3840x3840 | No |
The standard medium_large, 1536x1536, and 2048x2048 sizes are removed.
Configuration: .eslintrc.json
npx eslint js/**/*.tsConfiguration: .stylelintrc.json
npx stylelint css/**/*.scssConfiguration: Uses prettier-plugin-tailwindcss for automatic Tailwind class sorting.
npx prettier --write .Configuration: .editorconfig ensures consistent formatting across editors.
The child theme's .gitignore excludes:
.sass-cache/
node_modules/
bun.lockb
package.lock
php/browsers/browsers.json
php/ip2geo/GeoLite2-City.*
php/update.json
- All files in
css/custom/ - All files in
js/custom/ js/scripts.tscustom.php- All files in
php/custom/ css/tailwind/theme.jscss/tailwind/whitelist.htmlpackage.jsongulpfile.js- Compiled CSS/JS (for deployment without build tools)
node_modules/- GeoLite2 database files
- Browser detection JSON
- Theme update metadata