Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EthicalAds: Default to fixedfooter for smaller screens #447

Open
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

ericholscher
Copy link
Member

@ericholscher ericholscher commented Nov 22, 2024

This is something I've wanted for a little while.
I don't think we're quite ready to ship it,
but I think it makes sense in a lot of cases.

I'm generally becoming more excited about this placement,
especially when the floating one is pretty intrusive.

Demo

Screenshot 2025-01-14 at 4 12 54 PM Screenshot 2025-01-14 at 4 13 09 PM Screenshot 2025-01-14 at 4 26 14 PM Screenshot 2025-01-14 at 4 26 21 PM Screenshot 2025-01-14 at 5 02 37 PM

@ericholscher ericholscher changed the title Default to fixedfooter for smaller screens Draft: EthicalAds: Default to fixedfooter for smaller screens Nov 22, 2024
This is something I've wanted for a little while.
I don't think we're quite ready to ship it,
but I think it makes sense in a lot of cases.

I'm generally becoming more excited about this placement,
especially when the floating one is pretty intrusive.
@ericholscher ericholscher marked this pull request as ready for review December 11, 2024 18:46
@ericholscher ericholscher requested review from humitos and a team as code owners December 11, 2024 18:46
@ericholscher
Copy link
Member Author

ericholscher commented Dec 11, 2024

Marking this ready for review after our previous conversation. I'd like to see us doing the fixed footer by default more, since I think it's a much more user friendly ad format. We're hopefully gonna work on a nice fixed footer/header format with a small image, which I think will be even better, but I think this is a nicer UX for default.

In general I really dislike how not integrated the ad feels on non-Sphinx themes, so I think this is a "gentler" approach to ads that isn't quite as impactful for users who want to move to RTD for non-Sphinx themes.

NOTE: This is somewhere that it would be interesting to have a feature flag, so that we could test it a bit before rolling it out more widely.

@ericholscher
Copy link
Member Author

Looks like this hides the "sidebar" toggle on docify: https://test-builds.readthedocs.io/en/docsify/#/ -- and there wasn't an element I could find to add margin-bottom to, because I think they're fixed positioning....

The struggle of placement on arbitrary content :(

@ericholscher ericholscher changed the title Draft: EthicalAds: Default to fixedfooter for smaller screens EthicalAds: Default to fixedfooter for smaller screens Dec 11, 2024
@humitos
Copy link
Member

humitos commented Dec 12, 2024

In general I really dislike how not integrated the ad feels on non-Sphinx themes, so I think this is a "gentler" approach to ads that isn't quite as impactful for users who want to move to RTD for non-Sphinx themes.

Yeah, I think we should keep adding more frameworks to our heuristic detection and create better ad placement for specific documentation tools. Sphinx is well integrated because we did the work there. The same happens with all the other we have listed at:

addons/src/ethicalads.js

Lines 73 to 142 in 8ccaa33

if (docTool.isSphinxReadTheDocsLikeTheme()) {
selector = "nav.wy-nav-side > div.wy-side-scroll";
element = document.querySelector(selector);
if (this.elementAboveTheFold(element)) {
placement.setAttribute("data-ea-type", "readthedocs-sidebar");
placement.classList.add("ethical-rtd");
placement.classList.add("ethical-dark-theme");
knownPlacementFound = true;
}
} else if (docTool.isSphinxFuroLikeTheme()) {
// NOTE: The code to handle furo theme shouldn't be required,
// since furo uses explicit placement.
// However, the Jinja context variable READTHEDOCS is not injected anymore,
// and furo does not includes the explicit placement due to this.
// This is a temporal solution while they fix this.
selector = ".sidebar-tree";
element = document.querySelector(selector);
if (this.elementAboveTheFold(element)) {
placement.classList.add("ethical-alabaster");
placement.setAttribute("data-ea-type", "readthedocs-sidebar");
placement.setAttribute("id", "furo-sidebar-ad-placement");
knownPlacementFound = true;
}
} else if (docTool.isSphinxBookThemeLikeTheme()) {
selector = ".sidebar-primary-items__start.sidebar-primary__section";
element = document.querySelector(selector);
if (this.elementAboveTheFold(element)) {
placement.classList.add("ethical-alabaster");
placement.setAttribute("data-ea-type", "readthedocs-sidebar");
knownPlacementFound = true;
}
} else if (docTool.isSphinxAlabasterLikeTheme()) {
selector = "div.sphinxsidebar > div.sphinxsidebarwrapper";
element = document.querySelector(selector);
if (this.elementAboveTheFold(element)) {
placement.classList.add("ethical-alabaster");
placement.setAttribute("data-ea-type", "readthedocs-sidebar");
knownPlacementFound = true;
}
} else if (docTool.isMaterialMkDocsTheme()) {
selector = ".md-sidebar__scrollwrap";
element = document.querySelector(selector);
if (this.elementAboveTheFold(element)) {
// TODO: use a more styled CSS class.
// See https://github.com/readthedocs/ethical-ad-client/issues/193
placement.classList.add("ethical-alabaster");
placement.setAttribute("data-ea-type", "readthedocs-sidebar");
knownPlacementFound = true;
}
} else if (docTool.isDocusaurusTheme()) {
selector = ".menu.thin-scrollbar.menu_SIkG";
element = document.querySelector(selector);
if (this.elementAboveTheFold(element)) {
// TODO: use a more styled CSS class.
// See https://github.com/readthedocs/ethical-ad-client/issues/193
placement.classList.add("ethical-alabaster");
placement.classList.add("ethical-docusaurus");
placement.setAttribute("data-ea-type", "readthedocs-sidebar");
placement.setAttribute("data-ea-style", "image");
knownPlacementFound = true;
}
}

We can do the same for Markdoc, mdbook and similars to have better integrations on those as well. It shouldn't be hard. We need to find the right CSS selector.

I'm not opposed to have a better default for frameworks we don't know; but we for those we are aware they exist and people are using them, we should integrate them better by using custom positioning.

NOTE: This is somewhere that it would be interesting to have a feature flag, so that we could test it a bit before rolling it out more widely.

As I mentioned in the meeting, I would not use feature flags for this, but use a AddonsConfig.ethicalads_* field instead.

humitos added a commit that referenced this pull request Dec 12, 2024
Show how simple is to add a better ad positioning for a documentation tool that
we know.

1. Find the CSS selector that allows to detect the framework
2. Find the CSS selector for the ad position
3. Add small CSS for styling if required

Related #447
@humitos
Copy link
Member

humitos commented Dec 12, 2024

I added an example of what I'm saying in #470. You can see it's pretty easy to create these custom positioning based on the framework. I think they will always work better (less intrusive, nicer look&feel) than a generic default.

You can try to follow those steps and add a better positioning for Markdoc, for example.

@humitos
Copy link
Member

humitos commented Dec 12, 2024

I'm generally becoming more excited about this placement,
especially when the floating one is pretty intrusive.

By they way, when performing visual changes, I would really appreciate having a screenshot of how it looks --that way is a lot easier to do a review; since it doesn't require me to clone the PR and testing it locally.

Copy link
Contributor

@agjohnson agjohnson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@humitos point on per-tool styling is probably better overall, but just noting a couple things with the approach here.

// TODO: THIS IS A HACK. IS THERE A BETTER WAY?
// Add margin to the bottom to avoid hiding bottom of content
const root_node = document.querySelector(docTool.getRootSelector());
root_node.style.marginBottom = "2em";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just noting a couple issues here, I haven't tried playing around with the styling here.

Altering style rules through Element.style ends up just adding a style="..." attribute to the element, which has a couple issues:

  • Inline style attributes can be blocked by CSP rules as unsafe.
  • Inline style attributes have the highest specificity in CSS, so the user loses control of this styling.

In both cases, it's better to use classes to apply rules to these elements.

@@ -158,6 +158,20 @@ export class EthicalAdsAddon extends AddonBase {
placement,
elementInsertBefore.lastChild,
);
} else if (window.innerWidth <= 1300 && window.innerWidth > 768) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just noticed this logic and the same above. These conditionals are only evaluated once, so don't catch the window resizing or rotating. Users have to reload before the placement style would shift in this case. These widths might be better off as CSS media queries, which are reactive.

humitos added a commit that referenced this pull request Dec 16, 2024
Show how simple is to add a better ad positioning for a documentation
tool that we know.

1. Find the CSS selector that allows to detect the framework
2. Find the CSS selector for the ad position
3. Add small CSS for styling if required


