diff --git a/.monorepo-source.json b/.monorepo-source.json
index 4a9da44..c1dea3a 100644
--- a/.monorepo-source.json
+++ b/.monorepo-source.json
@@ -1,7 +1,7 @@
{
"productId": "funkycommerce-headless",
"repository": "coded-letter/superfunky-theme",
- "source": "coded-letter/coded-letter-monorepo@d16d4008f1dc5c092d4866337e74d458994039a0",
+ "source": "coded-letter/coded-letter-monorepo@85e3fb0fe8ae963804a546effc243d1359feff2b",
"sourcePath": "workspace/backend/wordpress/themes/free/funkycommerce-headless",
"installSlug": "funkycommerce-headless",
"kind": "theme",
diff --git a/assets/video-hero-block.js b/assets/video-hero-block.js
index 1c305b9..acbbb1e 100644
--- a/assets/video-hero-block.js
+++ b/assets/video-hero-block.js
@@ -18,6 +18,7 @@
poster: { type: 'string', default: '' },
kicker: { type: 'string', default: '' },
title: { type: 'string', default: 'Video hero' },
+ headingLevel: { type: 'string', default: 'h2' },
description: { type: 'string', default: '' },
primaryCtaLabel: { type: 'string', default: '' },
primaryCtaHref: { type: 'string', default: '' },
@@ -52,6 +53,7 @@
el( SelectControl, { label: i18n.__( 'View', 'funkycommerce-headless' ), value: a.variant, options: [ { label: 'Full bleed', value: 'fullbleed' }, { label: 'Atmospheric glow', value: 'glow' }, { label: 'Split media/content', value: 'split' }, { label: 'Minimal editorial', value: 'minimal' }, { label: 'Compact strip', value: 'strip' } ], onChange: field( 'variant' ) } ),
el( TextControl, { label: i18n.__( 'Kicker', 'funkycommerce-headless' ), value: a.kicker, onChange: field( 'kicker' ) } ),
el( TextControl, { label: i18n.__( 'Heading', 'funkycommerce-headless' ), value: a.title, onChange: field( 'title' ) } ),
+ el( SelectControl, { label: i18n.__( 'Heading level', 'funkycommerce-headless' ), value: a.headingLevel, options: [ { label: 'H1', value: 'h1' }, { label: 'H2', value: 'h2' }, { label: 'H3', value: 'h3' }, { label: 'H4', value: 'h4' }, { label: 'H5', value: 'h5' }, { label: 'H6', value: 'h6' } ], onChange: field( 'headingLevel' ) } ),
el( TextareaControl, { label: i18n.__( 'Description', 'funkycommerce-headless' ), value: a.description, onChange: field( 'description' ) } ),
el( TextControl, { label: i18n.__( 'Primary button label', 'funkycommerce-headless' ), value: a.primaryCtaLabel, onChange: field( 'primaryCtaLabel' ) } ),
el( TextControl, { label: i18n.__( 'Primary button URL', 'funkycommerce-headless' ), value: a.primaryCtaHref, onChange: field( 'primaryCtaHref' ) } ),
diff --git a/functions.php b/functions.php
index f947bd2..f485c83 100644
--- a/functions.php
+++ b/functions.php
@@ -9,7 +9,7 @@
exit;
}
-define( 'FUNKYCOMMERCE_HEADLESS_VERSION', '1.2.15' );
+define( 'FUNKYCOMMERCE_HEADLESS_VERSION', '1.2.16' );
/**
* Whether Superfunky Pro is active and licensed.
@@ -424,6 +424,7 @@ function funkycommerce_content_shortcode_schemas() {
'poster' => array( 'default' => '', 'type' => 'url' ),
'kicker' => array( 'default' => '' ),
'title' => array( 'default' => __( 'Video hero', 'funkycommerce-headless' ) ),
+ 'heading_level' => array( 'default' => 'h2', 'enum' => array( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ) ),
'description' => array( 'default' => '' ),
'primary_cta_label' => array( 'default' => '' ),
'primary_cta_href' => array( 'default' => '', 'type' => 'url-path' ),
@@ -917,6 +918,7 @@ function funkycommerce_register_video_hero_block() {
'attributes' => array(
'src' => array( 'type' => 'string', 'default' => '' ), 'variant' => array( 'type' => 'string', 'default' => 'fullbleed' ), 'poster' => array( 'type' => 'string', 'default' => '' ),
'kicker' => array( 'type' => 'string', 'default' => '' ), 'title' => array( 'type' => 'string', 'default' => 'Video hero' ),
+ 'headingLevel' => array( 'type' => 'string', 'default' => 'h2' ),
'description' => array( 'type' => 'string', 'default' => '' ), 'primaryCtaLabel' => array( 'type' => 'string', 'default' => '' ),
'primaryCtaHref' => array( 'type' => 'string', 'default' => '' ), 'secondaryCtaLabel' => array( 'type' => 'string', 'default' => '' ),
'secondaryCtaHref' => array( 'type' => 'string', 'default' => '' ), 'align' => array( 'type' => 'string', 'default' => 'left' ),
@@ -932,7 +934,7 @@ function funkycommerce_register_video_hero_block() {
function funkycommerce_render_video_hero_block( $attributes ) {
$map = array(
- 'src' => 'src', 'variant' => 'variant', 'poster' => 'poster', 'kicker' => 'kicker', 'title' => 'title', 'description' => 'description',
+ 'src' => 'src', 'variant' => 'variant', 'poster' => 'poster', 'kicker' => 'kicker', 'title' => 'title', 'headingLevel' => 'heading_level', 'description' => 'description',
'primaryCtaLabel' => 'primary_cta_label', 'primaryCtaHref' => 'primary_cta_href',
'secondaryCtaLabel' => 'secondary_cta_label', 'secondaryCtaHref' => 'secondary_cta_href',
'align' => 'align', 'height' => 'height', 'overlayOpacity' => 'overlay_opacity',
diff --git a/inc/control-center.php b/inc/control-center.php
index b0f8ba4..2763492 100644
--- a/inc/control-center.php
+++ b/inc/control-center.php
@@ -508,7 +508,7 @@ function funkycommerce_sanitize_control_center( $input ) {
$output['backend_noindex_acknowledged'] = $previous['backend_noindex_acknowledged'] ?? 'no';
}
- $languages = function_exists( 'funkycommerce_available_language_slugs' ) ? funkycommerce_available_language_slugs() : array();
+ $languages = funkycommerce_control_center_language_slugs();
foreach ( $languages as $language ) {
$tagline_key = 'store_tagline_' . $language;
$promo_key = 'promo_text_' . $language;
@@ -533,6 +533,22 @@ function funkycommerce_sanitize_control_center( $input ) {
return $output;
}
+/**
+ * Return Polylang languages or the site's default language for editable UI strings.
+ */
+function funkycommerce_control_center_language_slugs() {
+ $languages = function_exists( 'funkycommerce_available_language_slugs' ) ? funkycommerce_available_language_slugs() : array();
+ if ( $languages ) {
+ return $languages;
+ }
+ if ( function_exists( 'funkycommerce_site_default_language_slug' ) ) {
+ return array( funkycommerce_site_default_language_slug() );
+ }
+ $locale = (string) get_option( 'WPLANG', '' );
+ $slug = sanitize_key( strtolower( strtok( $locale ?: 'en_US', '_-' ) ) );
+ return array( $slug ?: 'en' );
+}
+
/**
* Keep old option consumers working while the rest of the backend is migrated.
*/
@@ -762,6 +778,7 @@ function funkycommerce_storefront_control_settings( $language = '' ) {
'quietSeconds' => max( 2, min( 300, (int) ( $settings['recent_orders_quiet_seconds'] ?? 8 ) ) ),
'openLinksInNewTab' => 'yes' === ( $settings['recent_orders_links_new_tab'] ?? 'yes' ),
),
+ 'soundsEnabled' => 'yes' === ( $settings['sounds_enabled'] ?? 'no' ),
'loading' => array(
'enabled' => 'yes' === ( $settings['loader_enabled'] ?? 'yes' ),
'customUrl' => esc_url_raw( $settings['loader_custom_url'] ?? '' ),
@@ -1378,16 +1395,22 @@ function funkycommerce_render_control_center() {
$field ) : funkycommerce_render_control_field( $key, $field, $settings[ $key ] ?? ( $field['default'] ?? '' ) ); endforeach; ?>
-
+
-
-
+
+
__( 'Store tagline', 'funkycommerce-headless' ), 'type' => 'text' ), $settings[ 'store_tagline_' . $slug ] ?? '' );
funkycommerce_render_control_field( 'promo_text_' . $slug, array( 'label' => __( 'Promotional message', 'funkycommerce-headless' ), 'type' => 'text' ), $settings[ 'promo_text_' . $slug ] ?? '' );
- $ui_strings = wp_json_encode( funkycommerce_storefront_ui_strings_for_language( $slug ), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
+ $ui_strings = wp_json_encode(
+ array_merge(
+ funkycommerce_storefront_ui_strings_for_language( 'en' ),
+ funkycommerce_storefront_ui_strings_for_language( $slug )
+ ),
+ JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE
+ );
funkycommerce_render_control_field( 'ui_strings_' . $slug, array( 'label' => __( 'Storefront UI strings', 'funkycommerce-headless' ), 'type' => 'json', 'default' => '{}' ), $ui_strings ?: '{}' );
?>
diff --git a/inc/multilingual-content.php b/inc/multilingual-content.php
index 821d4a2..14289b1 100644
--- a/inc/multilingual-content.php
+++ b/inc/multilingual-content.php
@@ -41,6 +41,15 @@ function funkycommerce_polylang_taxonomies( $taxonomies, $is_settings ) {
}
add_filter( 'pll_get_taxonomies', 'funkycommerce_polylang_taxonomies', 10, 2 );
+function funkycommerce_site_default_language_slug() {
+ $locale = (string) get_option( 'WPLANG', '' );
+ if ( '' === $locale ) {
+ $locale = 'en_US';
+ }
+ $slug = sanitize_key( strtolower( strtok( $locale, '_-' ) ) );
+ return $slug ?: 'en';
+}
+
function funkycommerce_available_language_slugs() {
if ( ! function_exists( 'pll_languages_list' ) ) {
return array();
@@ -274,6 +283,13 @@ function funkycommerce_language_data( $slug ) {
'locale' => '',
);
if ( ! function_exists( 'pll_languages_list' ) ) {
+ $locale = (string) get_option( 'WPLANG', '' );
+ $locale = $locale ?: 'en_US';
+ if ( funkycommerce_site_default_language_slug() === $slug ) {
+ $translations = function_exists( 'wp_get_available_translations' ) ? wp_get_available_translations() : array();
+ $data['name'] = (string) ( $translations[ $locale ]['native_name'] ?? $locale );
+ $data['locale'] = $locale;
+ }
return $data;
}
diff --git a/inc/native-shortcodes.php b/inc/native-shortcodes.php
index 6f9fd59..d1ffdfb 100644
--- a/inc/native-shortcodes.php
+++ b/inc/native-shortcodes.php
@@ -965,9 +965,10 @@ function funkycommerce_native_render_video_hero( $atts ) {
$ctas = funkycommerce_native_cta_link( $a['primary_cta_label'], $a['primary_cta_href'], $a['primary_cta_target'], $a['primary_cta_rel'], 'funkycommerce-native-cta funkycommerce-native-cta--primary' );
$ctas .= funkycommerce_native_cta_link( $a['secondary_cta_label'], $a['secondary_cta_href'], $a['secondary_cta_target'], $a['secondary_cta_rel'], 'funkycommerce-native-cta funkycommerce-native-cta--secondary' );
$height = preg_match( '/^\d+(px|vh|%)$/', $a['height'] ) ? $a['height'] : '70vh';
+ $heading_tag = in_array( $a['heading_level'], array( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ), true ) ? $a['heading_level'] : 'h2';
return sprintf(
- '
%3$s%4$s%6$s
%7$s
%8$s%9$s',
+ '
%3$s%4$s%6$s<%18$s>%7$s%18$s>%8$s%9$s
',
esc_attr( $a['align'] ),
esc_attr( $height ),
$poster,
@@ -984,7 +985,8 @@ function funkycommerce_native_render_video_hero( $atts ) {
$a['muted'] ? '🔇' : '🔊',
$starts_playing ? esc_attr__( 'Pause background video', 'funkycommerce-headless' ) : esc_attr__( 'Play background video', 'funkycommerce-headless' ),
$starts_playing ? '❚❚' : '▶',
- esc_attr( $a['variant'] )
+ esc_attr( $a['variant'] ),
+ $heading_tag
);
}
diff --git a/inc/navigation-commerce.php b/inc/navigation-commerce.php
index 096426e..df81c46 100644
--- a/inc/navigation-commerce.php
+++ b/inc/navigation-commerce.php
@@ -492,6 +492,7 @@ function funkycommerce_register_navigation_commerce_graphql() {
'aiAssistant' => array( 'type' => array( 'non_null' => 'FunkyCommerceAiAssistant' ) ),
'footer' => array( 'type' => array( 'non_null' => 'FunkyCommerceFooter' ) ),
'recentOrders' => array( 'type' => array( 'non_null' => 'FunkyCommerceRecentOrders' ) ),
+ 'soundsEnabled' => array( 'type' => array( 'non_null' => 'Boolean' ) ),
'loading' => array( 'type' => array( 'non_null' => 'FunkyCommerceLoading' ) ),
'features' => array( 'type' => array( 'non_null' => 'FunkyCommerceFeatures' ) ),
'payments' => array( 'type' => array( 'non_null' => 'FunkyCommercePaymentPresentation' ) ),
@@ -544,6 +545,7 @@ function funkycommerce_register_navigation_commerce_graphql() {
'aiAssistant' => $controls['aiAssistant'],
'footer' => $controls['footer'],
'recentOrders' => $controls['recentOrders'],
+ 'soundsEnabled' => $controls['soundsEnabled'],
'loading' => $controls['loading'],
'features' => $controls['features'],
'payments' => $controls['payments'],
diff --git a/package-lock.json b/package-lock.json
index d6cfb4e..e142a6c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "funkycommerce-headless-theme",
- "version": "1.2.15",
+ "version": "1.2.16",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "funkycommerce-headless-theme",
- "version": "1.2.15",
+ "version": "1.2.16",
"devDependencies": {
"autoprefixer": "^10.4.20",
"esbuild": "^0.28.2",
diff --git a/package.json b/package.json
index 6549a9e..5ad199c 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "funkycommerce-headless-theme",
"private": true,
- "version": "1.2.15",
+ "version": "1.2.16",
"description": "Tailwind/build tooling for the FunkyCommerce Headless WordPress theme's native frontend assets (block templates, template parts, loader, and Spotify slot).",
"scripts": {
"build:css": "tailwindcss -i ./assets/css/theme-source.css -o ./assets/dist/theme.css --minify",
diff --git a/readme.txt b/readme.txt
index debd107..10f58dd 100644
--- a/readme.txt
+++ b/readme.txt
@@ -4,7 +4,7 @@ Tags: headless, woocommerce, wpgraphql, full-site-editing
Requires at least: 6.7
Tested up to: 6.8
Requires PHP: 7.4
-Stable tag: 1.2.15
+Stable tag: 1.2.16
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
@@ -15,6 +15,11 @@ Control Center, the theme also ships a complete native WordPress rendering path
"Native frontend theme" below) with accessible header/footer/navigation templates and
core front/home/singular/archive/search/404 routes styled to match the storefront.
+== 1.2.16 highlights ==
+
+* Adds validated H1-H6 controls to video heroes and keeps static hero heading levels consistent in native and headless rendering.
+* Fixes root fragment links from nested routes, honors the interaction-sound toggle, and exposes default-language UI strings without requiring Polylang.
+
== 1.2.15 highlights ==
* Disables automatic paragraph and line-break insertion across WordPress-rendered content.
diff --git a/style.css b/style.css
index 6bd4b8f..0259556 100644
--- a/style.css
+++ b/style.css
@@ -7,7 +7,7 @@ Description: A minimal, content-first block theme for polished WordPress preview
Requires at least: 6.7
Tested up to: 6.8
Requires PHP: 7.4
-Version: 1.2.15
+Version: 1.2.16
Update URI: https://github.com/coded-letter/superfunky-theme
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html