Repository: Textualize/rich

Issue:
[BUG] export_html leaks style into the page
**Describe the bug**

Following discussion https://github.com/Textualize/rich/discussions/2640
Injecting HTML generated with Rich's `Console.export_html()` method seems to leak some style into the page.

![htmlstyleleak](https://user-images.githubusercontent.com/3999221/201484491-b8d6711a-92d7-4f08-baeb-473cdeaa9902.png)

The page is built with MkDocs + Material for MkDocs. The dark theme is activated.
The light theme is less impacted, I just see extra "bars" (like an empty code line) in-between block elements:

![lighttheme](https://user-images.githubusercontent.com/3999221/201484624-0b437e8e-bef5-405f-8329-ecb96d6da35b.png)

Code used to generate the HTML:

```python
import os

from rich.console import Console

report = """$ griffe check griffe -ssrc -b0.24.0 -a0.23.0
[bold]src/griffe/loader.py[/]:156: GriffeLoader.resolve_aliases([blue]only_exported[/]): [yellow]Parameter kind was changed[/]: positional or keyword -> keyword-only
etc.
"""

with open(os.devnull, "w") as devnull:
    console = Console(record=True, width=150, file=devnull)
    console.print(report, markup=True, highlight=False)
print(console.export_html(inline_styles=True))
```

Both `inline_styles=True` and `inline_styles=False` give the same result.

**Platform**
<details>
<summary>Click to expand</summary>

Linux

```console
$ python -m rich.diagnose
╭───────────────────────── <class 'rich.console.Console'> ─────────────────────────╮
│ A high level console interface.                                                  │
│                                                                                  │
│ ╭──────────────────────────────────────────────────────────────────────────────╮ │
│ │ <console width=239 ColorSystem.TRUECOLOR>                                    │ │
│ ╰──────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                  │
│     color_system = 'truecolor'                                                   │
│         encoding = 'utf-8'                                                       │
│             file = <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'> │
│           height = 58                                                            │
│    is_alt_screen = False                                                         │
│ is_dumb_terminal = False                                                         │
│   is_interactive = True                                                          │
│       is_jupyter = False                                                         │
│      is_terminal = True                                                          │
│   legacy_windows = False                                                         │
│         no_color = False                                                         │
│          options = ConsoleOptions(                                               │
│                        size=ConsoleDimensions(width=239, height=58),             │
│                        legacy_windows=False,                                     │
│                        min_width=1,                                              │
│                        max_width=239,                                            │
│                        is_terminal=True,                                         │
│                        encoding='utf-8',                                         │
│                        max_height=58,                                            │
│                        justify=None,                                             │
│                        overflow=None,                                            │
│                        no_wrap=False,                                            │
│                        highlight=None,                                           │
│                        markup=None,                                              │
│                        height=None                                               │
│                    )                                                             │
│            quiet = False                                                         │
│           record = False                                                         │
│         safe_box = True                                                          │
│             size = ConsoleDimensions(width=239, height=58)                       │
│        soft_wrap = False                                                         │
│           stderr = False                                                         │
│            style = None                                                          │
│         tab_size = 8                                                             │
│            width = 239                                                           │
╰──────────────────────────────────────────────────────────────────────────────────╯
╭─── <class 'rich._windows.WindowsConsoleFeatures'> ────╮
│ Windows features available.                           │
│                                                       │
│ ╭───────────────────────────────────────────────────╮ │
│ │ WindowsConsoleFeatures(vt=False, truecolor=False) │ │
│ ╰───────────────────────────────────────────────────╯ │
│                                                       │
│ truecolor = False                                     │
│        vt = False                                     │
╰───────────────────────────────────────────────────────╯
╭────── Environment Variables ───────╮
│ {                                  │
│     'TERM': 'xterm-256color',      │
│     'COLORTERM': 'truecolor',      │
│     'CLICOLOR': None,              │
│     'NO_COLOR': None,              │
│     'TERM_PROGRAM': None,          │
│     'COLUMNS': None,               │
│     'LINES': None,                 │
│     'JUPYTER_COLUMNS': None,       │
│     'JUPYTER_LINES': None,         │
│     'JPY_PARENT_PID': None,        │
│     'VSCODE_VERBOSE_LOGGING': None │
│ }                                  │
╰────────────────────────────────────╯
platform="Linux"
```

```console
% pdm list --freeze | grep rich
rich==12.6.0
```

</details>



Discussion:
The "extra bars" I mentioned:

```html
<p>

<meta charset="UTF-8">
<style>
.r1 {font-weight: bold}
.r2 {color: #000080; text-decoration-color: #000080}
.r3 {color: #808000; text-decoration-color: #808000}
body {
    color: #000000;
    background-color: #ffffff;
}
</style>



    <code>
        </code></p>
```

Kinda weird? Not sure what is injecting this :thinking: 
The `export_html` method generates a page, i.e. an entire document with head and body. Which is not something you can drop in to a template.

You can set `code_format` to a string template, in order to create an HTML fragment. Have an look at `_export_format.py` for the original template, which you can modify


Aaaaaaah OK, that makes sense. I'll do what you suggest, thanks!
Did I solve your problem? 

Why not buy the devs a [coffee](https://ko-fi.com/textualize) to say thanks?
@willmcgugan the extra paragraphs are here because in the base format `pre` is wrapped in `code` while it should be the opposite.

Code in `_export_format.py`:

```
    <code>
        <pre style="font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">{code}</pre>
    </code>
```

Should be:

```
    <pre style="font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">
        <code>{code}</code>
    </pre>
```

At least it fixed the issue on my side.
Ah. Care to PR for that?

Resolving patch:
diff --git a/rich/_export_format.py b/rich/_export_format.py
--- a/rich/_export_format.py
+++ b/rich/_export_format.py
@@ -12,9 +12,9 @@
 </head>
 <html>
 <body>
-    <code>
-        <pre style="font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">{code}</pre>
-    </code>
+    <pre style="font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">
+        <code>{code}</code>
+    </pre>
 </body>
 </html>
 """


Test patch:
diff --git a/tests/test_console.py b/tests/test_console.py
--- a/tests/test_console.py
+++ b/tests/test_console.py
@@ -528,7 +528,7 @@ def test_export_html():
     console = Console(record=True, width=100)
     console.print("[b]foo <script> 'test' [link=https://example.org]Click[/link]")
     html = console.export_html()
-    expected = '<!DOCTYPE html>\n<head>\n<meta charset="UTF-8">\n<style>\n.r1 {font-weight: bold}\n.r2 {color: #ff00ff; text-decoration-color: #ff00ff; font-weight: bold}\n.r3 {color: #008000; text-decoration-color: #008000; font-weight: bold}\nbody {\n    color: #000000;\n    background-color: #ffffff;\n}\n</style>\n</head>\n<html>\n<body>\n    <code>\n        <pre style="font-family:Menlo,\'DejaVu Sans Mono\',consolas,\'Courier New\',monospace"><span class="r1">foo &lt;</span><span class="r2">script</span><span class="r1">&gt; </span><span class="r3">&#x27;test&#x27;</span><span class="r1"> </span><a class="r1" href="https://example.org">Click</a>\n</pre>\n    </code>\n</body>\n</html>\n'
+    expected = '<!DOCTYPE html>\n<head>\n<meta charset="UTF-8">\n<style>\n.r1 {font-weight: bold}\n.r2 {color: #ff00ff; text-decoration-color: #ff00ff; font-weight: bold}\n.r3 {color: #008000; text-decoration-color: #008000; font-weight: bold}\nbody {\n    color: #000000;\n    background-color: #ffffff;\n}\n</style>\n</head>\n<html>\n<body>\n    <pre style="font-family:Menlo,\'DejaVu Sans Mono\',consolas,\'Courier New\',monospace">\n        <code><span class="r1">foo &lt;</span><span class="r2">script</span><span class="r1">&gt; </span><span class="r3">&#x27;test&#x27;</span><span class="r1"> </span><a class="r1" href="https://example.org">Click</a>\n</code>\n    </pre>\n</body>\n</html>\n'
     assert html == expected
 
 
@@ -536,7 +536,7 @@ def test_export_html_inline():
     console = Console(record=True, width=100)
     console.print("[b]foo [link=https://example.org]Click[/link]")
     html = console.export_html(inline_styles=True)
-    expected = '<!DOCTYPE html>\n<head>\n<meta charset="UTF-8">\n<style>\n\nbody {\n    color: #000000;\n    background-color: #ffffff;\n}\n</style>\n</head>\n<html>\n<body>\n    <code>\n        <pre style="font-family:Menlo,\'DejaVu Sans Mono\',consolas,\'Courier New\',monospace"><span style="font-weight: bold">foo </span><span style="font-weight: bold"><a href="https://example.org">Click</a></span>\n</pre>\n    </code>\n</body>\n</html>\n'
+    expected = '<!DOCTYPE html>\n<head>\n<meta charset="UTF-8">\n<style>\n\nbody {\n    color: #000000;\n    background-color: #ffffff;\n}\n</style>\n</head>\n<html>\n<body>\n    <pre style="font-family:Menlo,\'DejaVu Sans Mono\',consolas,\'Courier New\',monospace">\n        <code><span style="font-weight: bold">foo </span><span style="font-weight: bold"><a href="https://example.org">Click</a></span>\n</code>\n    </pre>\n</body>\n</html>\n'
     assert html == expected
 
 
@@ -589,7 +589,7 @@ def test_save_text():
 
 
 def test_save_html():
-    expected = "<!DOCTYPE html>\n<head>\n<meta charset=\"UTF-8\">\n<style>\n\nbody {\n    color: #000000;\n    background-color: #ffffff;\n}\n</style>\n</head>\n<html>\n<body>\n    <code>\n        <pre style=\"font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">foo\n</pre>\n    </code>\n</body>\n</html>\n"
+    expected = "<!DOCTYPE html>\n<head>\n<meta charset=\"UTF-8\">\n<style>\n\nbody {\n    color: #000000;\n    background-color: #ffffff;\n}\n</style>\n</head>\n<html>\n<body>\n    <pre style=\"font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">\n        <code>foo\n</code>\n    </pre>\n</body>\n</html>\n"
     console = Console(record=True, width=100)
     console.print("foo")
     with tempfile.TemporaryDirectory() as path: