WordPress / gutenberg

The Block Editor project for WordPress and beyond. Plugin is available from the official repository.
https://wordpress.org/gutenberg/
Other
10.49k stars 4.19k forks source link

Differing CSS specificity between Non-Iframed Editor and Front end #63925

Closed wongjn closed 2 months ago

wongjn commented 3 months ago

Description

With non-iframed block editor, specificity of top-level element selectors are different relative to other selectors. I believe this is due to the .editor-style-wrapper prefix.

Step-by-step reproduction instructions

  1. Create theme using code snippets below.
  2. Activate the created theme.
  3. Edit a page or post.
  4. Add a Heading block, with default attributes and any text content.
  5. Save.
  6. Observe the font size.
  7. Top right 3-dots menu → Preferences → Enable "Custom Fields".
  8. Hit Show and Reload Page button (to enable the non-iframe editor).
  9. Observe the heading block size is different to step 5.
  10. On the front end, observe the heading block size is the same as 5.

Screenshots, screen recording, code snippet

Theme

theme.json
{
  "$schema": "https://schemas.wp.org/wp/6.6/theme.json",
  "version": 3,
  "styles": {
    "blocks": {
      "core/heading": {
        "typography": {
          "fontSize": "var(--wp--preset--font-size--large)"
        }
      }
    },
    "elements": {
      "heading": {
        "typography": {
          "fontSize": "inherit"
        }
      }
    }
  }
}
style.css
/*
Theme Name: Test
*/
templates/index.html
<!-- wp:post-content /-->

Observed behavior

  Front end Iframe editor Non-iframe editor
