scripting / feedlandBlogrollToolkit

Browser-based JavaScript code, html and styles for adding an OPML-based blogroll to a website, with a connection back to FeedLand for realtime updates.
MIT License
3 stars 0 forks source link

Bootstrap styles colliding with themes #6

Open NickGreen opened 5 months ago

NickGreen commented 5 months ago

Synopsis

While testing the plugin on production sites, we noticed that when the plugin is activated, it is breaking the styles and layout of the themes. We've found that this is the result of bootstrap.css being so wide-ranging in its selectors.

We definitely don't want the activation of the plugin to collide so broadly with WordPress themes, and so we need to figure out how to get more specific with our CSS.

Screenshots

SCR-20240401-oaqx

SCR-20240401-oams

Possible solutions

  1. It might be possible to wrap the entire bootstrap CSS in a specific selector, like #divBlogrollContainer. Bootstrap 2.3.1, which is currently being loaded, used Less as a compiler, so we'd need to research how to get that working.
  2. We might be able to just pull out the specific styles from bootstrap.css that are needed for the blogrolls to work, such as the dropdowns and tooltips, and wrap those in a selector, and then get rid of the superfluous styles.
fmfernandes commented 5 months ago

I found a nice way of trying to find which CSS is being used or not, you can read more about it here: https://developer.chrome.com/docs/devtools/coverage.

So, using that method, I've set up a test site locally and added the blogroll plugin to a page and looked for the https://s3.amazonaws.com/scripting.com/code/includes/bootstrap.css file and ran a report for it. Looks like ~90% of the file contents are unused. If you click on the Download button (Download) Chrome will generate a .json file with ranges of unused bytes in the file.

I created a simple PHP script to parse those and echo only the used bytes:

<?php

$file = 'coverage.json';

$coverage_entries = json_decode( file_get_contents( $file ), true);

if ( ! isset( $argv[1] ) ) {
    echo 'Usage: php coverage.php <url>' . PHP_EOL;
    exit( 1 );
}

foreach ( $coverage_entries as $entry ) {
    if ( $entry['url'] !== $argv[1] ) {
        continue;
    }

    $used_css = '';
    foreach ( $entry['ranges'] as $range ) {
        $used_css .= substr( $entry['text'], $range['start'], $range['end'] - $range['start'] ) . PHP_EOL;
    }

    echo $used_css . PHP_EOL;
}

You'd use it like this: php coverage.php 'https://s3.amazonaws.com/scripting.com/code/includes /bootstrap.css?ver=1.0.0' while having downloaded the coverage report and saved it as coverage.json in the same folder as the PHP script.

This is the final output:

CSS file