![Screenshot_2024-12-12_12-12-47](https://github.com/user-attachments/assets/7b3dac1f-701f-4e55-83b4-13bb86e96690)


Related #447
@ericholscher
Copy link
Member Author

Next steps:

  • Figure out a somewhat reliable way to bump most content "up"
  • Ship fixed footer as default instead of floating image ad
  • Continue building better integrations into various themes/tools as they come up

@ericholscher
Copy link
Member Author

I think this PR should be ready to be merged. I'm adding a class to the main content node to add a bottom margin so hopefully the ad doesn't cover the content.

The other option I was wondering about is adding a close button to the ad, just in case the content under it needs to be accessed. @davidfischer thoughts on building that into the placement itself, or should we customize it?

@humitos
Copy link
Member

humitos commented Jan 13, 2025

@ericholscher can you post screenshots of how it looks under different doc tools? That would help with the review.

@ericholscher
Copy link
Member Author

ericholscher commented Jan 15, 2025

Screenshots attached to description.

font-size: 20px;
line-height: 20px;
.ethical-fixedfooter-margin {
margin-bottom: 5em;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@agjohnson Is there a way to have this match the height of the ad instead of a random height?

Seems like you could hack it with something like:

// Get the height of the target element
const targetElement = document.querySelector('.target-class');
const matchingElement = document.querySelector('.matching-class');

// Match the height dynamically
matchingElement.style.height = `${targetElement.offsetHeight}px`;

but I don't think there's a way to do it non-dynamically, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even calculating the margin at load time, it will break when resizing the window.

I think the problem here is position: fixed (that comes from EthicalAd client CSS). Using a more selector with more specificity (😉 @agjohnson ) we can override it. Example showing how it works:

#readthedocs-ea {
  position: static;
}

With this, we don't need to add the extra class .ethical-fixedfooter-margin to all the elements as you are doing in https://github.com/readthedocs/addons/pull/447/files#diff-10e47ee23788d0e8d3d4e13cee726fd04c8fe035922a74ac1c7d432db1dfb9faR191

I understand this works because we are adding this element as the last one in the document.body.

However, it does not work well in all the documentation tools because the ad may end behind a scroll 😞

Demo

Material for MkDocs

Peek 2025-01-15 10-51

Sphinx Furo

Peek 2025-01-15 10-58

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There isn't pure CSS solution here given the fixed footer is a separate element from the elements you're adding classes to for the margin -- there isn't a way to reference between elements like this unless one element is a direct descendant. In this case you have more options and calc() might be able to get there.

I'd avoid a JS solution here especially using Element.style -- my note above applies here.

It feels like we should have multiple explicit styles by viewport size, not necessarily try to do this dynamically -- that is we'd have explicit heights for mobile, table, desktop widths.

Comment on lines +183 to +193
// Add margin to content and footers to avoid hiding content
const root_node = document.querySelector(docTool.getRootSelector());
const footers = document.querySelectorAll("footer, .footer");

if (root_node) {
root_node.classList.add("ethical-fixedfooter-margin");
}

footers.forEach((footer) => {
footer.classList.add("ethical-fixedfooter-margin");
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could end up adding a lot of extra space at the bottom depending on the number of elements the page has.

Using stickybox

Screenshot_2025-01-15_11-03-26

Using fixedfooter

Screenshot_2025-01-15_11-03-39

Copy link
Contributor

@agjohnson agjohnson Jan 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd need to be a lot more precise adding margins into user content like this. Adding margin into the main document content is probably not what we want either, as there isn't a direct link between the main document content and the bottom of the frame.

I suppose I'm not convinced we should be adding margins into nested elements yet. document.body is probably the place we should be adding padding (body { padding-bottom: 1rem; }) for a fixed footer, but not where we should insert an ad placement.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we just not try and just the bottom margins at all? The issue is that it covers up fixed position content on other themes (eg. mkdocs-material), so I wasn't sure what the right approach was.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I had to choose, I would prefer to cover footer content than breaking layout (adding weird margins). In either case, we can work on "better placement for that particular doctool" when we see it or when users complain 😄

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I was trying to build a better placement for mkdocs-material specifically, because I know that's one we want to support, so understanding the "right" way to bump the ad above the footer is still useful. I agree maybe we just add that to the theme-specific support?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have a specific positioning for Material for MkDocs that's working already: https://docs.pypi.org/

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, but not for smaller screen sizes, like we are discussing?

footer.classList.add("ethical-fixedfooter-margin");
});

// Inject the actual ad
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems than depending on the theme/doctool the may be injected in a weird place and not covering the full width:

Solidity

Screenshot_2025-01-15_11-10-41

Our docs

Screenshot_2025-01-15_11-10-20

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, my comment above was about not adding the fixed footer to document.body, but this feels like we should be adding the fixed footer placement to document.body. Do we want it full width of the frame or not?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want it full width of the frame or not?

I think it should be full width, yes.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It shouldn't be showing on the RTD theme, since that has a custom placement. I'm not really sure what the change being suggested here is -- isn't the ad getting injected into the document.body?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ad is being injected on RTD theme here. I think that happens because of the current logic: "if the specific placement it not above the fold, we show the default placement". The "default placement" now is fixedfooter, and that's why it's being shown.

Copy link
Member

@humitos humitos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the idea is good, but it's hard to do it correctly in a reliable way. I don't think it has to be perfect 1, but at least I would like to fix those cases where it seems to be broken (not using the full width of the page and adding a lot of blank space at the bottom).

With those changes, I think I will feel more comfortable deploying this and keeping an eye on those new cases where it breaks to fix them as we go.

This change will affect all the documentation pages which looks like a big change to deploy something we don't feel comfortable about. We talked about adding AddonsConfig.ethicalads_type = "text" and add it a few projects to start. That may be still an option as well.

Footnotes

  1. making it reactive could be a v2 of this implementation, for example

Copy link
Contributor

@agjohnson agjohnson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted some options here, but I'd overall agree that the fixed footer is going to be the hardest placement to get correct across all tools/themes. Text ad would indeed be an easier place to start for a site wide default placement.

This seems like it might be close but it does need some more precision. Altering users styles without precision is bound to cause some friction or confusion with users.

Comment on lines +183 to +193
// Add margin to content and footers to avoid hiding content
const root_node = document.querySelector(docTool.getRootSelector());
const footers = document.querySelectorAll("footer, .footer");

if (root_node) {
root_node.classList.add("ethical-fixedfooter-margin");
}

footers.forEach((footer) => {
footer.classList.add("ethical-fixedfooter-margin");
});
Copy link
Contributor

@agjohnson agjohnson Jan 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd need to be a lot more precise adding margins into user content like this. Adding margin into the main document content is probably not what we want either, as there isn't a direct link between the main document content and the bottom of the frame.

I suppose I'm not convinced we should be adding margins into nested elements yet. document.body is probably the place we should be adding padding (body { padding-bottom: 1rem; }) for a fixed footer, but not where we should insert an ad placement.

footer.classList.add("ethical-fixedfooter-margin");
});

