Utility and helper functions
Chisel provides a focused set of helper functions (PHP, Twig, and JavaScript) to speed up development, keep templates clean, and standardize output. PHP helpers are organized by domain in
core/Helpers/, Twig functions are registered incore/WP/Twig.php, and JavaScript utilities live insrc/scripts/modules/.
All helpers are namespaced underChisel\Helpersand use static methods for easy access throughout the codebase. Twig functions wrap these helpers for template use, maintaining a consistent API between PHP and template contexts.
Common patterns include BEM class generation, responsive image handling, environment detection, cache management, and AJAX communication. Helpers integrate seamlessly with WordPress, Timber, ACF, WooCommerce, Gravity Forms, and Yoast SEO.
Twig helpers
File: core/WP/Twig.php
Twig functions are registered via the Twig class which extends the Timber Twig environment. All functions are automatically available in Twig templates.
Navigation
get_nav_menu(name)
Returns a Timber menu object from the global context.
Source: Chisel\WP\Twig::get_nav_menu()
{{ get_nav_menu('primary') }}TwigPost and product handling
timber_set_product(post)
Ensures global $product is set correctly in WooCommerce loops.
Source: WoocommerceHelpers::timber_set_product()
{% for post in posts %}
{{ timber_set_product(post) }}
{# Now $product global is available #}
{% endfor %}Twigpost_classes(classes, prefix='c-post')
Transforms WordPress post classes into ITCSS/BEM style.
Source: Chisel\WP\Twig::post_classes()
<article class="{{ post_classes(post.class, 'c-article') }}">TwigImages
get_responsive_image(image_id, size='medium', attrs={})
Outputs responsive <img> with proper dimensions to reduce CLS (Cumulative Layout Shift).
Source: ImageHelpers::get_responsive_image()
{{ get_responsive_image(post.thumbnail.id, 'large', { class: 'c-image' }) }}TwigComponents and UI
get_icon(args)
Renders an SVG icon by name and options. See views/objects/icon.twig for full argument list.
Source: Chisel\WP\Components::get_icon()
{{ get_icon({ name: 'arrow-right', inline: true }) }}Twigcomments_template()
Outputs comments pattern if supported/open.
Source: CommentsHelpers::comments_template()
{{ comments_template() }}Twigbreadcrumbs()
Renders Yoast SEO breadcrumbs (if plugin active).
Source: YoastHelpers::breadcrumbs()
{{ breadcrumbs() }}TwigBEM and styling
bem(name, ...modifiers)
Generates BEM class names with modifiers from booleans, strings, or keyed values.
Source: ThemeHelpers::bem()
<div class="{{ bem('c-card', 'featured', post.sticky ? 'sticky' : false) }}">
{# Outputs: c-card c-card--featured c-card--sticky #}
</div>
<div class="{{ bem('c-btn', { size: 'large', color: 'primary' }) }}">
{# Outputs: c-btn c-btn--size-large c-btn--color-primary #}
</div>TwigPHP helpers
All helpers are located in core/Helpers/ and use static methods.
ThemeHelpers
File: core/Helpers/ThemeHelpers.php
Environment detection:
ThemeHelpers::is_dev_env(): boolPHPChecks if WP_ENVIRONMENT_TYPE is 'development'
ThemeHelpers::is_fast_refresh(): boolPHPChecks if development mode with fast refresh enabled (checks for build/runtime.js).
Theme information:
ThemeHelpers::get_theme_version(): stringPHPReturns theme version from stylesheet.
ThemeHelpers::get_theme_name(): stringPHPReturns site title from get_bloginfo('name').
BEM class generation:
ThemeHelpers::bem( string $name = '', mixed ...$modifiers ): stringPHPGenerates BEM classes from modifiers. Supports booleans, strings, arrays, and associative arrays.
// Simple modifiers
ThemeHelpers::bem('c-btn', 'primary', 'large');
// Output: c-btn c-btn--primary c-btn--large
// Conditional modifiers
ThemeHelpers::bem('c-card', $is_featured ? 'featured' : false);
// Output: c-card c-card--featured (if $is_featured is true)
// Keyed modifiers
ThemeHelpers::bem('c-btn', array( 'size' => 'large', 'color' => 'primary' ));
// Output: c-btn c-btn--size-large c-btn--color-primaryPHPColor palettes:
ThemeHelpers::get_colors_palette( string $type ): string|arrayPHPReturns color palettes from theme.json for ACF or TinyMCE.
Parameters:
$type→'acf'(returns array of hex colors) or'tinymce'(returns formatted string)
$acf_colors = ThemeHelpers::get_colors_palette('acf');
// Returns: ['#ff0000', '#00ff00', '#0000ff']
$tinymce_colors = ThemeHelpers::get_colors_palette('tinymce');
// Returns: "ff0000", "Red", "00ff00", "Green", ...PHPLogin logo data:
ThemeHelpers::get_login_page_logo_data(): arrayPHPReturns array with logo URL, width, height for WP login page CSS.
AssetsHelpers
File: core/Helpers/AssetsHelpers.php
get_final_handle($handle): string
Returns namespaced asset handle; appends -fast-refresh suffix in dev hot module mode.
$handle = AssetsHelpers::get_final_handle('app');
// Production: 'chisel-app'
// Development (with fast refresh): 'chisel-app-fast-refresh'PHPImageHelpers
File: core/Helpers/ImageHelpers.php
get_image_url($name, $is_icon=false): string
Returns URL to theme asset in /assets/images/ or /assets/icons/.
$logo_url = ImageHelpers::get_image_url('logo.svg');
// Returns: https://example.com/wp-content/themes/chisel/assets/images/logo.svg
$icon_url = ImageHelpers::get_image_url('search.svg', true);
// Returns: https://example.com/wp-content/themes/chisel/assets/icons/search.svgPHPget_responsive_image($id, $size='medium', $attrs=[]): string
Returns responsive image HTML with srcset and sizes. Optional width/height override to prevent layout shifts.
echo ImageHelpers::get_responsive_image(123, 'large', [
'class' => 'c-hero__image',
'alt' => 'Hero image'
]);PHPCacheHelpers
File: core/Helpers/CacheHelpers.php
expiry($custom = null): int
Returns theme’s cache expiry (0 if chisel_environment_cache filter returns false).
$expiry = CacheHelpers::expiry();
// Returns: HOUR_IN_SECONDS or 0
$custom_expiry = CacheHelpers::expiry(DAY_IN_SECONDS);PHPclear_environment_cache(): void
Clears Twig loader cache.
CacheHelpers::clear_environment_cache();PHPDataHelpers
File: core/Helpers/DataHelpers.php
json_encode_for_data_attribute($data): string
JSON encodes data for embedding into data-* HTML attributes (properly escaped).
$json = DataHelpers::json_encode_for_data_attribute(array('key' => 'value'));
// Returns: escaped JSON string safe for HTML attributesPHPobject_to_array($obj): array
Converts object to array via JSON encode/decode.
$array = DataHelpers::object_to_array($stdClass);PHPAjaxHelpers
File: core/Helpers/AjaxHelpers.php
get_ajax_endpoint_url(): string
Returns full REST URL for theme AJAX (namespace/base from AjaxController).
$url = AjaxHelpers::get_ajax_endpoint_url();
// Returns: https://example.com/wp-json/chisel/v2/ajaxPHPajax_json_decode($value): mixed
Safe JSON decode for POSTed strings. Returns original value if not valid JSON.
$data = AjaxHelpers::ajax_json_decode($_POST['data']);PHPBlocksHelpers
File: core/Helpers/BlocksHelpers.php
get_block_object_classnames($blockName): string
Generates BEM classes for blocks.
$classes = BlocksHelpers::get_block_object_classnames('chisel/slider');
// Returns: c-block c-block--chisel c-block--sliderPHPrender_twig_file($blockName, $context): string
Renders a block’s Twig template (render.twig or {block-name}.twig).
echo BlocksHelpers::render_twig_file('chisel/accordion', $context);PHPacf_block_render($block, $content, $is_preview, $post_id): void
Main ACF block renderer. Builds context with fields, wrapper_attributes, etc., applies filters, and renders block Twig.
BlocksHelpers::acf_block_render($block, $content, $is_preview, $post_id);PHPget_block_inline_css($blocksUrl, $blockName): string
Fetches built CSS from script.css for inlining (critical CSS).
$css = BlocksHelpers::get_block_inline_css($blocks_url, 'accordion');PHPAcfHelpers
File: core/Helpers/AcfHelpers.php
Safe wrappers for ACF functions that return false if ACF is not active.
get_field($selector, $postId=false, $format=true, $escape=false): mixed
$value = AcfHelpers::get_field('field_name', $post_id);PHPupdate_field($selector, $value, $postId=false): bool
AcfHelpers::update_field('field_name', 'new value', $post_id);PHPCommentsHelpers
File: core/Helpers/CommentsHelpers.php
comments_template(): string
Returns comments block pattern when comments are supported and open.
echo CommentsHelpers::comments_template();PHPYoastHelpers
File: core/Helpers/YoastHelpers.php
is_yoast_active(): bool
Checks if Yoast SEO plugin is active.
if ( YoastHelpers::is_yoast_active() ) {
// Use Yoast features
}PHPbreadcrumbs(): string
Returns breadcrumb HTML, empty if not active or on front page.
echo YoastHelpers::breadcrumbs();PHPWoocommerceHelpers
File: core/Helpers/WoocommerceHelpers.php
is_woocommerce_active(): bool
Checks if WooCommerce plugin is active.
if ( WoocommerceHelpers::is_woocommerce_active() ) {
// Use WooCommerce features
}PHPtimber_set_product($post): void
Fixes loop product context for Timber/Twig templates.
WoocommerceHelpers::timber_set_product($post);PHPget_products_grid_classnames($products, $has_sidebar): string
Returns o-grid classes capped at 4 columns for product grids.
$classes = WoocommerceHelpers::get_products_grid_classnames($products, false);PHPGravityFormsHelpers
File: core/Helpers/GravityFormsHelpers.php
is_gf_active(): bool
Checks if Gravity Forms plugin is active.
if ( GravityFormsHelpers::is_gf_active() ) {
// Use Gravity Forms features
}PHPget_forms_list(): array
Returns array of form ID => title pairs, useful for ACF select field population. Useful to prefill ACF select field for choosing a form.
$forms = GravityFormsHelpers::get_forms_list();
// Returns: array( 1 => 'Contact Form', 2 => 'Newsletter Signup' )PHPget_form($id, $showTitle=false, $showDesc=false, $showInactive=false, $fieldValues=null, $ajax=true, $tabindex=0, $echo=false): string
Renders Gravity Form programmatically by ID.
echo GravityFormsHelpers::get_form(
1, // Form ID
false, // Show title
false, // Show description
false, // Show inactive
null, // Field values
true, // AJAX
0, // Tab index
false // Echo (return string instead)
);PHPJavascript helpers
File: src/scripts/helpers/utils.js
ajaxRequest utility
Signature: ajaxRequest(action, ajaxData = {}, ajaxParams = {}, ajaxHeaders = {}) => Promise
Purpose: Thin wrapper over fetch for authenticated REST AJAX calls to theme endpoints.
Behavior:
- Builds endpoint from localized
chiselScripts.ajax.urlandaction - Example:
/wp-json/chisel/v2/ajax/load-more - Automatically adds
X-WP-Nonceheader - Handles both plain objects and
FormDatainstances - Serializes nested objects to JSON strings in FormData
Security context: URL and nonce come from PHP localization in Assets::set_properties() under chiselScripts.ajax.
Response format: Matches theme endpoints (e.g., { error: 0|1, data, message? })
Usage:
import Utils from './helpers/utils';
// Basic request
const response = await Utils.ajaxRequest('load-more', {
post_type: 'post',
per_page: 10,
page: 2
});
if (response.error) {
console.error(response.message);
} else {
console.log(response.data);
}
// With FormData
const formData = new FormData(form);
const response = await Utils.ajaxRequest('contact-form', formData);
// Custom params and headers
const response = await Utils.ajaxRequest(
'search-posts',
{ query: 'wordpress' },
{ method: 'GET' },
{ 'Custom-Header': 'value' }
);JavaScriptJavaScript modules
Load more module
File: src/scripts/modules/load-more.js
Purpose: Attaches “Load more” behavior to listings using the utils.ajaxRequest('load-more', ...) endpoint.
DOM hooks:
- Container:
.js-load-more-container(where items are appended) - Wrapper:
.js-load-more(requires dataset attributes) - Button:
.js-load-more-button(adds.is-loadingclass while fetching)
Required data attributes:
data-post-type→ Post type to loaddata-per-page→ Number of posts per pagedata-max-page→ Maximum pages available
Behavior:
- Starts at
page: 2 - Stops when
page >= maxPageand removes wrapper - Server renders templates via Twig item templates (see
LoadMoreEndpoint::handle())
HTML structure:
<div class="js-load-more-container">
<!-- Posts rendered here -->
</div>
<div class="js-load-more"
data-post-type="post"
data-per-page="10"
data-max-page="5">
<button class="js-load-more-button c-btn">
Load More
</button>
</div>HTMLSlider module
File: src/scripts/modules/slider.js
Purpose: Initializes Swiper instances for elements with .js-slider. Dynamically imports Swiper (CSS + JS).
DOM hooks:
- Slider root:
.js-slider(expects.swiper-slidechildren) - Container:
.js-slider-container(optional wrapper)
Data attributes:
| Attribute | Type | Default | Description |
|---|---|---|---|
data-type | string | 'default' | Slider type for custom params |
data-arrows | 'yes'|'no' | 'no' | Enable navigation arrows |
data-dots | 'yes'|'no' | 'no' | Enable pagination dots |
data-dots-dynamic | number | 0 | Dynamic bullets count |
data-autoplay | 'yes'|'no' | 'no' | Enable autoplay |
data-autoplay-timeout | number | 5000 | Autoplay delay (ms) |
data-loop | 'yes'|'no' | 'no' | Enable infinite loop |
data-speed | number | 1000 | Transition speed (ms) |
data-slides-per-view | number | 1 | Visible slides |
data-space-between | number | 10 | Gap between slides (px) |
data-direction | string | 'horizontal' | 'horizontal' or 'vertical' |
data-auto-height | 'yes'|'no' | 'no' | Auto-adjust height per slide |
data-center | 'yes'|'no' | 'no' | Center active slide |
data-scrollbar | 'yes'|'no' | 'no' | Enable scrollbar |
data-effect | string | 'slide' | Transition effect |
data-free-mode | 'yes'|'no' | 'no' | Free mode scrolling |
data-parallax | 'yes'|'no' | 'no' | Parallax effect |
data-initial-slide | number | 0 | Start slide index |
data-breakpoints | JSON | — | Responsive breakpoints |
data-args | JSON | — | Override Swiper params |
data-thumbnails | number | 0 | Enable thumbnails with count |
Custom slider types:
Create type-specific params by adding a method to the Slider class:
productsSliderParams() {
this.params = {
...this.params,
spaceBetween: 20,
slidesPerView: 3,
breakpoints: {
320: { slidesPerView: 1 },
768: { slidesPerView: 2 },
1024: { slidesPerView: 3 }
}
};
};JavaScriptThen use: data-type="products"
Thumbnails feature:
- Each slide needs
data-thumbnail-urlattribute - Set
data-thumbnails="{count}"to enable - Script clones slider for thumbnails and wires via Swiper’s
Thumbsmodule - Automatically sets width/height to avoid CLS
Accessibility:
- ARIA live region announces slide changes
- Navigation buttons include proper ARIA labels
- Screen reader friendly
Best practices
PHP helpers
- Use static methods: All helpers are static—no instantiation needed
- Check plugin availability: Use
is_*_active()helpers before plugin-specific code - Leverage BEM helper: Use
ThemeHelpers::bem()for consistent class generation - Environment awareness: Use
is_dev_env()andis_fast_refresh()for conditional dev features - Safe ACF access: Always use
AcfHelperswrappers instead of direct ACF functions
Twig functions
- Wrap PHP helpers: Twig functions should wrap PHP helpers for consistency
- Use custom hooks: Extend Twig via
chisel_twig_register_*action hooks - Keep templates clean: Move complex logic to PHP helpers, keep Twig declarative
JavaScript utilities
- Use Utils.ajaxRequest: Always use the provided wrapper for AJAX calls
- Handle errors: Check
response.errorflag in all AJAX responses - Module pattern: Keep features isolated in separate module files
- Dynamic imports: Use dynamic imports for large libraries like Swiper
General
- Namespace custom helpers: Create helpers in
custom/app/Helpers/folder with clear naming - Filter integration: Use provided filters to extend core helpers
- Documentation: Document custom helpers with comments