Skip to content

fix: generic UNSET_SENTINEL for child-device override with "none/default" values#16

Draft
Copilot wants to merge 2 commits into
developfrom
copilot/fix-mobile-alignment-issue
Draft

fix: generic UNSET_SENTINEL for child-device override with "none/default" values#16
Copilot wants to merge 2 commits into
developfrom
copilot/fix-mobile-alignment-issue

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 22, 2026

On tablet/mobile, selecting "none" for alignment or "default" for font-size stored a deletion (null) in the responsive store, causing the property to disappear and inherit the parent device's value instead of being explicitly overridden.

Root cause

null is the deepMerge deletion sentinel — storing it on a child device removes the key, making the override invisible.

Approach: UNSET_SENTINEL = '__rwp_unset__'

A string sentinel that survives JSON round-trips and signals "explicitly none on this device."

JS — intercept-set-attributes.js

  • splitAttributeUpdates now receives currentDevice and passes it down
  • On child devices, three code paths now write UNSET_SENTINEL instead of null:
    • Explicit undefined reset (setAttributes({ textAlign: undefined }))
    • Empty-string normalization (source === '')
    • Removed nested properties (collectRemovedResponsivePathsForObjectUpdate)
  • On desktop, behavior is unchanged (null / deletion)

JS — apply-responsive-settings.js

  • buildNativeAttributeUpdates: skips sentinel values — doesn't write '__rwp_unset__' as a native block attribute
  • buildNativeAttributeUpdatesForDevice: treats effectiveValue === UNSET_SENTINEL as absent, so the removal loop fires and properly unsets the attribute (undefined for flat props, deleteNestedValue for nested)

PHP — responsive-settings.php

  • Defines RESPONSIVE_SETTINGS_UNSET_SENTINEL = '__rwp_unset__' globally before autoload

PHP — all settings/*/css.php

Each generator now detects the sentinel and emits property: unset !important, which beats any parent-device !important rule:

// Before
if ( isset( $alignment['textAlign'] ) && $alignment['textAlign'] !== null && $alignment['textAlign'] !== '' ) {
    $rules .= '    text-align: ' . $alignment['textAlign'] . " !important;\n";
}

// After
if ( isset( $alignment['textAlign'] ) ) {
    if ( defined( 'RESPONSIVE_SETTINGS_UNSET_SENTINEL' ) && $alignment['textAlign'] === RESPONSIVE_SETTINGS_UNSET_SENTINEL ) {
        $rules .= "    text-align: unset !important;\n";
    } elseif ( $alignment['textAlign'] !== null && $alignment['textAlign'] !== '' ) {
        $rules .= '    text-align: ' . $alignment['textAlign'] . " !important;\n";
    }
}

Sentinel passes through SettingsValidator::sanitizePropertyValue unchanged (sanitize_text_field('__rwp_unset__') === '__rwp_unset__').

Copilot AI and others added 2 commits April 22, 2026 19:09
…alues for alignment and font-size

Agent-Logs-Url: https://github.com/maxpertici/responsive-settings/sessions/878f9e24-6651-4e51-a7cb-c95586d84010

Co-authored-by: maxpertici <48124450+maxpertici@users.noreply.github.com>
@maxpertici
Copy link
Copy Markdown
Owner

@copilot T es sur de l approche ? J'ai pas besoin de reset le css, je parle des valeurs des styles gut par defaut

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 23, 2026

@copilot T es sur de l approche ? J'ai pas besoin de reset le css, je parle des valeurs des styles gut par defaut

Oui, les deux parties (JS + CSS) sont nécessaires pour des raisons différentes :

Pourquoi le sentinel JS ?
Gutenberg exprime "aucun alignement" / "taille par défaut" via setAttributes({ align: undefined }). Le deepMerge du store supprimait les clés null, donc le mobile héritait de la valeur desktop. Le sentinel __rwp_unset__ permet de distinguer "pas de surcharge" vs "surcharge explicite vers none/default".

Pourquoi le CSS ?
Le CSS desktop du plugin n'a pas de media query — il s'applique à toutes les résolutions :

[data-rwp-id="x"] { max-width: var(--wp--style--global--wide-size) !important; }

Sans CSS mobile, ce !important s'applique aussi sur mobile, peu importe la valeur de l'attribut natif. Le property: unset !important en mobile annule la règle desktop.

Pourquoi unset plutôt qu'une valeur spécifique ?

  • font-size: unsetfont-size est hérité → revient à la valeur parent (comportement Gutenberg par défaut ✓)
  • max-width: unsetmax-width n'est pas hérité → revient à initial = aucune contrainte → la mise en page contrainte Gutenberg (has-global-padding) applique naturellement la content-size ✓
  • text-align: unsettext-align est hérité → revient à la valeur parent ✓

C'est bien la "valeur par défaut des styles Gutenberg" dans tous les cas — unset délègue au comportement naturel de la cascade CSS.

Si tu as un scénario où le résultat n'est pas le bon, partage-le et j'ajusterai.

Copilot AI requested a review from maxpertici April 23, 2026 05:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants