=== HTML Attributes Customizer for Blocks ===
Contributors: neerajk1409
Tags: blocks, gutenberg, html attributes, block editor, custom attributes
Requires at least: 6.0
Tested up to: 6.9
Requires PHP: 7.4
Stable tag: 1.0.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Add custom HTML attributes to any block, with optional dynamic variables on the front end.

== Description ==

HTML Attributes Customizer for Blocks adds an **HTML attributes** panel in the block sidebar. Enter one attribute per line as `name="value"` (double quotes), or a boolean attribute as a single name (for example `inert`).

Attributes are stored on the block as JSON objects: `htmlbacAttributes` and `htmlbacInnerAttributes` (for example `{"fetchpriority":"high","data-nolazy":"0"}`). Boolean flags use `true`.

**Merging:** Duplicate names on multiple lines are combined. Values merge with spaces (semicolons for `style`). `class` merges with the block’s existing classes instead of replacing them.

**Variables:** Use tokens such as `{{post.title}}` or `{{user.auth_status}}` inside quoted values. They are replaced when the block renders on the front end.

== Installation ==

1. Upload the plugin folder to `/wp-content/plugins/block-attr-customizer/`.
2. Activate through **Plugins** in WordPress.
3. Edit a block, open the sidebar, and expand **HTML attributes**.

== Frequently Asked Questions ==

= Will variables work in the editor preview? =

No. The editor shows raw `{{…}}` tokens; replacement runs on the published front end.

= Can I add my own variables? =

Yes. Register a token in the editor reference, resolve it on the front end, then use it in attribute values as `{{my.plugin.token}}`.

**1. Register the token (shows in the Variables tab):**

    add_filter( 'htmlbac_variable_definitions', function ( $definitions ) {
    	$definitions[] = array(
    		'key'         => 'my.plugin.token',
    		'group'       => 'other',
    		'description' => __( 'Example custom token for HTML attributes.', 'my-plugin' ),
    	);
    	return $definitions;
    } );

**2. Resolve the token when the block renders:**

    add_filter( 'htmlbac_resolve_variable', function ( $value, $key ) {
    	if ( 'my.plugin.token' === $key ) {
    		return esc_attr( 'hello-from-my-plugin' );
    	}
    	return $value;
    }, 10, 2 );

**3. Optional — override the full replacement map in one place:**

    add_filter( 'htmlbac_variable_replacements', function ( $replacements ) {
    	$replacements['my.plugin.token'] = esc_attr( 'hello-from-my-plugin' );
    	return $replacements;
    } );

In the block sidebar, add a line such as `data-example="{{my.plugin.token}}"`.

= Some blocks have an “Inner element” field. Why? =

Blocks such as Group, Image, and Video output an inner `div`, `img`, or `video` that the outer wrapper cannot target. Use **Inner element** for those.

**Enable inner attributes for more blocks (built-in strategies):**

Built-in strategy keys include `group_inner`, `first_img`, `first_video`, `first_a`, `first_figure`, and others. Map a block name to one of those keys:

    add_filter( 'htmlbac_inner_target_blocks', function ( $strategies ) {
    	$strategies['core/cover']  = 'first_img';
    	$strategies['core/button'] = 'first_a';
    	return $strategies;
    } );

**Custom strategy (when no built-in key fits):**

    add_filter( 'htmlbac_inner_target_blocks', function ( $strategies ) {
    	$strategies['my-plugin/my-block'] = 'my_first_button';
    	return $strategies;
    } );

    add_filter( 'htmlbac_apply_inner_strategy', function ( $block_content, $entries, $strategy, $block_name ) {
    	if ( 'my_first_button' !== $strategy || 'my-plugin/my-block' !== $block_name ) {
    		return $block_content;
    	}
    	if ( ! class_exists( 'WP_HTML_Tag_Processor' ) ) {
    		return $block_content;
    	}

    	$processor = new WP_HTML_Tag_Processor( $block_content );
    	if ( ! $processor->next_tag( 'button' ) ) {
    		return $block_content;
    	}

    	foreach ( $entries as $entry ) {
    		$name = isset( $entry['name'] ) ? (string) $entry['name'] : '';
    		if ( '' === $name ) {
    			continue;
    		}
    		if ( null === $entry['value'] ) {
    			$processor->set_attribute( $name, true );
    			continue;
    		}
    		$processor->set_attribute( $name, (string) $entry['value'] );
    	}

    	return $processor->get_updated_html();
    }, 10, 4 );

== Changelog ==

= 1.0.0 =
* Initial release.
* HTML attributes panel in the block sidebar with `htmlbacAttributes` and `htmlbacInnerAttributes` (JSON objects).
* Merge duplicate attribute names; boolean attributes without `=`.
* Dynamic variable tokens on the front end.
* Inner element support for blocks such as Group, Image, and Video.
