Converter sinh ra HTML hoàn chỉnh với:
- Tailwind CSS 4 classes + semantic tokens (
base-*,primary,secondary-*) - MathJax 3 auto-injected khi có công thức toán
- Dark mode support via
dark:variants - Responsive design cho tất cả elements
Lưu ý: semantic tokens cần stylesheet Tailwind/DaisyUI của project để render đúng màu.
npm install
npm run tailwind:buildLệnh trên build từ styles.css ra src/static/css/styles.css.
HTML được sinh từ converter đã bao gồm MathJax loader:
<!-- Tự động chèn vào cuối nếu có toán học -->
<script>
window.MathJax = window.MathJax || {
tex: {inlineMath: [['\\(', '\\)'], ['$', '$']]},
svg: {fontCache: 'global'}
};
</script>
<script async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js"></script>Khi nào được inject:
- Nếu markdown chứa
$...$hay\(...\)→ MathJax được inject - Nếu không có toán học → MathJax không được thêm (tiết kiệm bandwidth)
from md2html_tailwind4 import Converter
# Trong view/handler
converter = Converter(font_size='base')
html = converter.convert_md_to_html(markdown_content)
# Truyền vào template
return render(request, 'template.html', {'content': html | safe})Template (Django):
{% extends "base.html" %}
{% block content %}
<div class="bg-white dark:bg-neutral-900 p-4">
{{ content | safe }}
</div>
{% endblock %}| Format | Ví dụ | Kết quả |
|---|---|---|
| Inline $ | $\rightarrow$ |
→ |
| Inline () | \(\rightarrow\) |
→ |
| Display $$ | $$\sum x$$ |
∑ x (trên dòng riêng) |
| Display [] | \[E=mc^2\] |
E=mc² (trên dòng riêng) |
Converter tự động gom các math fragment thành 1 expression:
# Input
Price: $4.99 \rightarrow $14.99
# Converter xử lý thành
Price: $4.99 \rightarrow 14.99$
# HTML output
Price: <span class="arithmatex">\(4.99 \rightarrow 14.99\)</span>Hỗ trợ patterns:
$num1 \cmd $num2→$num1 \cmd num2$✓$num1$ \cmd $num2$→$num1 \cmd num2$✓$num1 $num2→ No merge (must have LaTeX command)
✅ Tốt:
- Tiền tệ: $4.99 \rightarrow $14.99
- Công thức: $E=mc^2$
- Khoa học: $\alpha + \beta = \gamma$❌ Tránh:
- Tiền tệ viết liên tục: $4.99$14.99 (không parse)
- Mix ké: $4.99 text $text (confusing)
- Không escape khi cần: $abc$def (sẽ parse $abc$ riêng)Headings:
h1: text-2xl sm:text-3xl lg:text-4xl font-bold
h2: text-xl sm:text-2xl lg:text-3xl font-bold
h3: text-lg sm:text-xl lg:text-2xl font-semibold
Paragraphs:
text-base leading-7 text-justify text-base-content
Code blocks:
rounded-xl border border-base-300 bg-base-300 p-4 text-base-content dark:bg-base-200
Admonitions:
note/info: border-blue-400 bg-blue-50
warning: border-amber-400 bg-amber-50
danger/error: border-red-400 bg-red-50
converter = Converter(font_size='sm') # Nhỏ
converter = Converter(font_size='base') # Mặc định
converter = Converter(font_size='lg') # LớnHTML được sinh tự động hỗ trợ dark mode. Để kích hoạt:
<!-- HTML đã có dark: classes -->
<!-- Kích hoạt bằng JS: -->
<script>
document.documentElement.classList.add('dark');
// Hoặc toggle
document.documentElement.classList.toggle('dark');
</script>
<!-- Hoặc CSS -->
@media (prefers-color-scheme: dark) {
html { @apply dark; }
}# settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'OPTIONS': {
'builtins': ['django.template.defaulttags'],
},
},
]
# views.py
from md2html_tailwind4 import Converter
from django.utils.safestring import mark_safe
def article_view(request, slug):
article = Article.objects.get(slug=slug)
converter = Converter()
html_content = converter.convert_md_to_html(article.markdown_text)
return render(request, 'article.html', {
'content': mark_safe(html_content)
})
# template.html
<div class="prose prose-sm md:prose-base max-w-4xl">
{{ content }}
</div>from fastapi import FastAPI, Response
from md2html_tailwind4 import Converter
app = FastAPI()
@app.get("/article/{article_id}")
def get_article(article_id: int):
# Fetch article from DB
converter = Converter()
html = converter.convert_md_to_html(article.content)
return Response(content=html, media_type="text/html")# build.py
from pathlib import Path
from md2html_tailwind4 import Converter
converter = Converter(font_size='base')
md_dir = Path('content')
html_dir = Path('output')
for md_file in md_dir.glob('*.md'):
with open(md_file) as f:
content = f.read()
html = converter.convert_md_to_html(content)
output_file = html_dir / md_file.stem / 'index.html'
output_file.parent.mkdir(exist_ok=True, parents=True)
output_file.write_text(html)- MathJax được inject từ CDN (618 kB gzipped)
- Auto-inject chỉ khi cần: Không có toán → không load MathJax
- Async loading:
<script async>không block page render
from functools import lru_cache
@lru_cache(maxsize=1000)
def cached_convert(markdown_text):
converter = Converter()
return converter.convert_md_to_html(markdown_text)- HTML đã bao gồm class names Tailwind + semantic tokens DaisyUI
- MathJax từ CDN → đảm bảo CORS allowed trên production
- Cần build stylesheet từ
styles.cssđể semantic tokens render đúng
# Input
{% if condition %}...{% endif %}
# Converter tự động escape
{% templatetag openblock %} if condition {% templatetag closeblock %}...Lý do: Tránh Django parse lại khi render
# Input
[Link](https://example.com)
# Output
<a href="..." target="_blank" rel="noopener noreferrer">Link</a>Tự động thêm:
target="_blank"(mở tab mới)rel="noopener noreferrer"(security)
Audio tables (3 cột, cột 3 = media URL):
| Tên | URL | Audio |
|-----|-----|-------|
| Song 1 | /path/song.mp3 | [play] |
→ Chuyển thành interactive audio player
Bảng thường:
| Col1 | Col2 |
|------|------|
| Data | Data |
→ Responsive table với scroll trên mobile
- v1.4.2+: Thêm pymdownx.arithmatex để parse
$...$expressions - v1.4.3+: Auto-inject MathJax 3 khi phát hiện toán học
- v1.5.0+: Smart merge của math fragments (e.g.,
$4.99 \rightarrow $14.99)
Nguyên nhân 1: CSP (Content Security Policy)
<!-- Header cần allow CDN -->
<meta http-equiv="Content-Security-Policy"
content="script-src 'unsafe-inline' https://cdn.jsdelivr.net">Nguyên nhân 2: Format không đúng
# ❌ Lỗi
$4.99 \rightarrow $14.99
→ Phải có LaTeX command giữa hai $
# ✅ Đúng
$4.99 \rightarrow 14.99$Nguyên nhân 3: JS không chạy
# Đảm bảo output được mark as safe
from django.utils.safestring import mark_safe
html = mark_safe(converter.convert_md_to_html(content))- Đảm bảo CSS output
src/static/css/styles.cssđược load trên page - Đảm bảo đã chạy
npm run tailwind:build - Nếu đang dùng dark mode, set
data-theme="dark"trên root node
- Kiểm tra MathJax loading time (DevTools → Network)
- Disable MathJax nếu không cần toán: tự remove
<script>tags - Dùng
font_size='sm'cho mobile
Hỏi thêm? Tham khảo README.md hoặc file này!