Screenshot ![image](https://github.com/user-attachments/assets/b5d60206-8bf8-447c-922b-0abc6541de91) ![image](https://github.com/user-attachments/assets/dee00014-85a9-40af-8b46-25feca143037) ![image](https://github.com/user-attachments/assets/f33603a3-18ea-4bee-84f8-f3fc76197fe2)
CSS ```css :root :where(.wp-block-heading) { font-size: var(--wp--preset--font-size--large); } h1, h2, h3, h4, h5, h6 { font-size: inherit; } ``` ```css :root :where(.wp-block-heading) { font-size: var(--wp--preset--font-size--large); } h1, h2, h3, h4, h5, h6 { font-size: inherit; } ``` ```css .editor-styles-wrapper h1, .editor-styles-wrapper h2, .editor-styles-wrapper h3 .editor-styles-wrapper h4, .editor-styles-wrapper h5, .editor-styles-wrapper h6 { font-size: inherit; } .editor-styles-wrapper :where(.wp-block-heading) { font-size: var(--wp--preset--font-size--large); } ```
Analysis ✅ `core/heading` block styles override top-level element `heading` styles. ✅ `core/heading` block styles override top-level element `heading` styles. ❌ Top-level element `heading` styles override `core/heading` block styles. Element `heading` styles have specificity (0, 1, 1) whereas `core/heading` block styles have (0, 1, 0).

Environment info

Please confirm that you have searched existing issues in the repo.

Please confirm that you have tested with all plugins deactivated except Gutenberg.

talldan commented 3 months ago

Thanks for reporting this. I think it's a similar issue to the one mentioned here: https://github.com/WordPress/gutenberg/issues/53468#issuecomment-2241279024

Though the selectors you mention are different.

Something to note is that the iframe is there for a reason, it prevents outside styles leaking in and the inside styles leaking out. Without it some extra specificity may naturally be required and the editor-styles-wrapper is needed to scope the canvas styles.

That said, it shouldn't change from WordPress release to release unless there's a good reason, so we can investigate if there's been a change, and if so try to fix things in another WordPress minor release.

I also wonder if it's worth looking at making the .editor-styles-wrapper part of the selector :where(.editor-styles-wrapper) so that it doesn't bump specificity so much.

ktmn commented 3 months ago

Something to note is that the iframe is there for a reason, it prevents outside styles leaking in and the inside styles leaking out.

But why is the non-iframed editor there? By default I have the iframed editor, but when I install a plugin that adds metaboxes it switches to non-iframed. But then when I switch to Tablet/Mobile preview it's iframed again and works perfectly. So why doesn't the editor just switch to an iframed "Desktop" preview right off the bat so I never have to even think about the non-iframed editor?

talldan commented 3 months ago

But then when I switch to Tablet/Mobile preview it's iframed again and works perfectly.

It's a good point! I was curious about it too. I had a look through some past issues and the discussion here outlines the reasoning - https://github.com/WordPress/gutenberg/pull/48286#issuecomment-1709587836

TLDR: There are no big metabox compatibility issues, it's more about finding a UI that isn't clunky as the default experience.

A solution has been attempted in https://github.com/WordPress/gutenberg/pull/59242, but there's a lot of discussion there and a blocking review.

When using third party blocks you may also have some that are using an apiVersion: 1 or apiVersion: 2 that force the editor to be non-iframed, and I believe the reason is that those blocks may have compatibility issues with the way they load assets.

talldan commented 3 months ago

I've tested this on WordPress 6.5 and it's showing the same behavior as 6.6, so it doesn't seem like this is a recent regression, but it is still a bug.

Ignore that - I accidentally test with the Gutenberg plugin active on my 6.5 environment. I can confirm it was a regression in 6.6, so hopefully it's something we can put a fix out for in a minor WordPress release (6.6.2 or 6.6.3).

FWIW, it seems like there's more momentum on solving the custom fields issue - https://github.com/WordPress/gutenberg/pull/64247.

talldan commented 3 months ago

64266 is also reporting the same issue, but with the Post Title block. I've closed that one as a duplicate.

mrleemon commented 2 months ago

Is this issue going to be fixed in 6.6.2, or is it scheduled for 6.7?

apmeyer commented 2 months ago

I am hoping this is addressed before 6.7. As noted, enabling plugins that add meta boxes (such ACF or Yoast) disables the iFramed editor for hybrid themes and puts us back into the situation where new CSS specificity is applying block spacing margins to things it should not be.

talldan commented 2 months ago

Is this issue going to be fixed in 6.6.2, or is it scheduled for 6.7?

It's unclear as of yet, the first thing is that a fix still needs to be worked on for the issue. It's one of the next things on my list unless someone is able to pick it up before me. 🙏

talldan commented 2 months ago

Here's a breakdown of the problem and some possible fixes. Sorry for the long comment, but hopefully it makes it clear exactly what has happened and provides some options:

6.5

The theme.json block styles previously output a very simple selector like this (specificity 0,1,0):

.wp-block-heading

This beat element styles. Those would be output like this when in an iframe (0,0,1):

h1, h2, h3, h4, h5, h6

When non-iframed, the selectors are prefixed (using postcss-prefixwrap) to scope the styles. This increased the specificity of the theme.json block styles (0,2,0):

.editor-visual-styles .wp-block-heading

It still beats the similarly prefixed element styles when there's no iframe (0,1,1):

.editor-visual-styles h1,
.editor-visual-styles h2,
.editor-visual-styles h3,
.editor-visual-styles h4,
.editor-visual-styles h5,
.editor-visual-styles h6

6.6

In 6.6 iframed editor the block style has changed to this, the same specificity (0,1,0):

:root :where(.wp-block-heading)

In the non-iframed editor, problems start to happen. The CSS prefixer behaves differently for selectors prefixed with :root, body, or html (the prefixer calls this 'root prefixing'), it instead replaces that part of the classname instead of prepending to avoid an invalid selector:

.editor-visual-styles :where(.wp-block-heading)

Now it stays at 0,1,0. Element styles are prefixed in the same way they were in 6.5 (0,1,1) so the non-iframed element styles now beat the block styles in terms of specificity.

Possible fixes

Change how root prefixing works

The main problem to me appears to be how the prefixing works. For some root prefixed selectors it keeps the specificity the same, but for others it increases the specificity and that causes inconsistencies in how styles are applied.

We could look at changing how the prefixing works, but that would be quite a big change, and would require a lot of testing. Possibly too big in scope for 6.6.2.

If we go this route, an option is to consider changing how root prefixing works so that it also results in a specificity bump (in this example, perhaps generating a selector like :root .editor-visual-styles :where(.wp-block-heading) with a specificity of 0,2,0 like in 6.5).

Make the prefixing result in no specificity bump

Similar to the above, another option is to try making the .editor-visual-styles prefix have no specificity bump by instead changing the prefix to :where(.editor-visual-styles). In the situation we're discussing above though, it results in a selector like :where(.editor-visual-styles) :where(.wp-block-heading), which has zero specificity, so it'd still require changes to how root prefixing works.

A risk is that styles that aren't supposed to apply to the editor canvas start to take precedence over block/theme styles.

Try adjusting the element style selectors like #64076 did

It might be possible to try a similar fix like in #64076. If the element selectors are changed to something like html :where(h1), they'd still have the same specificity when iframed. When non-iframed, the selector becomes .editor-visual-styles :where(h1), which is a modest reduction in specificity from (0,1,1) to (0,1,0). The element selectors have the same specificity as the block selectors so ordering has to be relied upon. A worry is that this might cause some other knock-on effects, so it'd require a lot of testing, but is smaller in scope. It feels a little hacky.

Would love to hear if anyone has more ideas on fixing this.