mews.page/pelican-mews
2026-10-11 15:53:03 +03:00
..
.github feat: pelican plugin 2026-10-11 08:09:18 +03:00
pelican/plugins/mews feat: pelican plugin 2026-10-11 08:09:18 +03:00
tests feat: pelican plugin 2026-10-11 08:09:18 +03:00
.editorconfig feat: pelican plugin 2026-10-11 08:09:18 +03:00
.gitignore feat: pelican plugin 2026-10-11 08:09:18 +03:00
.pre-commit-config.yaml feat: pelican plugin 2026-10-11 08:09:18 +03:00
CONTRIBUTING.md feat: pelican plugin 2026-10-11 08:09:18 +03:00
pyproject.toml docs: license the code under Apache 2.0 and the spec under CC BY-SA 4.0 2026-10-11 15:53:03 +03:00
README.md feat: pelican plugin 2026-10-11 08:09:18 +03:00
tasks.py feat: pelican plugin 2026-10-11 08:09:18 +03:00
uv.lock feat: pelican plugin 2026-10-11 08:09:18 +03:00

pelican-mews

Make Pelican output pages in Mews Profile format: a small-web subset of XHTML Mobile Profile 1.2 that any browser or WAP 2.0 phone can read.

The plugin reads the allowed elements and attributes from the bundled Mews DTD, so the subset is data, not code. Every file Pelican writes that ends in .xhtml is parsed as XML, brought inside the subset, and re-serialized as well-formed UTF-8 XML with the XHTML namespace on <html>. The plugin also copies the Mews stylesheet into the output tree and gives themes a MEWS_CSS global plus the mews/head.html partial.

The bundled pelican/plugins/mews/dtd/mews.dtd is a minimal placeholder. Replace it with the real Mews Profile DTD when it is available; the plugin picks up the change without any code edit.

Install

pip install pelican-mews

Pelican loads the plugin from the pelican.plugins.mews module, so the pelican and pelican.plugins namespace packages installed by other plugins coexist with this one.

Settings

Pages and articles must be written as .xhtml, and Markdown must render as XHTML, so the plugin has valid XML to work on:

PLUGINS = ["pelican.plugins.mews"]

MARKDOWN = {
    "output_format": "xhtml",
}

PAGE_URL = "{slug}.xhtml"
PAGE_SAVE_AS = "{slug}.xhtml"
ARTICLE_URL = "{slug}.xhtml"
ARTICLE_SAVE_AS = "{slug}.xhtml"

If your PLUGINS list is already set, add "pelican.plugins.mews" to it.

Three optional settings:

Setting Default Meaning
MEWS_STRICT False Fail the build on content outside the subset instead of stripping it. Removals are always logged either way.
MEWS_CSS_DIR "css" Directory below the output root where mews-0.1.css is copied. Themes see the resulting path as the MEWS_CSS global.
MEWS_OUTPUT_PATH unset Put the finished pages in this folder instead of OUTPUT_PATH.

Rendering beside an existing blog

To keep a normal blog output untouched and build the Mews tree in parallel, point MEWS_OUTPUT_PATH at a folder of its own. Pelican still writes the .xhtml pages where *_SAVE_AS says, but the plugin moves the finished page into MEWS_OUTPUT_PATH, keeping the subpath it had under OUTPUT_PATH, and copies the stylesheet there too. The main output ends up without any Mews files:

# Articles keep their normal HTML output.
ARTICLE_URL = "{slug}.html"
ARTICLE_SAVE_AS = "{slug}.html"

# Only pages become Mews pages, in a tree of their own.
PAGE_URL = "{slug}.xhtml"
PAGE_SAVE_AS = "{slug}.xhtml"
MEWS_OUTPUT_PATH = "mews-output"

The folder works like OUTPUT_PATH: a relative path is resolved from the working directory, so mews-output sits beside output.

Themes

The plugin ships partials only, never a full base template. Include the head partial from your page template:

<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN"
  "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="{{ DEFAULT_LANG }}" lang="{{ DEFAULT_LANG }}">
<head>
  <title>{{ page.title }}</title>
  {% include "mews/head.html" %}
</head>

The partial renders the conformance marker, the viewport, and the stylesheet link with {{ MEWS_CSS }} (for example css/mews-0.1.css). Your theme keeps responsibility for the doctype, the namespace, and the rest of the page.

Serving .xhtml

Serve Mews pages as application/xhtml+xml so browsers parse them as XML. A page the plugin has written is well-formed, so XML mode is safe.

nginx:

location ~ \.xhtml$ {
    types { }
    default_type application/xhtml+xml;
}

Apache, in .htaccess:

AddType application/xhtml+xml .xhtml

Python's built-in server already maps .xhtml to application/xhtml+xml:

python -m http.server

Testing

uv sync --group test
uv run pytest