Contrast profiles¶
A label is readable when its text stands out from its fill. The same diagram source can render with different text colours depending on where it is shown. The tool therefore cannot score contrast without knowing the host. A profile names the host.
The problem: the host may choose the text colour¶
On GitHub, a classDef with fill: and color: renders as written.
The fill and the text colour you declared are the pair a reader sees.
On a MkDocs Material site, the theme forces the label text to its own colour: dark in the light theme, light in the dark theme.
Your color: is ignored, and the fill has to work under both of the theme's text colours.
A check that scored the declared color: on a Material site would pass diagrams that readers cannot read.
The three profiles¶
| Profile | Scores | Used when |
|---|---|---|
github |
The declared fill against the declared color:, and against the declared stroke: for borders. A linkStyle color: is scored against the edge label box. An erDiagram is scored row by row, as below. A sequenceDiagram is scored by its theme colours, as below. |
The markdown renders as written, as on GitHub |
mkdocs-material |
The fill against the host's forced text colour, in both the light and dark theme, for every diagram type except a sequenceDiagram, whose labels the host forces outright |
The markdown renders through Material, which forces label text |
auto |
mkdocs-material when any parent folder of the file holds mkdocs.yml or mkdocs.yaml, otherwise github |
The default |
Under github, a translucent fill is first composited over both page backgrounds, because a reader sees it blended with the page.
The page is GitHub's unless you set a canvas.
A border pair is a stroke: scored against its fill, and it must reach 3 to 1.
Under github, outside an erDiagram, a border pair that fails is reported as contrast-border-below-ui-minimum.
Under mkdocs-material, and in an erDiagram under github, border pairs are advisory: they are scored but never reported.
A faint fill under a saturated stroke cannot reach 3 to 1 on both theme backgrounds, and there is no warning level.
So contrast-border-below-ui-minimum never fires on a Material site.
Under auto, a mkdocs.yml at the repository root makes every file in the repository score as Material, README.md included.
Lint a MkDocs Material site gives the two fixes.
The colours each profile assumes¶
A profile scores against fixed page and text colours, taken from the host's stock theme:
| Profile | Theme | Page background | Label text |
|---|---|---|---|
github |
Light | #ffffff |
The declared color: |
github |
Dark | #0d1117 |
The declared color: |
mkdocs-material |
Light | #ffffff |
#36464e |
mkdocs-material |
Dark | #1e2129 |
hsl(225 18% 86% / 0.82) |
A Material site that overrides --md-default-fg-color or the page background is not modelled, so its diagrams are scored as the stock theme draws them.
A host with its own page sets a canvas¶
The canvas is the page colour behind everything a diagram does not box.
Under github it defaults to GitHub's two page colours from the table above.
A host that draws its own page sets the canvas to its own colours.
A documentation viewer with its own light and dark theme is one such host.
Every page-backed pair is then scored on that page:
- A translucent fill. It is composited over the canvas in each theme.
- An edge label box. Its translucent layers sit on the canvas, so the box shifts slightly with the page.
- A sequence diagram's page-backed elements. These are the rows in the sequence table whose background is the page, such as message text and lifelines.
An opaque fill, an erDiagram row and a sequence participant box draw their own surface, so the canvas does not change them.
Set the canvas with --canvas-light and --canvas-dark, or with the canvas option in the config file.
Each side is an opaque CSS colour, and a side you leave out keeps GitHub's.
A translucent canvas is refused, because nothing shows through a page.
The canvas changes the page only.
Scoring still assumes the host draws Mermaid's default theme in light and its dark theme in dark.
That assumption sets the edge label box, the erDiagram rows and every sequence theme variable.
A host that draws another theme, or the same theme in both modes, is not modelled, so its scores are approximate.
For a sequence diagram on a host that draws one theme in both modes, pin that theme, so both modes are scored with it.
On the host's canvas that pin can pass, though a pinned theme fails on GitHub's own page, as the sequence section explains.
Run the check with --canvas-light and --canvas-dark set to the host's page to confirm it.
A host that draws a different theme in each mode has no single theme to pin.
For a sequence diagram on that host, and for any other diagram, check the scores against the host by eye in both modes.
A canvas applies under github only.
Material draws its own page, and this tool models only Material's stock colours.
How a file under mkdocs-material treats a canvas depends on where the canvas comes from:
| Canvas from | Profile from | Result |
|---|---|---|
| The config | auto, by default or set |
The canvas is set aside for that file, which is scored under mkdocs-material. One stderr line names every such file, and --json lists them under canvasSetAside. |
--canvas-light or --canvas-dark |
auto |
Usage error canvas-profile, exit 2 |
| Any | --profile mkdocs-material, or the rule's profile option set to mkdocs-material |
Usage error, exit 2: flag when both are flags, rule-option when both are on one config rule, otherwise canvas-profile |
The config covers the whole tree. So a repo with a Material site keeps one config with a canvas, and its Material files set it aside. A flag is an explicit setting for the run, so the run stops rather than ignore it and report a pass the canvas never affected. To pass the flags in such a repo, run them over the GitHub-rendered paths only.
Edge labels¶
Under github, outside an erDiagram, a linkStyle line that sets color: sets the text of that edge's label.
Mermaid draws the label on its own box, so the colour is scored against that box in both themes:
| Theme | Edge label box |
|---|---|
| Light | #e8e8e8 |
| Dark | #585858 |
No single colour reaches 4.5 to 1 on both boxes, so a fixed edge label colour always fails one theme.
A linkStyle with no color: styles only the line, and has no label pair.
Under mkdocs-material the host forces edge label text, so a linkStyle color: is ignored there.
An erDiagram is scored row by row¶
Under github, an erDiagram does not draw a class's fill behind every label.
The fill paints the even attribute rows only, and the odd rows keep Mermaid's own theme surface.
So each label is scored against both rows, in both of Mermaid's themes:
| Theme | Odd row | Even row, before the fill | Label text with no color: |
|---|---|---|---|
| Light | #ffffff |
#f1f1ff |
#333333 |
| Dark | #2c2d2d |
#060606 |
#cccccc |
A declared color: is scored on every row, so in an erDiagram a color: with no fill: is scored too.
With no color:, the theme's label text is scored on the filled even rows.
A missing color: is not reported here, because a translucent fill with no color: is how an entity reads in both themes.
Under mkdocs-material, an erDiagram is scored like any other diagram.
A sequence diagram is scored by its theme colours¶
A sequenceDiagram has no style lines.
Its colours are theme variables, some set in frontmatter or an %%{init}%% directive and the rest derived by Mermaid's theme.
The tool reads the config with Mermaid's own parser and resolves every variable with Mermaid's own theme engine, in both themes.
Under github that is the default theme in light and the dark theme in dark.
A theme the config pins applies to both, because GitHub draws a pinned theme in light and in dark.
The SVG has no background, so anything without a box of its own sits on the page.
A themeVariables.background paints nothing behind it.
Under github, each element is scored against this background:
| Element | Foreground | Background | Kind |
|---|---|---|---|
| Participant label and border | actorTextColor, actorBorder |
actorBkg |
Text, border |
Human actor name and stick figure |
actorTextColor, actorBorder |
The page | Text, border |
| Lifeline | actorLineColor |
The page | Border |
| Message text and arrow | signalTextColor, signalColor |
The page | Text, border |
| Note | noteTextColor |
noteBkgColor |
Text |
Frame keyword, such as loop |
labelTextColor |
labelBoxBkgColor |
Text |
| Frame description and frame line | loopTextColor, labelBoxBorderColor |
The page | Text, border |
| Activation bar | activationBorderColor |
activationBkgColor |
Border |
autonumber number |
sequenceNumberColor |
signalColor |
Text |
Messages and frame text inside a rect |
signalTextColor, signalColor, loopTextColor |
The rect colour |
Text, border |
A row is scored when the diagram uses that kind of element.
Two rows are scored more widely.
The participant label row is scored even when every participant is a human actor.
Frame text inside a rect is scored whenever the diagram has a frame, even one outside the rect.
A translucent background is composited over the page first.
With no theme pinned, Mermaid's default and dark themes pass, except notes in dark.
The dark theme draws note text at 4.49 to 1, so a note with no colours set fails in dark.
A note draws its own box, so setting noteBkgColor and noteTextColor to one readable pair fixes it in both themes.
On GitHub's own page, a pinned theme draws both light and dark, so its page-backed text fails on one of the two pages. theme: default puts its dark message text on GitHub's dark page, and theme: dark puts its light message text on the white page.
Setting the page-backed colours yourself does not help there: no text colour reaches 4.5 to 1 on both #ffffff and #0d1117.
So with GitHub's canvas, leave the theme unpinned and let Mermaid draw default in light and dark in dark.
A host with its own canvas that draws one theme in both modes is the exception: pin that theme, as the canvas section says.
Under mkdocs-material, Material draws Mermaid's default theme in both of its schemes.
Its own CSS replaces every sequence label, box, note, frame, rect and number colour.
So no sequence text pair is the author's, and none is scored.
Three surfaces keep the diagram's colours: lifelines, arrowheads and activation bars.
They are scored as borders, so they are advisory, as every border under this profile is.
A config the tool cannot model is reported as contrast-directive-unauditable:
themeCSS. CSS can restyle anything, and under Material it replaces the host's own CSS.- A theme other than
base,default,dark,forestorneutral. The other themes colour each actor from palettes of their own. - A colour Mermaid cannot read. A bad colour that others derive from, such as
primaryColor, stops the whole theme. Any other bad colour, such assignalTextColor, is reported for each pair it is in, and the rest of the diagram is still scored.
The colours change between Mermaid releases, so the package pins the exact Mermaid version it resolves them with.
What is not scored¶
Contrast is scored for the colours a diagram declares. None of these is scored, and none of them produces a finding:
- A node with no style. It takes the renderer's theme colours, which cannot be known without drawing it.
- A node's
color:with nofill:beside it, outside anerDiagram. AclassDeforstyleline that sets onlycolor:has no declared background to score it against. - A node's
stroke:with nofill:beside it. A border on an unfilled node has no declared background to score it against, under either profile. - A fully transparent fill. A
fill:transparentor a fill with zero alpha, such as#ffffff00, draws no box, so itscolor:andstroke:are not scored. White text on that fill passes, even on a white page. - A
linkStyleline'sstroke:. An edge line is not scored against the page. - A border pair under
mkdocs-materialor in anerDiagram. It is scored but advisory, as The three profiles explains. - Theme settings outside a
sequenceDiagram. An%%{init}%%directive or frontmatter that setsthemeorthemeVariablesis read only for a sequence diagram. - A sequence
boxand the other participant shapes. The colour of aboxaround a group of participants is not scored, and nor are shapes such asdatabase. - Style statements outside a flowchart,
erDiagram,stateDiagramorclassDiagram. AclassDeforstylein another diagram type, such asblock-beta, is reported ascontrast-directive-unauditable.
Why a fill with no label colour is reported¶
Under github, a fill: with no color: beside it cannot be scored: the text colour depends on the renderer's default theme.
Skipping it would let a gate pass a diagram it never checked, so it is reported as contrast-directive-unauditable.
Under mkdocs-material the host supplies the text colour, so the same directive is scoreable.
Why some contrast findings have no fix¶
The only colour fix swaps a declared color: for black or white.
It does so only when that choice reaches 4.5 to 1 on every background the directive is scored against.
- A border has no fixer, because changing a stroke changes how a category looks.
- Under
mkdocs-materialthere is nocolor:to rewrite, so the fill itself must change, and choosing a fill is a design decision. - In an
erDiagram, a class colours alternating rows on different backgrounds, so no single label colour may pass them all. - In a
sequenceDiagram, a colour is a theme variable that other variables derive from. Changing one can move pairs it was not scored in. Text on the page also needs a colour that flips with the theme.
Choosing a profile¶
Leave it on auto unless a file renders somewhere its folder does not reveal.
Pass --profile for a one-off run, or set the profile option on the contrast rules in the config file.
Lint a MkDocs Material site shows the workflow.