css
.ribbon {
    padding: 5px!important;
    inline-size: var(--ribbon-width, 190px)!important;
    font-size: 12px!important;
    color: light-dark(oklch(0.95 0.031 235.3983149013),oklch(0.95 0.031 235.3983149013))!important;
    letter-spacing: 1px!important;
}
php
<?php

add_filter( 'allowed_block_types_all', function( $allowed, $context ) {

	/**
	 * 1) Never restrict the Site Editor (templates need access to layout blocks).
	 *    This mirrors WordPress Dev Blog guidance to avoid breaking the Site Editor.
	 */
	if ( isset( $context->post ) && $context->post ) {
		$post_type = $context->post->post_type;

		// Site Editor post types.
		if ( in_array( $post_type, array( 'wp_template', 'wp_template_part' ), true ) ) {
			return true;
		}

		// 2) Only restrict blocks on blog posts.
		if ( 'post' !== $post_type ) {
			return $allowed;
		}
	} else {
		// Fallback detection for Site Editor screens.
		if ( function_exists( 'get_current_screen' ) ) {
			$screen = get_current_screen();
			if ( $screen && 'site-editor' === $screen->id ) {
				return true;
			}
		}
		if ( isset( $context->name ) && 'core/edit-site' === $context->name ) {
			return true;
		}

		// If we don't know what we're editing, don't be aggressive.
		return $allowed;
	}

	/**
	 * 3) Essential core blocks for writing blog posts.
	 *    (Keep paragraph — Gutenberg expects it as a default appender in many flows.)
	 */
	$essential_core_blocks = array(
		'core/paragraph',
		'core/heading',
		'core/list',
		'core/list-item',     // inner block for list
		'core/quote',
		'core/pullquote',

		'core/image',
		'core/gallery',
		'core/video',
		'core/audio',
		'core/file',

		'core/table',

		'core/code',
		'core/preformatted',
		'core/html',
		'core/shortcode',

		'core/buttons',
		'core/button',        // inner block for buttons

		// Embeds (variations hang off this in modern WP)
		'core/embed',

		// Reusable block / synced pattern block (optional, but handy):
		'core/block',
	);

	/**
	 * 4) Collect Etch blocks.
	 *    We try to detect them without you needing to know the namespace:
	 *    - common namespaces like etch/ or etchwp/
	 *    - category containing "etch"
	 *    - render callback class/function containing "etch"
	 *    - script/style handles containing "etch"
	 */
	$registry = WP_Block_Type_Registry::get_instance();
	$types    = $registry->get_all_registered();

	$etch_blocks = array();

	$maybe_etch_namespaces = array( 'etch', 'etchwp' ); // add more if needed

	foreach ( $types as $name => $type ) {
		$is_etch = false;

		// Namespace check.
		foreach ( $maybe_etch_namespaces as $ns ) {
			if ( 0 === strpos( $name, $ns . '/' ) ) {
				$is_etch = true;
				break;
			}
		}

		// Category check.
		if ( ! $is_etch && isset( $type->category ) && is_string( $type->category ) ) {
			if ( false !== stripos( $type->category, 'etch' ) ) {
				$is_etch = true;
			}
		}

		// Render callback check.
		if ( ! $is_etch && isset( $type->render_callback ) && $type->render_callback ) {
			$cb = $type->render_callback;

			if ( is_string( $cb ) && false !== stripos( $cb, 'etch' ) ) {
				$is_etch = true;
			} elseif ( is_array( $cb ) && ! empty( $cb[0] ) ) {
				$class = is_object( $cb[0] ) ? get_class( $cb[0] ) : ( is_string( $cb[0] ) ? $cb[0] : '' );
				if ( $class && false !== stripos( $class, 'etch' ) ) {
					$is_etch = true;
				}
			}
		}

		// Script/style handle check (WP 6.1+ uses *_handles, older props are deprecated but may exist).
		if ( ! $is_etch ) {
			$handle_props = array(
				'editor_script_handles',
				'script_handles',
				'view_script_handles',
				'editor_style_handles',
				'style_handles',
				'view_style_handles',

				// Deprecated props (still sometimes populated depending on registration).
				'editor_script',
				'script',
				'view_script',
				'editor_style',
				'style',
				'view_style',
			);

			foreach ( $handle_props as $prop ) {
				if ( empty( $type->$prop ) ) {
					continue;
				}
				foreach ( (array) $type->$prop as $h ) {
					if ( is_string( $h ) && false !== stripos( $h, 'etch' ) ) {
						$is_etch = true;
						break 2;
					}
				}
			}
		}

		if ( $is_etch ) {
			$etch_blocks[] = $name;
		}
	}

	$etch_blocks = array_values( array_unique( $etch_blocks ) );

	/**
	 * 5) Fallback:
	 *    If we didn’t detect any Etch blocks (detection failed, or nothing registered yet),
	 *    allow all NON-core blocks so you don’t lose Etch blocks.
	 *    (This is safer than accidentally locking yourself out.)
	 */
	if ( empty( $etch_blocks ) ) {
		$non_core = array();
		foreach ( array_keys( $types ) as $block_name ) {
			if ( 0 !== strpos( $block_name, 'core/' ) ) {
				$non_core[] = $block_name;
			}
		}
		$etch_blocks = $non_core;
	}

	return array_values( array_unique( array_merge( $essential_core_blocks, $etch_blocks ) ) );

}, 10, 2 );
html
<script async src="https://scripts.withcabin.com/hello.js"></script>
js
// Add your JS Code here, don't include the <script> tags

document.addEventListener('DOMContentLoaded', () => {
  const nav = document.querySelector('.etch-nav');

  if (!nav) return;

  const updateBodyClass = () => {
    const expanded = nav.getAttribute('aria-expanded') === 'true';
    document.body.classList.toggle('nav-open', expanded);
  };

  // Initial state
  updateBodyClass();

  // Watch attribute changes
  const observer = new MutationObserver(updateBodyClass);
  observer.observe(nav, {
    attributes: true,
    attributeFilter: ['aria-expanded']
  });
});