[Your Form] -> Settings -> Form Styler.
* - Click "Add New" to create a new styling feed.
* - Give your profile a name (e.g., "Dark Theme" or "Contact Page Style").
*
* 2) Configure Styles
* - Open the "Styling" section in the feed settings.
* - Global Tokens: Use sections like Typography, Colors, and Spacing to set general form styles.
* - Type Overrides: Select a field type from the dropdown, then click "Type overrides" in the navigation to edit.
* - Field Overrides: Click a specific field in the field list on the left to apply styles only to that field.
*
* 3) Activate and Apply
* - Mark the feed as "Default" if you want it applied to all instances of this form.
* - Use the "Apply in admin preview" checkbox to see your styles while building the form.
* - To apply a specific (non-default) feed via URL for testing, append `?blfs_feed=[FEED_ID]` to your page URL.
*
* 4) Advanced Management
* - Export/Import: Copy the JSON payload to move styles between forms or sites.
* - Reset: Use the "Reset defaults" button to clear all configurations and start fresh.
*
* Developer Notes
* - CSS Injection: Styles are injected into the page head via `` tags, scoped to the specific form and feed.
* - Scoping: CSS rules use dual selectors (`.blfs-scope-[ID]` and `#gform_wrapper_[ID]`) for maximum compatibility.
* - Payload: The authoritative data source is the base64-encoded JSON stored in the `style_b64` feed meta.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
add_action(
'gform_loaded',
function () {
if ( ! class_exists( 'GFForms' ) || ! method_exists( 'GFForms', 'include_addon_framework' ) ) {
return;
}
GFForms::include_addon_framework();
if ( class_exists( 'GF_BrightLeaf_Form_Styler_AddOn' ) ) {
return;
}
/**
* Class GF_BrightLeaf_Form_Styler_AddOn
* Extends the GFFeedAddOn class to provide custom form-styling functionality for Gravity Forms.
*
* This add-on enables the addition of conditional styling configurations, feed management for form styling,
* and admin preview capabilities. It integrates seamlessly with Gravity Forms to allow the configuration
* and application of styles at both global and field-level granularity.
*/
class GF_BrightLeaf_Form_Styler_AddOn extends GFFeedAddOn {
// phpcs:disable PSR2.Classes.PropertyDeclaration.Underscore,PHPCompatibility.FunctionDeclarations.NewClosure.ThisFoundOutsideClass
/**
* The version of the add-on.
*
* @var string
*/
protected $_version = '1.5.0';
/**
* The minimum required version of Gravity Forms.
*
* @var string
*/
protected $_min_gravityforms_version = '2.6';
/**
* The slug for the add-on.
*
* @var string
*/
protected $_slug = 'bl-gf-form-styler';
/**
* The path to the file containing the add-on.
*
* @var string
*/
protected $_path = __FILE__;
/**
* The full path to the file containing the add-on.
*
* @var string
*/
protected $_full_path = __FILE__;
/**
* The title of the add-on.
*
* @var string
*/
protected $_title = 'BrightLeaf Form Styler';
/**
* The short title of the add-on.
*
* @var string
*/
protected $_short_title = 'Form Styler';
// phpcs:enable PSR2.Classes.PropertyDeclaration.Underscore
/**
* The singleton instance of the class.
*
* @var self|null
*/
private static $instance = null;
/**
* Keep track of feeds already injected on this page load to prevent duplicate
How this works:
Global tokens become CSS variables on a feed-specific scope class. Type overrides apply to all fields of a given type.
Field overrides apply only to a specific field and win over type overrides.
Sections
Types
Pick a type, then open "Type overrides".
Fields
Form ID: Feed ID:
Generated CSS (preview)
Preview only — CSS is injected at runtime via a <style> tag and is not stored in a GF settings field.
JSON payload (advanced)
This is the authoritative saved payload. The UI edits this value. Invalid JSON will be rejected on save.
get_feeds( $form_id );
if ( is_array( $feeds ) ) {
foreach ( $feeds as $f ) {
$other_id = absint( rgar( $f, 'id' ) );
if ( absint( $feed_id ) === $other_id ) {
continue;
}
$meta = rgar( $f, 'meta' );
if ( rgar( $meta, 'is_default' ) ) {
$meta['is_default'] = 0;
if ( method_exists( 'GFAPI', 'update_feed_meta' ) ) {
/* @noinspection PhpUndefinedMethodInspection */
GFAPI::update_feed_meta( $other_id, $meta );
} else {
$this->update_feed_meta( $other_id, $meta );
}
}
}
}
}
return parent::save_feed_settings( $feed_id, $form_id, $settings );
}
/**
* Get default style payload.
*
* @return array Default tokens and override structures.
*/
protected function default_style_payload() {
// All token values are empty strings by default.
// Only tokens the user explicitly fills in will emit CSS rules.
// This ensures a fresh feed has zero impact on form appearance.
return [
'version' => 1,
'tokens' => [
'typography' => [
'base_font_size' => '',
'label_font_size' => '',
'input_font_size' => '',
],
'colors' => [
'text' => '',
'label' => '',
'choice_label' => '',
'description' => '',
'input_bg' => '',
'input_border' => '',
'focus' => '',
'error' => '',
'button_bg' => '',
'button_text' => '',
],
'spacing' => [
'field_margin_bottom' => '',
'input_padding' => '',
'section_padding' => '',
],
'borders' => [
'radius' => '',
'border_width' => '',
],
'buttons' => [
'radius' => '',
'padding' => '',
],
'states' => [
'focus_ring' => '',
'error_border' => '',
],
],
'type_overrides' => (object) [],
'field_overrides' => (object) [],
];
}
/**
* Check for feed override in request and apply it to form object.
*
* @param array $form The form object.
*
* @return array Modified form object.
*/
public function maybe_set_feed_override_from_request( $form ) {
if ( empty( $form ) || empty( $form['id'] ) ) {
return $form;
}
if ( rgget( 'blfs_feed' ) ) {
$form['_blfs_feed_override'] = absint( rgget( 'blfs_feed' ) );
}
return $form;
}
/**
* Identify applicable feed for a form.
*
* Priority: Request override > Default feed > First active feed.
*
* @param array $form The form object.
*
* @return array|null The selected feed or null.
*/
protected function pick_applicable_feed_for_form( $form ) {
$form_id = (int) rgar( $form, 'id' );
if ( ! $form_id ) {
return null;
}
$override = (int) rgar( $form, '_blfs_feed_override' );
if ( $override ) {
$feed = GFAPI::get_feed( $override );
if ( is_array( $feed )
&& (int) rgar( $feed, 'form_id' ) === $form_id
&& rgar( $feed, 'addon_slug' ) === $this->_slug
) {
return $feed;
}
}
$feeds = $this->get_feeds( $form_id );
if ( empty( $feeds ) || ! is_array( $feeds ) ) {
return null;
}
foreach ( $feeds as $f ) {
if ( ! rgar( $f, 'is_active' ) ) {
continue;
}
if ( rgar( rgar( $f, 'meta' ), 'is_default' ) ) {
return $f;
}
}
foreach ( $feeds as $f ) {
if ( rgar( $f, 'is_active' ) ) {
return $f;
}
}
return null;
}
/**
* Inject scope class into form tag.
*
* @param string $form_tag The opening