```css article, aside, details, figcaption, figure, footer, header, hgroup, nav, section { display: block; } html { font-size: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } a:focus { outline: thin dotted #333; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } a:hover, a:active { outline: 0; } button, input, select, textarea { margin: 0; font-size: 100%; vertical-align: middle; } button, input { *overflow: visible; line-height: normal; } button, html input[type="button"], input[type="reset"], input[type="submit"] { cursor: pointer; -webkit-appearance: button; } label, select, button, input[type="button"], input[type="reset"], input[type="submit"], input[type="radio"], input[type="checkbox"] { cursor: pointer; } body { margin: 0; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; line-height: 20px; color: #333333; background-color: #ffffff; } a { color: #0088cc; text-decoration: none; } a:hover, a:focus { color: #005580; text-decoration: underline; } p { margin: 0 0 10px; } h1, h2, h3, h4, h5, h6 { margin: 10px 0; font-family: inherit; font-weight: bold; line-height: 20px; color: inherit; text-rendering: optimizelegibility; } h1, h2, h3 { line-height: 40px; } h1 { font-size: 38.5px; } h2 { font-size: 31.5px; } h3 { font-size: 24.5px; } ul, ol { padding: 0; margin: 0 0 10px 25px; } li { line-height: 20px; } label, input, button, select, textarea { font-size: 14px; font-weight: normal; line-height: 20px; } input, button, select, textarea { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; } table { max-width: 100%; background-color: transparent; border-collapse: collapse; border-spacing: 0; } .dropup, .dropdown { position: relative; } .dropdown-toggle { *margin-bottom: -3px; } .dropdown-toggle:active, .open .dropdown-toggle { outline: 0; } .dropdown-menu { position: absolute; top: 100%; left: 0; z-index: 1000; display: none; float: left; min-width: 160px; padding: 5px 0; margin: 2px 0 0; list-style: none; background-color: #ffffff; border: 1px solid #ccc; border: 1px solid rgba(0, 0, 0, 0.2); *border-right-width: 2px; *border-bottom-width: 2px; -webkit-border-radius: 6px; -moz-border-radius: 6px; border-radius: 6px; -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); -webkit-background-clip: padding-box; -moz-background-clip: padding; background-clip: padding-box; } .dropdown-menu .divider { *width: 100%; height: 1px; margin: 9px 1px; *margin: -5px 0 5px; overflow: hidden; background-color: #e5e5e5; border-bottom: 1px solid #ffffff; } .dropdown-menu > li > a { display: block; padding: 3px 20px; clear: both; font-weight: normal; line-height: 20px; color: #333333; white-space: nowrap; } .dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus, .dropdown-submenu:hover > a, .dropdown-submenu:focus > a { color: #ffffff; text-decoration: none; background-color: #0081c2; background-image: -moz-linear-gradient(top, #0088cc, #0077b3); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); background-image: -o-linear-gradient(top, #0088cc, #0077b3); background-image: linear-gradient(to bottom, #0088cc, #0077b3); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); } .open { *z-index: 1000; } .open > .dropdown-menu { display: block; } .fade { opacity: 0; -webkit-transition: opacity 0.15s linear; -moz-transition: opacity 0.15s linear; -o-transition: opacity 0.15s linear; transition: opacity 0.15s linear; } .fade.in { opacity: 1; } .tooltip { position: absolute; z-index: 1030; display: block; font-size: 11px; line-height: 1.4; opacity: 0; filter: alpha(opacity=0); visibility: visible; } .tooltip.in { opacity: 0.8; filter: alpha(opacity=80); } .tooltip.right { padding: 0 5px; margin-left: 3px; } .tooltip-inner { max-width: 200px; padding: 8px; color: #ffffff; text-align: center; text-decoration: none; background-color: #000000; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; } .tooltip-arrow { position: absolute; width: 0; height: 0; border-color: transparent; border-style: solid; } .tooltip.right .tooltip-arrow { top: 50%; left: 0; margin-top: -5px; border-right-color: #000000; border-width: 5px 5px 5px 0; } .popover { position: absolute; top: 0; left: 0; z-index: 1010; display: none; max-width: 276px; padding: 1px; text-align: left; white-space: normal; background-color: #ffffff; border: 1px solid #ccc; border: 1px solid rgba(0, 0, 0, 0.2); -webkit-border-radius: 6px; -moz-border-radius: 6px; border-radius: 6px; -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); -webkit-background-clip: padding-box; -moz-background-clip: padding; background-clip: padding-box; } .popover.right { margin-left: 10px; } .popover-title { padding: 8px 14px; margin: 0; font-size: 14px; font-weight: normal; line-height: 18px; background-color: #f7f7f7; border-bottom: 1px solid #ebebeb; -webkit-border-radius: 5px 5px 0 0; -moz-border-radius: 5px 5px 0 0; border-radius: 5px 5px 0 0; } .popover-title:empty { display: none; } .popover-content { padding: 9px 14px; } .popover .arrow, .popover .arrow:after { position: absolute; display: block; width: 0; height: 0; border-color: transparent; border-style: solid; } .popover .arrow { border-width: 11px; } .popover .arrow:after { border-width: 10px; content: ""; } .popover.right .arrow { top: 50%; left: -11px; margin-top: -11px; border-right-color: #999; border-right-color: rgba(0, 0, 0, 0.25); border-left-width: 0; } .popover.right .arrow:after { bottom: -10px; left: 1px; border-right-color: #ffffff; border-left-width: 0; } ```

This could probably use a little bit more refining, i.e. adding a parent container for all the elements #divBlogrollContainer and/or also removing generic elements like, h2, h3, aside, details, input, etc. that the blogroll doesn't use.

Then, in the plugin we could ship this file instead of grabbing from https://s3.amazonaws.com/scripting.com/code/includes/bootstrap.css. Since those are used mostly for base styles and we override some on the blogroll.css file, I don't think it's an issue if we ship it with the plugin.


This method mainly adresses the solution #⁠2 proposed by @NickGreen above.

What do you guys think?

scripting commented 5 months ago

I'm just tuning in now, but I admit I was wondering how isolated the environment was from anything declared by the toolkit.

It wasn't going to be an issue for the JS code because the blogroll () function isolates all of the code from everything else but CSS is different.

So -- is there any shielding, or should we assume any style that's declared by the toolkit will apply to everything else on the page?

Here's a question -- is it only the bootstrap stuff that's interfering? If so, why aren't the others interfering? Is it because the names that bootstrap defines (or redefines) are basic HTML styles and the others don't?

What do other WordPress plugins do?

I'll be checking back in the morning Eastern time.

scripting commented 5 months ago

Good morning! ;-)

Here's the approach I'm taking.

  1. I made a backup of the blogroll toolkit app.
  2. Changed the location it gets built into.
  3. Changed the title of the page so it's obvious which version we're looking at.
  4. Commented out the inclusion of the boostrap CSS file.
  5. Surveyed the damage. Here's a screen shot.

It doesn't look too terrible. Now I'm going to start looking into what styles need to be brought back to make it work, and when I do so, I'm going to narrow their scope so they only apply to the blogroll.

PS: Here's a snapshot of the same file before the changes above were made.

scripting commented 5 months ago

