Closed nbanyan closed 1 month ago
This addition will break documentation for people, even MkDocs Material, without them adding some new CSS.
Additionally, I don't think you've really made clear what you are "fixing" either. Can you demonstrate with some images or something showing what the change is?
Using a new mkdocs project with only the following YAML and no additional CSS:
site_name: My Docs
theme:
name: material
plugins:
- search
# Uncomment this block and use `PDF_GENERATE=1 mkdocs build`
# to export this MkDocs to HTML and PDF
- with-pdf:
enabled_if_env: PDF_GENERATE
relaxedjs_path: "/opt/homebrew/bin/relaxed"
render_js: True
headless_chrome_path: "/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome" #"/opt/homebrew/bin/chromium"
markdown_extensions:
- pymdownx.superfences
- pymdownx.tabbed:
alternate_style: true
Renderer | Web Browser | Print View | PDF Export |
---|---|---|---|
Current | |||
With Changes | |||
Changes with CSS |
The added CSS is:
<style>
/* @media print { */
.md-typeset .tabbed-set .tabbed-label-print {
display: none;
}
/* } */
.md-typeset .tabbed-set .tabbed-label-print > label,
.md-typeset .tabbed-set .tabbed-labels > label {
border-bottom: .1rem solid #0000;
border-radius: .1rem .1rem 0 0;
color: var(--md-default-fg-color--light);
cursor: pointer;
flex-shrink: 0;
font-size: .64rem;
font-weight: 700;
padding: .78125em 1.25em .625em;
scroll-margin-inline-start: 1rem;
transition: background-color .25s,color .25s;
white-space: nowrap;
width: auto;
}
.md-typeset .tabbed-set .tabbed-label-print > label {
padding-left: 0;
}
@media print {
.md-typeset .tabbed-set .tabbed-labels {
display: none;
}
.md-typeset .tabbed-set .tabbed-label-print {
display: block;
}
}
</style>
Noticing the extra space the hidden div adds. It shouldn't do that and I can't find a way to make it not add that space, so I added a alternate_style_for_pdf
config option to enable these additional labels when needed.
Unfortunately, you are claiming that export to PDF messes up tabs when printing, but I am not finding that true. I exported a site to PDF with absolutely no issues here.
Maybe you are claiming a specific PDF exporter is having trouble? But I'm not sure we should break documents because one exporter has trouble.
What PDF export tools/plugins did you use for that? The reason I'm using with-pdf with the RelaxedJS option is that it's the only method I've found so far that can export pages with Mermaid diagrams and Draw.io SVGs to PDF.
Browsers seem to print to PDF just fine.
Right, that's the Print View. But that doesn't work well when you need to print multiple pages with a cover page and table of contents. That's what Weasyprint and RelaxedJS can do, but some things end up rendered differently even when using headless Chrome as a generator.
Is that an "pymdownx" problem or a "Weasyprint" problem? This injects unnecessary HTML elements that will break other documentation. Yes, they can add additional CSS to hide them, but what you are suggesting is a hack for this specific PDF exporter.
I will add, if there is a specific need for this, a workaround mkdocs plugin could be crafted to post process the content and add such elements. I would recommend going this route as a work around. If/when this is resolved in the exporter, the required mkdocs plugin could be retired.
For anyone else who happens to needs this:
Add this as an MkDocs hook:
from mkdocs.config.defaults import MkDocsConfig, Page
from mkdocs.structure.files import Files
from typing import Union
from bs4 import BeautifulSoup
def on_page_content(html: str,
page: Page,
config: MkDocsConfig,
files: Files) -> Union[str, None]:
"""
The page_content event is called after the Markdown text
is rendered to HTML (but before being passed to a template)
and can be used to alter the HTML body of the page.
:param html: HTML rendered from Markdown source as string
:type html: str
:param page: mkdocs.structure.pages.Page instance
:type page: Page
:param config: global configuration object
:type config: MkDocsConfig
:param files: global files collection
:type files: Files
:return: Modified HTML
:rtype: str | None
"""
# Parse the HTML content
parsed_html = BeautifulSoup(html, 'html.parser')
# Process all 'tabbed-set' elements
for tabbed_set in parsed_html.find_all('div', class_='tabbed-set'):
# Ensure we're only processing the direct children of this tabbed-set
direct_children = list(tabbed_set.children)
# Find direct children that are 'tabbed-labels' and 'tabbed-content'
tabbed_labels = None
tabbed_content = None
for child in direct_children:
if child.name == 'div':
if 'tabbed-labels' in child.get('class', []):
tabbed_labels = child
elif 'tabbed-content' in child.get('class', []):
tabbed_content = child
if tabbed_labels and tabbed_content:
# Get all labels and content blocks
labels = tabbed_labels.find_all('label', recursive=False)
blocks = tabbed_content.find_all('div',
class_='tabbed-block',
recursive=False)
# Ensure the number of labels matches the number of blocks
if len(labels) == len(blocks):
for label, block in zip(labels, blocks):
# Create a new wrapper div
label_print_div = parsed_html.new_tag(
'div',
attrs={'class': 'tabbed-label-print'})
# Create a new label element with the same contents
new_label = parsed_html.new_tag('label')
new_label.string = label.get_text()
# Append the new label to the wrapper div
label_print_div.append(new_label)
# Insert the new wrapper div into the block
block.insert(0, label_print_div)
# Return the modified HTML
return str(parsed_html)
Add this to your CSS:
.md-typeset .tabbed-set .tabbed-label-print {
display: none;
}
.md-typeset .tabbed-set .tabbed-label-print > label,
.md-typeset .tabbed-set .tabbed-labels > label {
border-bottom: .1rem solid #0000;
border-radius: .1rem .1rem 0 0;
color: var(--md-default-fg-color--light);
cursor: pointer;
flex-shrink: 0;
font-size: .64rem;
font-weight: 700;
padding: .78125em 1.25em .625em;
scroll-margin-inline-start: 1rem;
transition: background-color .25s,color .25s;
white-space: nowrap;
width: auto;
}
.md-typeset .tabbed-set .tabbed-label-print > label {
padding-left: 0;
}
@media print {
.md-typeset .tabbed-set .tabbed-labels {
display: none;
}
.md-typeset .tabbed-set .tabbed-label-print {
display: block;
}
}
Adding hidden alt-style tabbed-set labels to the top of tabbed-blocks
.tabbed-labels
to make replicating that CSS easier.hidden
attribute on the new label's div wrapper because so it can easily be overridden by CSS and doesn't alter the existing appearance.aria-hidden: false
doesn't counteractdisplay: none
for AT, so I didn't add that. Such would likely need to be another, separate solution.Scenario: I'm working with Material for MkDocs using the alternate tabbed style. I also use domWalter's continuation of the mkdocs-with-pdf plugin using RelaxedJS. When exporting to PDF, tabbed-sets would print all the tab labels at the top followed by the tabbed-contents, just like it is in HTML despite the web browser's print view showing differently. So this is my fix for this - essentially creating something closer to the standard tabbed style for use only with
@media print
.