docx/font-color-theme--shade-80--accent3

Set a run's foreground color to a theme colour slot, optionally shifted by a themeTint or themeShade.

Library verdicts: python-docx: — docxjs: pass

Metadata

Feature iddocx/font-color-theme--shade-80--accent3
Formatdocx
Categorytext-formatting
Familydocx/font-color-theme
Axis valuesshift=shade-80, theme=accent3
Spececma-376-5-part-1 § 17.3.2.6 w:color

XPath assertions

ID / partPredicateXPathpython-docxdocxjs
theme-color-is-accent3
word/document.xml
equal = accent3
The run's w:color element must carry w:themeColor bound to the expected theme slot.
//w:r/w:rPr/w:color/@w:themeColor

Render assertions

IDPredicateSelectorpython-docxdocxjs
font-color-theme-accent3-shade-80-text-presentcss_selector/ exist
The rendered DOM must contain the fixture's run (theme-colour resolution itself is out of scope — renderers vary widely).
.docx-wrapper span, .docx-wrapper ppass

Generator source

scripts/gen_font_color_theme.py

#!/usr/bin/env python3
"""Generate a ``fixtures/docx/font-color-theme--*.docx`` fixture.

Parameterised generator for the ``docx/font-color-theme`` feature
family (12 theme slots x 7 tint/shade shifts = 84 cases).

Each invocation writes one fixture. The ``features/docx/font-color-theme.json``
manifest's ``generator.arg_template`` drives the arguments at expansion time::

    --theme <slot> --tint "<hex|empty>" --shade "<hex|empty>" --out <path>

``<slot>`` is an ``ST_ThemeColor`` enumeration value (e.g. ``accent1``,
``background1``, ``followedHyperlink``). ``--tint`` and ``--shade`` each
take a 2-digit uppercase hex byte (e.g. ``66``, ``99``, ``CC``) OR an
empty string to indicate "no shift". A given case sets at most one of
tint/shade — setting neither produces the base theme colour.

python-docx's ``font.color.rgb`` descriptor only writes ``w:val``, so
this generator reaches down to the oxml layer to add the
``w:themeColor`` / ``w:themeTint`` / ``w:themeShade`` attributes
directly, matching how Word emits the element (clause 17.3.2.6).
"""

from __future__ import annotations

import argparse
from pathlib import Path

from docx import Document
from docx.oxml import OxmlElement
from docx.oxml.ns import qn

_REPO_ROOT = Path(__file__).resolve().parent.parent


def _write(theme: str, tint: str, shade: str, out: Path) -> None:
    doc = Document()
    paragraph = doc.add_paragraph()
    run = paragraph.add_run("Theme colored text")
    rPr = run._r.get_or_add_rPr()
    color = OxmlElement("w:color")
    # ``w:val`` is required by the schema even when the effective colour
    # is resolved from the theme — Word writes ``auto`` as a placeholder.
    color.set(qn("w:val"), "auto")
    color.set(qn("w:themeColor"), theme)
    if tint:
        color.set(qn("w:themeTint"), tint)
    if shade:
        color.set(qn("w:themeShade"), shade)
    rPr.append(color)
    out.parent.mkdir(parents=True, exist_ok=True)
    doc.save(out, reproducible=True)


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--theme",
        default="accent1",
        help="ST_ThemeColor enum value (e.g. accent1, background1, followedHyperlink).",
    )
    parser.add_argument(
        "--tint",
        default="",
        help='2-digit uppercase hex byte (e.g. "66") or "" to skip.',
    )
    parser.add_argument(
        "--shade",
        default="",
        help='2-digit uppercase hex byte (e.g. "CC") or "" to skip.',
    )
    parser.add_argument(
        "--out",
        default=str(
            _REPO_ROOT / "fixtures" / "docx" / "font-color-theme.docx"
        ),
    )
    args = parser.parse_args()

    if args.tint and args.shade:
        raise SystemExit(
            "font-color-theme: --tint and --shade are mutually exclusive "
            "(Word picks one or neither per w:color element)."
        )

    out_path = Path(args.out)
    if not out_path.is_absolute():
        out_path = _REPO_ROOT / out_path
    _write(args.theme, args.tint, args.shade, out_path)
    print(out_path)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