I've made really good progress with the above approach.

I think I just have one more thing to figure out -- the tooltips are not getting picked up.

There is a problem that was there before -- when you click on the popup menu, the screen adjusts vertically, which is jarring and not good.

http://scripting.com/code/testing/blogrolltoolkit/index.html

Would welcome any advice on why tooltips aren't getting picked up or interpreted properly.

Here's the smallbootstrap.css file

http://scripting.com/code/testing/blogrolltoolkit/smallbootstrap.css

BTW, when deployed the "/testing" part of the URL will not be there.

scripting commented 5 months ago

I figured out the issue with tooltips.

I pulled all the styles that have "tooltip" in their name, and did the same thing I did with the other styles, added a .blogrollContainer in front of the declaration. But! The tooltip, when activated by hovering over an element in the blogroll, creates a temporary object subordinate to "body". Hence the styles are not applying.

I see where it gets "body" from, but to change that would require a change to the code in FeedLand believe it or not. I can do that, but I'd rather not, not right at this moment.

But be aware that I am going to attach this to and that may conflict with something else named "tooltip" that is not scoped as globally as this.

As I write this I realize I should probably make the change in FeedLand. ;-)

First I have to test it. Back in a bit.

scripting commented 5 months ago

Yes, that was the issue.

image

scripting commented 5 months ago

And the popovers that appear on the link to stories now work too.

Note I did make the change to FeedLand, it now allows the caller to specify the object that the tooltip is attached to so, the styles only apply to tooltips inside the blogroll.

Now I'm going to think about how I want to package this up. Do I want to use the smallbootstrap.css file in the main blogroll, or only in the toolkit. When I ask the question that way, it's obvious -- these changes must be in the blogroll code itself. Because it's always going to be included in some other app's world. I'll test it on Scripting News and on blogroll.social, and post an item here saying it's basically done, and you should test it on Om's site, to see if the problems are fixed.

There is still one issue I'd like to deal with, but it's not related to this issue. I'll post a separate note about it.

image

scripting commented 5 months ago

@fmfernandes -- btw, now that I've done this by hand, your script was pretty good. ;-)

it asked for more than it needed, at the beginning, all those top-level CSS things, it doesn't use them.

but it does use dropdown menu, popovers, tooltips. and that's about it.

this is the kind of thing i'd never trust a script to do. you have to understand how this might affect other parts of the system, and check for breakage at least somewhat, after every step.

scripting commented 5 months ago

This was a very good report. I had all the info I needed to figure out what was going wrong and what needed fixing.

Thanks..

All the new bits should be released. It was a complicated thing, so I might have missed something, so test carefully after trying it out on test versions of doc's and om's sites. it hopefully will cure the problems you found.

NickGreen commented 5 months ago

is there any shielding, or should we assume any style that's declared by the toolkit will apply to everything else on the page?

The CSS and JS files are loaded into the page, and could potentially affect everything on the page. The best form of shielding that we have is making sure that our styles and scripts only apply to anything within a wrap selector, like you already have with divBlogrollContainer.

What do other WordPress plugins do?

Typically, plugins try to be as specific as possible with their selectors. Also, using a unique prefix for everything helps prevent collisions.

I also noticed that we are currently enqueuing things on all front end pages if the plugin is active at all. I'm going to refine the plugin code so that it only enqueues the files on pages where the shortcode is in use. I'll include this change with the current changes around which css file to use.

Next steps

Regarding changes to the WordPress plugin: which file should we enqueue? Is it the https://s3.amazonaws.com/scripting.com/code/testing/blogrolltoolkit/smallbootstrap.css file that we should move going forward (EDIT: without the /testing after we're done testing)?

scripting commented 5 months ago

Regarding changes to the WordPress plugin: which file should we enqueue?

smallbootstrap.css replaces the previous bootstrap.css.

That is the only change in the new version.

scripting commented 5 months ago

BTW, I expect we may find collisions in the other files as well. Let's keep an eye out for that.

And only enqueuing these files on pages that use the short-code is definitely the right thing to do. ;-)

scripting commented 5 months ago

Sorry -- no "/testing" -- we're not testing.

This is such a fresh release so not-deployed, that there's no reason to maintain two versions.

Drop the /testing from the url.

Sorry for not making that clear. It's hard to switch gears like this, that's why it's esp important to check for mistakes, like that one. ;-)

scripting commented 5 months ago

Use this as a guide.

https://github.com/scripting/feedlandBlogrollToolkit/commit/0d1243e8c558b90a961d430d63e676aa6f74dfaa

I'm also using smallbootstrap.css on blogroll.social, but not on scripting.com -- it already uses the full Bootstrap toolkit and the same one that the blogroll code uses (they come from the same codebase).

NickGreen commented 5 months ago

Thank you for the clarifications. Our immediate testing shows that this fixes the CSS collisions that we were getting previously. I'll work on getting the plugin itself fixed up and will report back here when that's done and tested on our end.