// Inject the actual ad
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, my comment above was about not adding the fixed footer to document.body, but this feels like we should be adding the fixed footer placement to document.body. Do we want it full width of the frame or not?

font-size: 20px;
line-height: 20px;
.ethical-fixedfooter-margin {
margin-bottom: 5em;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There isn't pure CSS solution here given the fixed footer is a separate element from the elements you're adding classes to for the margin -- there isn't a way to reference between elements like this unless one element is a direct descendant. In this case you have more options and calc() might be able to get there.

I'd avoid a JS solution here especially using Element.style -- my note above applies here.

It feels like we should have multiple explicit styles by viewport size, not necessarily try to do this dynamically -- that is we'd have explicit heights for mobile, table, desktop widths.

// placement,
// elementInsertBefore.lastChild,
// );
} else if (window.innerWidth > 768) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a case for matchMedia: https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia

Noted earlier that this check isn't dynamic currently, it's only evaluated once. Using matchMedia as an event gives a way to do this after page load.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't easily reconfigure the ad without doing another API call, so we don't want it to be dynamic. We want to have a single ad load on page load, and stick with that, otherwise it will get weird.

Copy link
Member Author

@ericholscher ericholscher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not really sure what is being suggested here, but feel free to update this PR with an approach that y'all think will work.

// placement,
// elementInsertBefore.lastChild,
// );
} else if (window.innerWidth > 768) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't easily reconfigure the ad without doing another API call, so we don't want it to be dynamic. We want to have a single ad load on page load, and stick with that, otherwise it will get weird.

footer.classList.add("ethical-fixedfooter-margin");
});

// Inject the actual ad
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It shouldn't be showing on the RTD theme, since that has a custom placement. I'm not really sure what the change being suggested here is -- isn't the ad getting injected into the document.body?

Comment on lines +183 to +193
// Add margin to content and footers to avoid hiding content
const root_node = document.querySelector(docTool.getRootSelector());
const footers = document.querySelectorAll("footer, .footer");

if (root_node) {
root_node.classList.add("ethical-fixedfooter-margin");
}

footers.forEach((footer) => {
footer.classList.add("ethical-fixedfooter-margin");
});
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we just not try and just the bottom margins at all? The issue is that it covers up fixed position content on other themes (eg. mkdocs-material), so I wasn't sure what the right approach was.

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.

EthicalAd: implement better positioning
3 participants