Manifest (expanded)

{
  "$schema": "../manifest.schema.json",
  "id": "docx/font-color-theme--shade-80--accent3",
  "kind": "literal",
  "title": "Font color (theme)",
  "format": "docx",
  "category": "text-formatting",
  "summary": "Set a run's foreground color to a theme colour slot, optionally shifted by a themeTint or themeShade.",
  "spec": {
    "source": "ecma-376-5-part-1",
    "clause": "17.3.2.6",
    "element": "w:color",
    "rnc_reference": "spec/ecma-376-5/part-1/rnc/WordprocessingML.rnc",
    "xsd_reference": "spec/ecma-376-5/part-1/xsd/wml.xsd",
    "notes": "<w:color w:val=\"auto\" w:themeColor=\"<slot>\" [w:themeTint=\"<hex>\" | w:themeShade=\"<hex>\"]/> binds the run's foreground color to one of the 12 theme-color slots (ST_ThemeColor) from the document's theme1.xml. The optional w:themeTint / w:themeShade attributes shift the resolved colour lighter (tint) or darker (shade) by a 1-byte hex factor (Word's scale: 66=40%, 99=60%, CC=80%). Themes and tint/shade resolution are a rendering concern — authoring assertions only verify the XML attributes round-trip; rendering assertions only verify the run text is visible. This manifest is a parameterised family: 12 theme slots x 7 tint/shade shifts = 84 fixtures, each a distinct .docx named docx/font-color-theme--<theme>--<shift>.docx."
  },
  "fixtures": {
    "machine": "docx/font-color-theme--shade-80--accent3"
  },
  "generator": {
    "python": "scripts/gen_font_color_theme.py",
    "arg_template": "--theme {theme.val} --tint \"{shift.themeTint}\" --shade \"{shift.themeShade}\" --out fixtures/docx/font-color-theme--{shift.id}--{theme.id}.docx"
  },
  "_expansion": {
    "parent_id": "docx/font-color-theme",
    "bindings": {
      "shift": "shade-80",
      "theme": "accent3"
    }
  },
  "assertions": [
    {
      "id": "theme-color-is-accent3",
      "part": "word/document.xml",
      "namespaces": {
        "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
      },
      "xpath": "//w:r/w:rPr/w:color/@w:themeColor",
      "must": "equal",
      "value": "accent3",
      "description": "The run's w:color element must carry w:themeColor bound to the expected theme slot."
    }
  ],
  "render_assertions": [
    {
      "id": "font-color-theme-accent3-shade-80-text-present",
      "kind": "css_selector",
      "selector": ".docx-wrapper span, .docx-wrapper p",
      "must": "exist",
      "description": "The rendered DOM must contain the fixture's run (theme-colour resolution itself is out of scope — renderers vary widely)."
    }
  ]
}

Fixture

Download font-color-theme--shade-80--accent3.docx (18.6 KB)

Reference preview

Reference (machine, page 1 PNG)

docx/font-color-theme--shade-80--accent3 page 1 reference

Reference PDF


Download PDF Open in PDF.js

Spec notes

<w:color w:val="auto" w:themeColor="<slot>" [w:themeTint="<hex>" | w:themeShade="<hex>"]/> binds the run's foreground color to one of the 12 theme-color slots (ST_ThemeColor) from the document's theme1.xml. The optional w:themeTint / w:themeShade attributes shift the resolved colour lighter (tint) or darker (shade) by a 1-byte hex factor (Word's scale: 66=40%, 99=60%, CC=80%). Themes and tint/shade resolution are a rendering concern — authoring assertions only verify the XML attributes round-trip; rendering assertions only verify the run text is visible. This manifest is a parameterised family: 12 theme slots x 7 tint/shade shifts = 84 fixtures, each a distinct .docx named docx/font-color-theme--<theme>--<shift>.docx.