Module:palette
- The following documentation is located at Module:palette/documentation. [edit] Categories were auto-generated by Module:documentation. [edit]
- Useful links: subpage list • links • transclusions • testcases • sandbox
Generates a table of Palette variables along with contrast information. See MediaWiki:Gadget-Palette/table.
local export = {}
-- Given a CSS hex value, return a table of the RGB components.
-- The RGB is normalized so that white is (1.0, 1.0, 1.0).
local function get_rgb_from_hex(hex_colour)
hex_colour = hex_colour:gsub("#", "")
if #hex_colour == 3 then
return {
tonumber(hex_colour:sub(1, 1), 16) * 17 / 255,
tonumber(hex_colour:sub(2, 2), 16) * 17 / 255,
tonumber(hex_colour:sub(3, 3), 16) * 17 / 255
}
elseif #hex_colour == 6 then
return {
tonumber(hex_colour:sub(1, 2), 16) / 255,
tonumber(hex_colour:sub(3, 4), 16) / 255,
tonumber(hex_colour:sub(5, 6), 16) / 255
}
end
end
-- Calculate the WCAG2.1 relative luminance value given a RGB tuple.
-- https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html#dfn-relative-luminance
local function get_WCAG21_luminance(rgb)
local luminance_parts = {}
for _, rgb_part in ipairs(rgb) do
if rgb_part > 0.04045 then
table.insert(luminance_parts, ((rgb_part + 0.055) / 1.055) ^ 2.4)
else
table.insert(luminance_parts, rgb_part / 12.92)
end
end
local luminance = 0.2126 * luminance_parts[1] + 0.7152 * luminance_parts[2] + 0.0722 * luminance_parts[3]
return luminance
end
-- Calculate the WCAG2.1 contrast ratio given two RGB tuples.
-- https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html#dfn-contrast-ratio
local function get_WCAG21_contrast(rgb1, rgb2)
local luminance1 = get_WCAG21_luminance(rgb1)
local luminance2 = get_WCAG21_luminance(rgb2)
-- The lighter colour is used in the numerator.
local contrast_ratio
if luminance1 >= luminance2 then
contrast_ratio = (luminance1 + 0.05) / (luminance2 + 0.05)
else
contrast_ratio = (luminance2 + 0.05) / (luminance1 + 0.05)
end
return contrast_ratio
end
-- Calculate the APCA contrast ratio given a text RGB table and a background RGB table.
-- https://github.com/Myndex/SAPC-APCA/blob/master/documentation/APCA-W3-LaTeX.md
-- The variables are named to match the LaTeX document.
local function get_APCA_contrast(rgb_txt, rgb_bg)
local Ys_txt = rgb_txt[1] ^ 2.4 * 0.2126729 + rgb_txt[2] ^ 2.4 * 0.7151522 + rgb_txt[3] ^ 2.4 * 0.0721750
local Ys_bg = rgb_bg[1] ^ 2.4 * 0.2126729 + rgb_bg[2] ^ 2.4 * 0.7151522 + rgb_bg[3] ^ 2.4 * 0.0721750
local function f_sc(Y_c)
if Y_c <= 0 then
return 0
elseif Y_c <= 0.022 then
return Y_c + (0.022 - Y_c) ^ 1.414
else
return Y_c
end
end
local Y_txt = f_sc(Ys_txt)
local Y_bg = f_sc(Ys_bg)
local S_apc
if Y_bg > Y_txt then
S_apc = (Y_bg ^ 0.56 - Y_txt ^ 0.57) * 1.14
else
S_apc = (Y_bg ^ 0.65 - Y_txt ^ 0.62) * 1.14
end
local L_c
if math.abs(S_apc) < 0.1 then
L_c = 0
elseif S_apc > 0 then
L_c = (S_apc - 0.027) * 100
else
L_c = (S_apc + 0.027) * 100
end
return L_c
end
-- Get the WCAG2.1 rating as HTML, given a contrast value.
local function get_WCAG21_rating(contrast_value)
if contrast_value >= 7 then
return "<span style=\"color:var(--wikt-palette-deepblue)\">(AAA)</span>"
elseif contrast_value >= 4.5 then
return "<span style=\"color:var(--wikt-palette-forestgreen)\">(AA)</span>"
else
return "<span style=\"color:var(--wikt-palette-red)\">(FAIL)</span>"
end
end
-- Get the APCA rating as HTML, given a contrast value.
-- Using defaults text values: font size = 14px, font weight = 400
local function get_APCA_rating(contrast_value)
local abs_contrast_value = math.abs(contrast_value)
if abs_contrast_value >= 100 then
return "<span style=\"color:var(--wikt-palette-deepblue)\">(R4)</span>"
elseif abs_contrast_value >= 95 then
return "<span style=\"color:var(--wikt-palette-forestgreen)\">(R3)</span>"
elseif abs_contrast_value >= 90 then
return "<span style=\"color:var(--wikt-palette-forestgreen)\">(R2)</span>"
elseif abs_contrast_value >= 85 then
return "<span style=\"color:var(--wikt-palette-forestgreen)\">(R1)</span>"
else
return "<span style=\"color:var(--wikt-palette-red)\">(R0)</span>"
end
end
function export.show(frame)
local pageContent = mw.title.new("MediaWiki:Gadget-Palette.css"):getContent()
local lightMode = true;
local lightModeData = {}
local darkModeData = {}
local seenVariables = {} -- In order to maintain the order.
local output = {
[=[{| class="palette-table wikitable"
|+Table of palette variables in light and dark mode
|-
!scope="col"| Variable name
!scope="col"| Light mode
!scope="col"| Dark mode
!scope="col"| Use as a background colour?
!scope="col"| Use as a text colour?
|-"
]=]}
for line in pageContent:gmatch("[^\r\n]*") do
-- Hack to check whether we've reached the dark mode styles.
if line == "@media screen {" then
lightMode = false;
end
local definedVariable = line:match("--wikt%-palette%-[a-z]+")
if definedVariable then
if lightMode then
table.insert(seenVariables, definedVariable)
lightModeData[definedVariable] = line:match("#[a-zA-Z0-9]+")
else
darkModeData[definedVariable] = line:match("#[a-zA-Z0-9]+")
end
end
end
for _, var in ipairs(seenVariables) do
table.insert(output, "|<code>" .. var .. "</code>")
table.insert(output, "||class=\"wikt-auto-contrast-exempt\" style=\"background:" .. lightModeData[var] .. "\"|<span class=\"palette-highlight\">" .. lightModeData[var] .. "</span>")
table.insert(output, "||class=\"wikt-auto-contrast-exempt\" style=\"background:" .. darkModeData[var] .. "\"|<span class=\"palette-highlight\">" .. darkModeData[var] .. "</span>")
-- Contrast on body text in light mode.
local WCAG_21_contrast_text_lightmode = get_WCAG21_contrast(get_rgb_from_hex("#202122"), get_rgb_from_hex(lightModeData[var]))
local APCA_contrast_text_lightmode = get_APCA_contrast(get_rgb_from_hex("#202122"), get_rgb_from_hex(lightModeData[var]))
table.insert(output, "||'''Light mode:''' WCAG " .. string.format("%.2f", WCAG_21_contrast_text_lightmode) .. " " .. get_WCAG21_rating(WCAG_21_contrast_text_lightmode))
table.insert(output, ", APCA " .. string.format("%.2f", APCA_contrast_text_lightmode) .. " " .. get_APCA_rating(APCA_contrast_text_lightmode))
-- Contrast on body text in dark mode.
local WCAG_21_contrast_text_darkmode = get_WCAG21_contrast(get_rgb_from_hex("#eaecf0"), get_rgb_from_hex(darkModeData[var]))
local APCA_contrast_text_darkmode = get_APCA_contrast(get_rgb_from_hex("#eaecf0"), get_rgb_from_hex(darkModeData[var]))
table.insert(output, "<br> '''Dark mode:''' WCAG " .. string.format("%.2f", WCAG_21_contrast_text_darkmode) .. " " .. get_WCAG21_rating(WCAG_21_contrast_text_darkmode))
table.insert(output, ", APCA " .. string.format("%.2f", APCA_contrast_text_darkmode) .. " " .. get_APCA_rating(APCA_contrast_text_darkmode))
-- Contrast on default background in light mode.
local WCAG_21_contrast_bg_lightmode = get_WCAG21_contrast(get_rgb_from_hex(lightModeData[var]), get_rgb_from_hex("#ffffff"))
local APCA_contrast_bg_lightmode = get_APCA_contrast(get_rgb_from_hex(lightModeData[var]), get_rgb_from_hex("#ffffff"))
table.insert(output, "||'''Light mode:''' WCAG " .. string.format("%.2f", WCAG_21_contrast_bg_lightmode) .. " " .. get_WCAG21_rating(WCAG_21_contrast_bg_lightmode))
table.insert(output, ", APCA " .. string.format("%.2f", APCA_contrast_bg_lightmode) .. " " .. get_APCA_rating(APCA_contrast_bg_lightmode))
-- Contrast on default background in dark mode.
local WCAG_21_contrast_bg_darkmode = get_WCAG21_contrast(get_rgb_from_hex(darkModeData[var]), get_rgb_from_hex("#101418"))
local APCA_contrast_bg_darkmode = get_APCA_contrast(get_rgb_from_hex(darkModeData[var]), get_rgb_from_hex("#101418"))
table.insert(output, "<br> '''Dark mode:''' WCAG " .. string.format("%.2f", WCAG_21_contrast_bg_darkmode) .. " " .. get_WCAG21_rating(WCAG_21_contrast_bg_darkmode))
table.insert(output, ", APCA " .. string.format("%.2f", APCA_contrast_bg_darkmode) .. " " .. get_APCA_rating(APCA_contrast_bg_darkmode))
table.insert(output, "\n|-\n")
end
table.insert(output, "|-\n")
table.insert(output, "|colspan=\"5\" style=\"padding:1em;background:var(--wikt-palette-lavender,#f8f8ff)\"|According to [[mw:Recommendations for night mode compatibility on Wikimedia wikis]], editors should ensure that colours meet the WCAC 2.1 AA contrast standards (APCA is significantly stricter but corresponds more closely to human visual perception — see [[phab:T308772]]). Contrast ratios are calculated assuming standard text formatting (14px, unbolded) and standard colours on the rest of the page. To check contrast values between any two colours, see https://contrast.tools/.\n")
table.insert(output, "\n|}")
table.insert(output, frame:extensionTag("templatestyles", "", {src="Module:palette/styles.css"}))
return table.concat(output)
end
return export