Elliot Bear released a plugin called Drypoint which is very similar to Etch Enhancer but free. One of the most requested feature is the ability to load the ACSS color variables into their color chooser (Etch Enhancer already does this). The short-term work around is a code snippet that reads the generated ACSS files and appends the variable to the default variable collection in the style manager. This duplicates the variable entry so if you are worried about the few bytes that are added, you should only use this in a dev environment.

The code

<?php
/**
 * Plugin Name: ACSS Variables for Etch
 * Description: Keeps Automatic.css palette variables synchronized with Etch's default variables.
 * Version: 1.0.0
 * Requires PHP: 8.1
 */

defined( 'ABSPATH' ) || exit;

final class ACSS_Etch_Variable_Sync {

	private const DEFAULT_COLLECTION = 'default';
	private const DEFAULT_STYLE_ID   = 'etch-global-variable-style';
	private const LEGACY_STYLE_ID    = 'acss-variable-style';
	private const SIGNATURE_OPTION = 'acss_etch_variable_sync_signature';
	private const START_MARKER     = '/* ACSS VARIABLES: START - managed automatically */';
	private const END_MARKER       = '/* ACSS VARIABLES: END */';

	/**
	 * ACSS palette families available in Automatic.css 4.
	 *
	 * The filter allows future ACSS releases or site-specific additions to add
	 * another family without changing this snippet.
	 */
	private const COLOR_FAMILIES = array(
		'primary',
		'secondary',
		'tertiary',
		'accent',
		'base',
		'neutral',
		'danger',
		'warning',
		'info',
		'success',
	);

	/**
	 * Register synchronization hooks.
	 */
	public static function boot(): void {
		// Covers normal page loads, first installation, and manually replaced CSS files.
		add_action( 'init', array( __CLASS__, 'maybe_sync' ), 40 );

		// ACSS 4 fires this after its stylesheets have been written.
		add_action(
			'automaticcss_after_generate_framework_css',
			array( __CLASS__, 'sync_after_generation' ),
			100,
			1
		);
	}

	/**
	 * Force a sync after ACSS regenerates its CSS.
	 *
	 * @param array<mixed> $unused_variables ACSS framework variables supplied by the hook.
	 */
	public static function sync_after_generation( array $unused_variables = array() ): void {
		unset( $unused_variables );
		self::sync( true );
	}

	/**
	 * Synchronize only when the generated ACSS file or Etch variables changed.
	 */
	public static function maybe_sync(): void {
		self::sync( false );
	}

	/**
	 * Import the generated ACSS palette into Etch.
	 *
	 * @param bool $force Ignore the saved file signature.
	 * @return bool Whether a usable ACSS palette was found.
	 */
	private static function sync( bool $force ): bool {
		if ( ! defined( 'ACSS_PLUGIN_FILE' ) || ! defined( 'ETCH_PLUGIN_FILE' ) ) {
			return false;
		}

		$css_file = self::find_acss_variables_file();
		if ( null === $css_file ) {
			return false;
		}

		clearstatcache( true, $css_file );
		$signature = (string) filemtime( $css_file ) . ':' . (string) filesize( $css_file );
		$styles   = get_option( 'etch_styles', array() );
		$styles   = is_array( $styles ) ? $styles : array();
		$style_id = self::find_default_style_id( $styles );

		if (
			! $force
			&& get_option( self::SIGNATURE_OPTION, '' ) === $signature
			&& null !== $style_id
			&& self::contains_managed_block( $styles[ $style_id ] )
			&& ! self::has_legacy_collection( $styles )
		) {
			return true;
		}

		$css = file_get_contents( $css_file );
		if ( ! is_string( $css ) || '' === trim( $css ) ) {
			return false;
		}

		$variables = self::parse_color_variables( $css );
		if ( empty( $variables ) ) {
			// Never erase the last good block because of a missing/broken ACSS file.
			return false;
		}

		$style_id = $style_id ?? self::available_default_style_id( $styles );
		$style    = isset( $styles[ $style_id ] ) && is_array( $styles[ $style_id ] )
			? $styles[ $style_id ]
			: array(
				'collection' => self::DEFAULT_COLLECTION,
				'selector'   => ':root',
				'css'        => '',
				'readonly'   => false,
				'type'       => 'element',
			);

		$existing_css = isset( $style['css'] ) && is_string( $style['css'] ) ? $style['css'] : '';
		$user_css     = self::remove_managed_block( $existing_css );
		$managed_css  = self::format_managed_block( $variables );

		$style['collection'] = self::DEFAULT_COLLECTION;
		$style['selector']   = ':root';
		$style['css']        = '' === $user_css ? $managed_css : $user_css . "\n\n" . $managed_css;
		$style['readonly']   = false;
		$style['type']       = 'element';
		$styles[ $style_id ] = $style;

		self::remove_legacy_collection( $styles );

		if ( get_option( 'etch_styles', array() ) !== $styles ) {
			update_option( 'etch_styles', $styles );
		}

		update_option( self::SIGNATURE_OPTION, $signature, false );

		/**
		 * Fires after Etch's default variables have been synchronized.
		 *
		 * @param int    $count    Number of imported variables.
		 * @param string $css_file Source ACSS stylesheet.
		 */
		do_action( 'acss_etch_variable_sync_complete', count( $variables ), $css_file );

		return true;
	}

