<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.stiles.casa/index.php?action=history&amp;feed=atom&amp;title=Module%3ABox-header%2Fsandbox</id>
	<title>Module:Box-header/sandbox - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.stiles.casa/index.php?action=history&amp;feed=atom&amp;title=Module%3ABox-header%2Fsandbox"/>
	<link rel="alternate" type="text/html" href="https://wiki.stiles.casa/index.php?title=Module:Box-header/sandbox&amp;action=history"/>
	<updated>2026-04-04T00:19:42Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.39.2</generator>
	<entry>
		<id>https://wiki.stiles.casa/index.php?title=Module:Box-header/sandbox&amp;diff=6631&amp;oldid=prev</id>
		<title>imported&gt;Andrybak: test</title>
		<link rel="alternate" type="text/html" href="https://wiki.stiles.casa/index.php?title=Module:Box-header/sandbox&amp;diff=6631&amp;oldid=prev"/>
		<updated>2021-09-25T10:20:27Z</updated>

		<summary type="html">&lt;p&gt;test&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;local getArgs = require(&amp;#039;Module:Arguments&amp;#039;).getArgs&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
---------- Config data ----------&lt;br /&gt;
local namedColours = mw.loadData( &amp;#039;Module:Box-header/colours&amp;#039; )&lt;br /&gt;
local modes = {&lt;br /&gt;
	lightest = { sat=0.10, val=1.00 },&lt;br /&gt;
	light    = { sat=0.15, val=0.95 },&lt;br /&gt;
	normal   = { sat=0.40, val=0.85 },&lt;br /&gt;
	dark     = { sat=0.90, val=0.70 },&lt;br /&gt;
	darkest  = { sat=1.00, val=0.45 },&lt;br /&gt;
	content  = { sat=0.04, val=1.00 },&lt;br /&gt;
	grey     = { sat=0.00 }&lt;br /&gt;
}&lt;br /&gt;
local min_contrast_ratio_normal_text = 7  -- i.e 7:1&lt;br /&gt;
local min_contrast_ratio_large_text  = 4.5  -- i.e. 4.5:1&lt;br /&gt;
&lt;br /&gt;
-- Template parameter aliases&lt;br /&gt;
--   Specify each as either a single value, or a table of values&lt;br /&gt;
--   Aliases are checked left-to-right, i.e. `[&amp;#039;one&amp;#039;] = { &amp;#039;two&amp;#039;, &amp;#039;three&amp;#039; }` is equivalent to using `{{{one| {{{two| {{{three|}}} }}} }}}` in a template&lt;br /&gt;
local parameterAliases = {&lt;br /&gt;
	[&amp;#039;1&amp;#039;] = 1,&lt;br /&gt;
	[&amp;#039;2&amp;#039;] = 2,&lt;br /&gt;
	[&amp;#039;colour&amp;#039;] = &amp;#039;color&amp;#039;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
---------- Dependecies ----------&lt;br /&gt;
local colourContrastModule = require(&amp;#039;Module:Color contrast&amp;#039;)&lt;br /&gt;
local hex = require( &amp;#039;luabit.hex&amp;#039; )&lt;br /&gt;
&lt;br /&gt;
---------- Utility functions ----------&lt;br /&gt;
local function getParam(args, parameter)&lt;br /&gt;
	if args[parameter] then&lt;br /&gt;
		return args[parameter]&lt;br /&gt;
	end&lt;br /&gt;
	local aliases = parameterAliases[parameter]&lt;br /&gt;
	if not aliases then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	if type(aliases) ~= &amp;#039;table&amp;#039; then&lt;br /&gt;
		return args[aliases]&lt;br /&gt;
	end&lt;br /&gt;
	for _, alias in ipairs(aliases) do&lt;br /&gt;
		if args[alias] then&lt;br /&gt;
			return args[alias]&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function setCleanArgs(argsTable)&lt;br /&gt;
	local cleanArgs = {}&lt;br /&gt;
	for key, val in pairs(argsTable) do&lt;br /&gt;
		if type(val) == &amp;#039;string&amp;#039; then&lt;br /&gt;
			val = val:match(&amp;#039;^%s*(.-)%s*$&amp;#039;)&lt;br /&gt;
			if val ~= &amp;#039;&amp;#039; then&lt;br /&gt;
				cleanArgs[key] = val&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			cleanArgs[key] = val&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return cleanArgs&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Merge two tables into a new table. If the are any duplicate keys, the values from the second overwrite the values from the first.&lt;br /&gt;
local function mergeTables(first, second)&lt;br /&gt;
	local merged = {}&lt;br /&gt;
	for key, val in pairs(first) do&lt;br /&gt;
		merged[key] = val&lt;br /&gt;
	end&lt;br /&gt;
	for key, val in pairs(second) do&lt;br /&gt;
		merged[key] = val&lt;br /&gt;
	end&lt;br /&gt;
	return merged&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function toOpenTagString(selfClosedHtmlObject)&lt;br /&gt;
	local closedTagString = tostring(selfClosedHtmlObject)&lt;br /&gt;
	local openTagString = mw.ustring.gsub(closedTagString, &amp;#039; /&amp;gt;$&amp;#039;, &amp;#039;&amp;gt;&amp;#039;)&lt;br /&gt;
	return openTagString&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function normaliseHexTriplet(hexString)&lt;br /&gt;
	if not hexString then return nil end&lt;br /&gt;
	local hexComponent = mw.ustring.match(hexString, &amp;#039;^#(%x%x%x)$&amp;#039;) or mw.ustring.match(hexString, &amp;#039;^#(%x%x%x%x%x%x)$&amp;#039;)&lt;br /&gt;
	if hexComponent and #hexComponent == 6 then&lt;br /&gt;
		return mw.ustring.upper(hexString)&lt;br /&gt;
	end&lt;br /&gt;
	if hexComponent and #hexComponent == 3 then&lt;br /&gt;
		local r = mw.ustring.rep(mw.ustring.sub(hexComponent, 1, 1), 2)&lt;br /&gt;
		local g = mw.ustring.rep(mw.ustring.sub(hexComponent, 2, 2), 2)&lt;br /&gt;
		local b = mw.ustring.rep(mw.ustring.sub(hexComponent, 3, 3), 2)&lt;br /&gt;
		return &amp;#039;#&amp;#039; .. mw.ustring.upper(r .. g .. b)&lt;br /&gt;
	end&lt;br /&gt;
	return nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---------- Conversions ----------&lt;br /&gt;
local function decimalToPaddedHex(number)&lt;br /&gt;
	local prefixedHex = hex.to_hex(tonumber(number)) -- prefixed with &amp;#039;0x&amp;#039;&lt;br /&gt;
	local padding =  #prefixedHex == 3 and &amp;#039;0&amp;#039; or &amp;#039;&amp;#039; &lt;br /&gt;
	return mw.ustring.gsub(prefixedHex, &amp;#039;0x&amp;#039;, padding)&lt;br /&gt;
end&lt;br /&gt;
local function hexToDecimal(hexNumber)&lt;br /&gt;
	return tonumber(hexNumber, 16)&lt;br /&gt;
end&lt;br /&gt;
local function RGBtoHexTriplet(R, G, B)&lt;br /&gt;
	return &amp;#039;#&amp;#039; .. decimalToPaddedHex(R) .. decimalToPaddedHex(G) .. decimalToPaddedHex(B)&lt;br /&gt;
end&lt;br /&gt;
local function hexTripletToRGB(hexTriplet)&lt;br /&gt;
	local R_hex, G_hex, B_hex = string.match(hexTriplet, &amp;#039;(%x%x)(%x%x)(%x%x)&amp;#039;)&lt;br /&gt;
	return hexToDecimal(R_hex), hexToDecimal(G_hex), hexToDecimal(B_hex)&lt;br /&gt;
end&lt;br /&gt;
local function HSVtoRGB(H, S, V) -- per [[HSL and HSV#Converting_to_RGB]]&lt;br /&gt;
	local C = V * S&lt;br /&gt;
	local H_prime = H / 60&lt;br /&gt;
	local X = C * ( 1 - math.abs(math.fmod(H_prime, 2) - 1) )&lt;br /&gt;
	local R1, G1, B1&lt;br /&gt;
	if H_prime &amp;lt;= 1 then&lt;br /&gt;
		R1 = C&lt;br /&gt;
		G1 = X&lt;br /&gt;
		B1 = 0&lt;br /&gt;
	elseif H_prime &amp;lt;= 2 then&lt;br /&gt;
		R1 = X&lt;br /&gt;
		G1 = C&lt;br /&gt;
		B1 = 0&lt;br /&gt;
	elseif H_prime &amp;lt;= 3 then&lt;br /&gt;
		R1 = 0&lt;br /&gt;
		G1 = C&lt;br /&gt;
		B1 = X&lt;br /&gt;
	elseif H_prime &amp;lt;= 4 then&lt;br /&gt;
		R1 = 0&lt;br /&gt;
		G1 = X&lt;br /&gt;
		B1 = C&lt;br /&gt;
	elseif H_prime &amp;lt;= 5 then&lt;br /&gt;
		R1 = X&lt;br /&gt;
		G1 = 0&lt;br /&gt;
		B1 = C&lt;br /&gt;
	elseif H_prime &amp;lt;= 6 then&lt;br /&gt;
		R1 = C&lt;br /&gt;
		G1 = 0&lt;br /&gt;
		B1 = X&lt;br /&gt;
	end	&lt;br /&gt;
	local m = V - C&lt;br /&gt;
	local R = R1 + m&lt;br /&gt;
	local G = G1 + m&lt;br /&gt;
	local B = B1 + m&lt;br /&gt;
&lt;br /&gt;
	local R_255 = math.floor(R*255)&lt;br /&gt;
	local G_255 = math.floor(G*255)&lt;br /&gt;
	local B_255 = math.floor(B*255)&lt;br /&gt;
	return R_255, G_255, B_255&lt;br /&gt;
end&lt;br /&gt;
local function RGBtoHue(R_255, G_255, B_255) -- per [[HSL and HSV#Hue and chroma]]&lt;br /&gt;
	local R = R_255/255&lt;br /&gt;
	local G = G_255/255&lt;br /&gt;
	local B = B_255/255&lt;br /&gt;
&lt;br /&gt;
	local M = math.max(R, G, B)&lt;br /&gt;
	local m = math.min(R, G, B)&lt;br /&gt;
	local C = M - m&lt;br /&gt;
	local H_prime&lt;br /&gt;
	if C == 0 then&lt;br /&gt;
		return null&lt;br /&gt;
	elseif M == R then&lt;br /&gt;
		H_prime = math.fmod(((G - B)/C + 6), 6) -- adding six before taking mod ensures positive value&lt;br /&gt;
	elseif M == G then&lt;br /&gt;
		H_prime = (B - R)/C + 2&lt;br /&gt;
	elseif M == B then&lt;br /&gt;
		H_prime = (R - G)/C + 4&lt;br /&gt;
	end&lt;br /&gt;
	local H = 60 * H_prime&lt;br /&gt;
	return H&lt;br /&gt;
end&lt;br /&gt;
local function nameToHexTriplet(name)&lt;br /&gt;
	if not name then return nil end&lt;br /&gt;
	local codename = mw.ustring.gsub(mw.ustring.lower(name), &amp;#039; &amp;#039;, &amp;#039;&amp;#039;)&lt;br /&gt;
	return namedColours[codename]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---------- Choose colours ----------&lt;br /&gt;
local function calculateColours(H, S, V, minContrast)&lt;br /&gt;
	local bgColour = RGBtoHexTriplet(HSVtoRGB(H, S, V))&lt;br /&gt;
	local textColour = colourContrastModule._greatercontrast({bgColour})&lt;br /&gt;
	local contrast = colourContrastModule._ratio({ bgColour, textColour })&lt;br /&gt;
	if contrast &amp;gt;= minContrast then&lt;br /&gt;
		return bgColour, textColour&lt;br /&gt;
	elseif textColour == &amp;#039;#FFFFFF&amp;#039; then&lt;br /&gt;
		-- make the background darker and slightly increase the saturation&lt;br /&gt;
		return calculateColours(H, math.min(1, S+0.005), math.max(0, V-0.03), minContrast)&lt;br /&gt;
	else&lt;br /&gt;
		-- make the background lighter and slightly decrease the saturation&lt;br /&gt;
		return calculateColours(H, math.max(0, S-0.005), math.min(1, V+0.03), minContrast)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function makeColours(hue, modeName)&lt;br /&gt;
	local mode = modes[modeName]&lt;br /&gt;
	local isGrey = not(hue)&lt;br /&gt;
	if isGrey then hue = 0 end&lt;br /&gt;
&lt;br /&gt;
	local borderSat = isGrey and modes.grey.sat or 0.15&lt;br /&gt;
	local border = RGBtoHexTriplet(HSVtoRGB(hue, borderSat, 0.75))&lt;br /&gt;
&lt;br /&gt;
	local titleSat = isGrey and modes.grey.sat or mode.sat&lt;br /&gt;
	local titleBackground, titleForeground = calculateColours(hue, titleSat, mode.val, min_contrast_ratio_large_text)&lt;br /&gt;
&lt;br /&gt;
	local contentSat = isGrey and modes.grey.sat or modes.content.sat&lt;br /&gt;
	local contentBackground, contentForeground = calculateColours(hue, contentSat, modes.content.val, min_contrast_ratio_normal_text)&lt;br /&gt;
&lt;br /&gt;
	return border, titleForeground, titleBackground, contentForeground, contentBackground&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function findHue(colour)&lt;br /&gt;
	local colourAsNumber = tonumber(colour)&lt;br /&gt;
	if colourAsNumber and ( -1 &amp;lt; colourAsNumber ) and ( colourAsNumber &amp;lt; 360) then&lt;br /&gt;
		return colourAsNumber&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local colourAsHexTriplet = normaliseHexTriplet(colour) or nameToHexTriplet(colour)&lt;br /&gt;
	if colourAsHexTriplet then&lt;br /&gt;
		return RGBtoHue(hexTripletToRGB(colourAsHexTriplet))&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return null&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function normaliseMode(mode)&lt;br /&gt;
	if not mode or not modes[mw.ustring.lower(mode)] or mw.ustring.lower(mode) == &amp;#039;grey&amp;#039; then&lt;br /&gt;
		return &amp;#039;normal&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
	return mw.ustring.lower(mode)&lt;br /&gt;
end&lt;br /&gt;
---------- Build output ----------&lt;br /&gt;
local function boxHeaderOuter(args)&lt;br /&gt;
	local baseStyle = {&lt;br /&gt;
		clear = &amp;#039;both&amp;#039;,&lt;br /&gt;
		[&amp;#039;box-sizing&amp;#039;] = &amp;#039;border-box&amp;#039;,&lt;br /&gt;
		border = ( getParam(args, &amp;#039;border-type&amp;#039;) or &amp;#039;solid&amp;#039; ) .. &amp;#039; &amp;#039; .. ( getParam(args, &amp;#039;titleborder&amp;#039;) or getParam(args, &amp;#039;border&amp;#039;) or &amp;#039;#ababab&amp;#039; ),&lt;br /&gt;
		background = getParam(args, &amp;#039;titlebackground&amp;#039;) or &amp;#039;#bcbcbc&amp;#039;,&lt;br /&gt;
		color = getParam(args, &amp;#039;titleforeground&amp;#039;) or &amp;#039;#000&amp;#039;,&lt;br /&gt;
		padding = getParam(args, &amp;#039;padding&amp;#039;) or &amp;#039;.1em&amp;#039;,&lt;br /&gt;
		[&amp;#039;text-align&amp;#039;] = getParam(args, &amp;#039;title-align&amp;#039;) or &amp;#039;center&amp;#039;,&lt;br /&gt;
		[&amp;#039;font-family&amp;#039;] = getParam(args, &amp;#039;font-family&amp;#039;) or &amp;#039;sans-serif&amp;#039;,&lt;br /&gt;
		[&amp;#039;font-size&amp;#039;] = getParam(args, &amp;#039;titlefont-size&amp;#039;) or &amp;#039;100%&amp;#039;,&lt;br /&gt;
		[&amp;#039;margin-bottom&amp;#039;] = &amp;#039;0px&amp;#039;,&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	local tag = mw.html.create(&amp;#039;div&amp;#039;, {selfClosing = true})&lt;br /&gt;
		:addClass(&amp;#039;box-header-title-container&amp;#039;)&lt;br /&gt;
		:addClass(&amp;#039;flex-columns-noflex&amp;#039;)&lt;br /&gt;
		:css(baseStyle)&lt;br /&gt;
		:css(&amp;#039;border-width&amp;#039;, ( getParam(args, &amp;#039;border-top&amp;#039;) or getParam(args, &amp;#039;border-width&amp;#039;) or &amp;#039;1&amp;#039; ) .. &amp;#039;px &amp;#039; .. ( getParam(args, &amp;#039;border-width&amp;#039;) or &amp;#039;1&amp;#039; ) .. &amp;#039;px 0&amp;#039;)&lt;br /&gt;
		:css(&amp;#039;padding-top&amp;#039;, getParam(args, &amp;#039;padding-top&amp;#039;) or &amp;#039;.1em&amp;#039;)&lt;br /&gt;
		:css(&amp;#039;padding-left&amp;#039;, getParam(args, &amp;#039;padding-left&amp;#039;) or &amp;#039;.1em&amp;#039;)&lt;br /&gt;
		:css(&amp;#039;padding-right&amp;#039;, getParam(args, &amp;#039;padding-right&amp;#039;) or &amp;#039;.1em&amp;#039;)&lt;br /&gt;
		:css(&amp;#039;padding-bottom&amp;#039;, getParam(args, &amp;#039;padding-bottom&amp;#039;) or &amp;#039;.1em&amp;#039;)&lt;br /&gt;
		:css(&amp;#039;moz-border-radius&amp;#039;, getParam(args, &amp;#039;title-border-radius&amp;#039;) or &amp;#039;0&amp;#039;)&lt;br /&gt;
		:css(&amp;#039;webkit-border-radius&amp;#039;, getParam(args, &amp;#039;title-border-radius&amp;#039;) or &amp;#039;0&amp;#039;)&lt;br /&gt;
		:css(&amp;#039;border-radius&amp;#039;, getParam(args, &amp;#039;title-border-radius&amp;#039;) or &amp;#039;0&amp;#039;)&lt;br /&gt;
	return toOpenTagString(tag)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function boxHeaderTopLinks(args)&lt;br /&gt;
	local style = {&lt;br /&gt;
		float = &amp;#039;right&amp;#039;,&lt;br /&gt;
		[&amp;#039;margin-bottom&amp;#039;] = &amp;#039;.1em&amp;#039;,&lt;br /&gt;
		[&amp;#039;font-size&amp;#039;] = getParam(args, &amp;#039;font-size&amp;#039;) or &amp;#039;80%&amp;#039;,&lt;br /&gt;
		color = getParam(args, &amp;#039;titleforeground&amp;#039;) or &amp;#039;#000&amp;#039;&lt;br /&gt;
	}&lt;br /&gt;
	local tag = mw.html.create(&amp;#039;div&amp;#039;, {selfClosing = true})&lt;br /&gt;
		:addClass(&amp;#039;plainlinks noprint&amp;#039; )&lt;br /&gt;
		:css(style)&lt;br /&gt;
	return toOpenTagString(tag)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function boxHeaderEditLink(args)&lt;br /&gt;
	local page = getParam(args, &amp;#039;editpage&amp;#039;)&lt;br /&gt;
	if not page or page == &amp;#039;{{{2}}}&amp;#039;&lt;br /&gt;
	then&lt;br /&gt;
		return &amp;#039;&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
	local style = {&lt;br /&gt;
		color = getParam(args, &amp;#039;titleforeground&amp;#039;) or &amp;#039;#000&amp;#039;&lt;br /&gt;
	}&lt;br /&gt;
	local tag = mw.html.create(&amp;#039;span&amp;#039;)&lt;br /&gt;
		:css(style)&lt;br /&gt;
		:wikitext(&amp;#039;edit&amp;#039;)&lt;br /&gt;
	local linktext = tostring(tag)&lt;br /&gt;
	local linktarget = tostring(mw.uri.fullUrl(page, {action=&amp;#039;edit&amp;#039;, section=getParam(args, &amp;#039;section&amp;#039;)}))&lt;br /&gt;
	return &amp;#039;[&amp;#039; .. linktarget  .. &amp;#039; &amp;#039; .. linktext .. &amp;#039;]&amp;amp;nbsp;&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function boxHeaderViewLink(args)&lt;br /&gt;
	local style = {&lt;br /&gt;
		color = getParam(args, &amp;#039;titleforeground&amp;#039;) or &amp;#039;#000&amp;#039;&lt;br /&gt;
	}&lt;br /&gt;
	local tag = mw.html.create(&amp;#039;span&amp;#039;)&lt;br /&gt;
		:css(style)&lt;br /&gt;
		:wikitext(&amp;#039;view&amp;#039;)&lt;br /&gt;
	local linktext = tostring(tag)&lt;br /&gt;
	local linktarget = &amp;#039;:&amp;#039; .. getParam(args, &amp;#039;vieswage&amp;#039;)&lt;br /&gt;
	return &amp;quot;&amp;lt;b&amp;gt;·&amp;lt;/b&amp;gt;&amp;amp;nbsp;[[&amp;quot; .. linktarget  .. &amp;#039;|&amp;#039; .. linktext .. &amp;#039;]]&amp;amp;nbsp;&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function boxHeaderTitle(args)&lt;br /&gt;
	local baseStyle = {&lt;br /&gt;
		[&amp;#039;font-family&amp;#039;] = getParam(args, &amp;#039;title-font-family&amp;#039;) or &amp;#039;sans-serif&amp;#039;,&lt;br /&gt;
		[&amp;#039;font-size&amp;#039;] = getParam(args, &amp;#039;title-font-size&amp;#039;) or &amp;#039;100%&amp;#039;,&lt;br /&gt;
		[&amp;#039;font-weight&amp;#039;] = getParam(args, &amp;#039;title-font-weight&amp;#039;) or &amp;#039;bold&amp;#039;,&lt;br /&gt;
		border = &amp;#039;none&amp;#039;,&lt;br /&gt;
		margin = &amp;#039;0&amp;#039;,&lt;br /&gt;
		padding = &amp;#039;0&amp;#039;,&lt;br /&gt;
		color = getParam(args, &amp;#039;titleforeground&amp;#039;) or &amp;#039;#000&amp;#039;;&lt;br /&gt;
	}&lt;br /&gt;
	local tagName = getParam(args, &amp;#039;SPAN&amp;#039;) and &amp;#039;span&amp;#039; or &amp;#039;h2&amp;#039;&lt;br /&gt;
	local tag = mw.html.create(tagName)&lt;br /&gt;
		:css(baseStyle)&lt;br /&gt;
		:css(&amp;#039;padding-bottom&amp;#039;, &amp;#039;.1em&amp;#039;)&lt;br /&gt;
		:wikitext(getParam(args, &amp;#039;title&amp;#039;))&lt;br /&gt;
	if getParam(args, &amp;#039;extra&amp;#039;) then&lt;br /&gt;
		local rules = mw.text.split(getParam(args, &amp;#039;extra&amp;#039;), &amp;#039;;&amp;#039;, true)&lt;br /&gt;
		for _, rule in pairs(rules) do&lt;br /&gt;
			local parts = mw.text.split(rule, &amp;#039;:&amp;#039;, true)&lt;br /&gt;
			local prop = parts[1]&lt;br /&gt;
			local val = parts[2]&lt;br /&gt;
			if prop and val then&lt;br /&gt;
				tag:css(prop, val)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return tostring(tag)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function boxBody(args)&lt;br /&gt;
	local baseStyle = {&lt;br /&gt;
		[&amp;#039;box-sizing&amp;#039;] = &amp;#039;border-box&amp;#039;,&lt;br /&gt;
		border = ( getParam(args, &amp;#039;border-width&amp;#039;) or &amp;#039;1&amp;#039; ) .. &amp;#039;px solid &amp;#039; .. ( getParam(args, &amp;#039;border&amp;#039;) or &amp;#039;#ababab&amp;#039;),&lt;br /&gt;
		[&amp;#039;vertical-align&amp;#039;] = &amp;#039;top&amp;#039;;&lt;br /&gt;
		background = getParam(args, &amp;#039;background&amp;#039;) or &amp;#039;#fefeef&amp;#039;,&lt;br /&gt;
		opacity = getParam(args, &amp;#039;background-opacity&amp;#039;) or &amp;#039;1&amp;#039;,&lt;br /&gt;
		color = getParam(args, &amp;#039;foreground&amp;#039;) or &amp;#039;#000&amp;#039;,&lt;br /&gt;
		[&amp;#039;text-align&amp;#039;] = getParam(args, &amp;#039;text-align&amp;#039;) or &amp;#039;left&amp;#039;,&lt;br /&gt;
		margin = &amp;#039;0 0 10px&amp;#039;,&lt;br /&gt;
		padding = getParam(args, &amp;#039;padding&amp;#039;) or &amp;#039;1em&amp;#039;,&lt;br /&gt;
	}&lt;br /&gt;
	local tag = mw.html.create(&amp;#039;div&amp;#039;, {selfClosing = true})&lt;br /&gt;
		:css(baseStyle)&lt;br /&gt;
		:css(&amp;#039;border-top-width&amp;#039;, ( getParam(args, &amp;#039;border-top&amp;#039;) or &amp;#039;1&amp;#039; ) .. &amp;#039;px&amp;#039;)&lt;br /&gt;
		:css(&amp;#039;padding-top&amp;#039;, getParam(args, &amp;#039;padding-top&amp;#039;) or &amp;#039;.3em&amp;#039;)&lt;br /&gt;
		:css(&amp;#039;border-radius&amp;#039;, getParam(args, &amp;#039;border-radius&amp;#039;) or &amp;#039;0&amp;#039;)&lt;br /&gt;
	return toOpenTagString(tag)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function contrastCategories(args)&lt;br /&gt;
	local cats = &amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
	local titleText = nameToHexTriplet(getParam(args, &amp;#039;titleforeground&amp;#039;)) or normaliseHexTriplet(getParam(args, &amp;#039;titleforeground&amp;#039;)) or &amp;#039;#000000&amp;#039;&lt;br /&gt;
	local titleBackground = nameToHexTriplet(getParam(args, &amp;#039;titlebackground&amp;#039;)) or normaliseHexTriplet(getParam(args, &amp;#039;titlebackground&amp;#039;)) or &amp;#039;#bcbcbc&amp;#039;&lt;br /&gt;
	local titleContrast = colourContrastModule._ratio({titleBackground, titleText})&lt;br /&gt;
	local insufficientTitleContrast = type(titleContrast) == &amp;#039;number&amp;#039; and ( titleContrast &amp;lt; min_contrast_ratio_large_text )&lt;br /&gt;
&lt;br /&gt;
	local bodyText = nameToHexTriplet(getParam(args, &amp;#039;foreground&amp;#039;)) or normaliseHexTriplet(getParam(args, &amp;#039;foreground&amp;#039;)) or &amp;#039;#000000&amp;#039;&lt;br /&gt;
	local bodyBackground = nameToHexTriplet(getParam(args, &amp;#039;background&amp;#039;)) or normaliseHexTriplet(getParam(args, &amp;#039;background&amp;#039;)) or &amp;#039;#fefeef&amp;#039;&lt;br /&gt;
	local bodyContrast =  colourContrastModule._ratio({bodyBackground, bodyText})&lt;br /&gt;
	local insufficientBodyContrast = type(bodyContrast) == &amp;#039;number&amp;#039; and ( bodyContrast &amp;lt; min_contrast_ratio_normal_text )&lt;br /&gt;
&lt;br /&gt;
	if insufficientTitleContrast and insufficientBodyContrast then&lt;br /&gt;
		return &amp;#039;[[Category:Box-header with insufficient title contrast]][[Category:Box-header with insufficient body contrast]]&amp;#039;&lt;br /&gt;
	elseif insufficientTitleContrast then&lt;br /&gt;
		return &amp;#039;[[Category:Box-header with insufficient title contrast]]&amp;#039;&lt;br /&gt;
	elseif insufficientBodyContrast then&lt;br /&gt;
		return &amp;#039;[[Category:Box-header with insufficient body contrast]]&amp;#039;&lt;br /&gt;
	else&lt;br /&gt;
		return &amp;#039;&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---------- Main functions / entry points ----------&lt;br /&gt;
&lt;br /&gt;
-- Entry point for templates (manually-specified colours)&lt;br /&gt;
function p.boxHeader(frame)&lt;br /&gt;
	local args = getArgs(frame)&lt;br /&gt;
	local page = args.editpage&lt;br /&gt;
	if not args.editpage or args.editpage == &amp;#039;&amp;#039; then&lt;br /&gt;
		page = mw.title.getCurrentTitle().prefixedText&lt;br /&gt;
	end&lt;br /&gt;
	local output = p._boxHeader(args, page)&lt;br /&gt;
	if mw.ustring.find(output, &amp;#039;{&amp;#039;) then&lt;br /&gt;
		return frame:preprocess(output)&lt;br /&gt;
	end&lt;br /&gt;
	return output&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Entry point for modules (manually-specified colours)&lt;br /&gt;
function p._boxHeader(_args, page)&lt;br /&gt;
	local args = setCleanArgs(_args)&lt;br /&gt;
	if page and not args.editpage then&lt;br /&gt;
		args.editpage = page&lt;br /&gt;
	end&lt;br /&gt;
	if not args.title then&lt;br /&gt;
		args.title = &amp;#039;{{{title}}}&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
	local output = {}&lt;br /&gt;
	table.insert(output, boxHeaderOuter(args))&lt;br /&gt;
	if not getParam(args, &amp;#039;EDITLINK&amp;#039;) then&lt;br /&gt;
		table.insert(output, boxHeaderTopLinks(args))&lt;br /&gt;
		if not getParam(args, &amp;#039;noedit&amp;#039;) then&lt;br /&gt;
			table.insert(output, boxHeaderEditLink(args))&lt;br /&gt;
		end&lt;br /&gt;
		if getParam(args, &amp;#039;vieswage&amp;#039;) then&lt;br /&gt;
			table.insert(output, boxHeaderViewLink(args))&lt;br /&gt;
		end&lt;br /&gt;
		if getParam(args, &amp;#039;top&amp;#039;) then&lt;br /&gt;
			table.insert(output, getParam(args, &amp;#039;top&amp;#039;) .. &amp;#039;&amp;amp;nbsp;&amp;#039;)&lt;br /&gt;
		end&lt;br /&gt;
		table.insert(output, &amp;#039;&amp;lt;/div&amp;gt;&amp;#039;)&lt;br /&gt;
	end&lt;br /&gt;
	table.insert(output, boxHeaderTitle(args))&lt;br /&gt;
	table.insert(output, &amp;#039;&amp;lt;/div&amp;gt;&amp;#039;)&lt;br /&gt;
	table.insert(output, boxBody(args))&lt;br /&gt;
	table.insert(output, contrastCategories(args))&lt;br /&gt;
&lt;br /&gt;
	return table.concat(output)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Entry point for templates (automatically calculated colours)&lt;br /&gt;
function p.autoColour(frame)&lt;br /&gt;
	local args = getArgs(frame)&lt;br /&gt;
	local colourParam = getParam(args, &amp;#039;colour&amp;#039;)&lt;br /&gt;
	local generatedColour = nil&lt;br /&gt;
	if not colourParam or colourParam == &amp;#039;&amp;#039; then&lt;br /&gt;
		-- convert the root page name into a number and use that&lt;br /&gt;
		local root = mw.title.getCurrentTitle().rootPageTitle.prefixedText&lt;br /&gt;
		local rootStart = mw.ustring.sub(root, 1, 12)&lt;br /&gt;
		local digitsFromRootStart = mw.ustring.gsub(rootStart, &amp;quot;.&amp;quot;, function(s) return math.fmod(string.byte(s, 2) or string.byte(s, 1), 10) end)&lt;br /&gt;
		local numberFromRoot = tonumber(digitsFromRootStart, 10)&lt;br /&gt;
		generatedColour = math.fmod(numberFromRoot, 360)&lt;br /&gt;
	end&lt;br /&gt;
	local output = p._autoColour(args, generatedColour)&lt;br /&gt;
	if mw.ustring.find(output, &amp;#039;{&amp;#039;) then&lt;br /&gt;
		return frame:preprocess(output)&lt;br /&gt;
	end&lt;br /&gt;
	return output&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Entry point for modules (automatically calculated colours)&lt;br /&gt;
function p._autoColour(_args, generatedColour)&lt;br /&gt;
	local args = setCleanArgs(_args)&lt;br /&gt;
	local hue = generatedColour or findHue(getParam(args, &amp;#039;colour&amp;#039;))&lt;br /&gt;
	local mode = normaliseMode(getParam(args, &amp;#039;mode&amp;#039;))&lt;br /&gt;
	local border, titleForeground, titleBackground, contentForeground, contentBackground = makeColours(hue, mode)&lt;br /&gt;
	local boxTemplateArgs = mergeTables(args, {&lt;br /&gt;
		title = getParam(args, &amp;#039;1&amp;#039;) or &amp;#039;{{{1}}}&amp;#039;,&lt;br /&gt;
		editpage = getParam(args, &amp;#039;2&amp;#039;) or &amp;#039;&amp;#039;,&lt;br /&gt;
		noedit = getParam(args, &amp;#039;2&amp;#039;) and &amp;#039;&amp;#039; or &amp;#039;yes&amp;#039;,&lt;br /&gt;
		border = border,&lt;br /&gt;
		titleforeground = titleForeground,&lt;br /&gt;
		titlebackground = titleBackground,&lt;br /&gt;
		foreground = contentForeground,&lt;br /&gt;
		background = contentBackground&lt;br /&gt;
	})&lt;br /&gt;
	return p._boxHeader(boxTemplateArgs)&lt;br /&gt;
end&lt;br /&gt;
	&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>imported&gt;Andrybak</name></author>
	</entry>
</feed>