	/**
	 * Locate the focused ACSS variables file, with automatic.css as a fallback.
	 */
	private static function find_acss_variables_file(): ?string {
		$uploads = wp_upload_dir( null, false );
		if ( ! empty( $uploads['error'] ) || empty( $uploads['basedir'] ) ) {
			return null;
		}

		$directory = trailingslashit( $uploads['basedir'] ) . 'automatic-css/';
		$candidates = array(
			$directory . 'automatic-variables.css',
			$directory . 'automatic.css',
		);

		foreach ( $candidates as $candidate ) {
			if ( is_readable( $candidate ) ) {
				return $candidate;
			}
		}

		return null;
	}

	/**
	 * Find Etch's existing :root style in the default collection.
	 *
	 * @param array<string, mixed> $styles Etch global styles.
	 */
	private static function find_default_style_id( array $styles ): ?string {
		if (
			isset( $styles[ self::DEFAULT_STYLE_ID ] )
			&& is_array( $styles[ self::DEFAULT_STYLE_ID ] )
			&& self::DEFAULT_COLLECTION === ( $styles[ self::DEFAULT_STYLE_ID ]['collection'] ?? null )
			&& ':root' === ( $styles[ self::DEFAULT_STYLE_ID ]['selector'] ?? null )
		) {
			return self::DEFAULT_STYLE_ID;
		}

		foreach ( $styles as $id => $style ) {
			if (
				is_string( $id )
				&& is_array( $style )
				&& self::DEFAULT_COLLECTION === ( $style['collection'] ?? null )
				&& ':root' === ( $style['selector'] ?? null )
			) {
				return $id;
			}
		}

		return null;
	}

	/**
	 * Return a deterministic default-style ID without overwriting another style.
	 *
	 * @param array<string, mixed> $styles Etch global styles.
	 */
	private static function available_default_style_id( array $styles ): string {
		$id     = self::DEFAULT_STYLE_ID;
		$suffix = 2;

		while ( isset( $styles[ $id ] ) ) {
			$id = self::DEFAULT_STYLE_ID . '-' . $suffix;
			++$suffix;
		}

		return $id;
	}

	/**
	 * Confirm that the default style contains our complete managed block.
	 *
	 * @param mixed $style Etch style record.
	 */
	private static function contains_managed_block( $style ): bool {
		return is_array( $style )
			&& self::DEFAULT_COLLECTION === ( $style['collection'] ?? null )
			&& ':root' === ( $style['selector'] ?? null )
			&& is_string( $style['css'] ?? null )
			&& str_contains( $style['css'], self::START_MARKER )
			&& str_contains( $style['css'], self::END_MARKER );
	}

	/**
	 * Detect a standalone collection created by version 1.0 of this snippet.
	 *
	 * @param array<string, mixed> $styles Etch global styles.
	 */
	private static function has_legacy_collection( array $styles ): bool {
		foreach ( $styles as $id => $style ) {
			if (
				is_string( $id )
				&& str_starts_with( $id, self::LEGACY_STYLE_ID )
				&& is_array( $style )
				&& in_array( $style['collection'] ?? '', array( 'ACSS Variable', 'ACSS Variables' ), true )
				&& ':root' === ( $style['selector'] ?? null )
			) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Remove the obsolete standalone ACSS collection after merging its values.
	 *
	 * @param array<string, mixed> $styles Etch global styles, passed by reference.
	 */
	private static function remove_legacy_collection( array &$styles ): void {
		foreach ( $styles as $id => $style ) {
			if (
				is_string( $id )
				&& str_starts_with( $id, self::LEGACY_STYLE_ID )
				&& is_array( $style )
				&& in_array( $style['collection'] ?? '', array( 'ACSS Variable', 'ACSS Variables' ), true )
				&& ':root' === ( $style['selector'] ?? null )
			) {
				unset( $styles[ $id ] );
			}
		}
	}

	/**
	 * Parse only ACSS palette tokens that are present in the generated CSS.
	 *
	 * Includes base colors, all generated shades, hover colors, black/white,
	 * and the optional "-ref" tokens generated by ACSS color-scheme settings.
	 *
	 * @return array<string, string>
	 */
	private static function parse_color_variables( string $css ): array {
		$css = preg_replace( '~/\*.*?\*/~s', '', $css ) ?? $css;
		$variables = array();

		if (
			! preg_match_all(
				'/((?:--)[a-zA-Z0-9_-]+)\s*:\s*([^;}{]+);/',
				$css,
				$matches,
				PREG_SET_ORDER
			)
		) {
			return $variables;
		}

		foreach ( $matches as $match ) {
			$name  = strtolower( trim( $match[1] ) );
			$value = trim( $match[2] );

			if ( '' !== $value && self::is_palette_variable( $name, $value ) ) {
				$variables[ $name ] = $value;
			}
		}

		return $variables;
	}

	/**
	 * Decide whether an emitted custom property is an ACSS palette variable.
	 */
	private static function is_palette_variable( string $name, string $value ): bool {
		$families = apply_filters( 'acss_etch_variable_color_families', self::COLOR_FAMILIES );
		$families = is_array( $families ) ? array_map( 'sanitize_key', $families ) : self::COLOR_FAMILIES;

		$is_palette_variable = in_array( $name, array( '--black', '--white' ), true );

		if ( ! $is_palette_variable ) {
			foreach ( $families as $family ) {
				$family = preg_quote( $family, '/' );
				if (
					preg_match(
						'/^--' . $family . '(?:-(?:hover|ultra-light|light|semi-light|semi-dark|dark|ultra-dark))?(?:-ref)?$/',
						$name
					)
				) {
					$is_palette_variable = true;
					break;
				}
			}
		}

		/**
		 * Filter individual variables for custom ACSS extensions.
		 *
		 * @param bool   $is_palette_variable Import decision.
		 * @param string $name                CSS custom property, including "--".
		 * @param string $value               Generated CSS value.
		 */
		return (bool) apply_filters(
			'acss_etch_color_variable_is_allowed',
			$is_palette_variable,
			$name,
			$value
		);
	}

	/**
	 * Remove only the block previously generated by this snippet.
	 */
	private static function remove_managed_block( string $css ): string {
		$pattern = '~(?:\R\s*)?'
			. preg_quote( self::START_MARKER, '~' )
			. '.*?'
			. preg_quote( self::END_MARKER, '~' )
			. '(?:\s*\R)?~s';

		$cleaned = preg_replace( $pattern, '', $css ) ?? $css;

		return trim( $cleaned );
	}

	/**
	 * Format the managed block with a comment for every emitted color family.
	 *
	 * @param array<string, string> $variables Parsed ACSS variables.
	 */
	private static function format_managed_block( array $variables ): string {
		$groups = array();

		foreach ( self::COLOR_FAMILIES as $family ) {
			$groups[ $family ] = array();
		}
		$groups['black-white'] = array();
		$groups['other']       = array();

		foreach ( $variables as $name => $value ) {
			$matched = false;

			foreach ( self::COLOR_FAMILIES as $family ) {
				if ( str_starts_with( $name, '--' . $family ) ) {
					$groups[ $family ][ $name ] = $value;
					$matched = true;
					break;
				}
			}

			if ( ! $matched && in_array( $name, array( '--black', '--white' ), true ) ) {
				$groups['black-white'][ $name ] = $value;
				$matched = true;
			}

			if ( ! $matched ) {
				$groups['other'][ $name ] = $value;
			}
		}

		$labels = array(
			'black-white' => 'Black & White',
			'other'       => 'Other ACSS Colors',
		);
		$lines = array( self::START_MARKER );

		foreach ( $groups as $family => $family_variables ) {
			if ( empty( $family_variables ) ) {
				continue;
			}

			$label   = $labels[ $family ] ?? ucwords( str_replace( '-', ' ', $family ) );
			$lines[] = '';
			$lines[] = '/* ' . $label . ' */';

			foreach ( $family_variables as $name => $value ) {
				$lines[] = $name . ': ' . $value . ';';
			}
		}

		$lines[] = '';
		$lines[] = self::END_MARKER;

		return implode( "\n", $lines );
	}
}

ACSS_Etch_Variable_Sync::boot();