Module:Sandbox/TheUnit 72/UCB
| Uses Lua: |
The Universal Clickable Button (UCB) module renders a styled, clickable button inline in wikitext. It supports internal wiki links, external URLs, named colors, raw hex color codes, icons, tooltips, custom CSS, and hover effects. It is implemented via Module:Sandbox/TheUnit_72/UCB with styles loaded from Module:Sandbox/TheUnit_72/UCB/styles.css.
Usage
[edit]To use this template, transclude it with the following syntax:
{{Universal Clickable Button|<link or URL>|<label>|color=<color>}}
Or with fully named parameters:
{{Universal Clickable Button|link=Target page|text=Label|color=blue}}
Parameters
[edit]| Parameter | Aliases | Description | Default |
|---|---|---|---|
| 1 (first positional) | link, url |
The link target. Accepts an internal wiki page title or an external URL beginning with https://, http://, or //. Required. If empty, the template renders nothing. |
— |
| 2 (second positional) | text |
The visible label displayed on the button. | (empty) |
| 3 (third positional) | color, colour |
The background color of the button. Accepts a named color (see color table below), a 6-digit hex code (#3366cc), or a 3-digit hex code (#36c). The hash symbol is optional. |
#f8f9fa (light grey)
|
color / colour |
— | Named alternative to the third positional parameter for the background color. | #f8f9fa
|
icon |
— | Filename of a wiki file (with or without the File: prefix) to display as a 16px icon to the left of the label text. |
(none) |
tooltip |
— | Text for the HTML title attribute, shown on mouse hover as a browser tooltip. |
(none) |
class |
— | One or more additional CSS classes to apply to the button span. The class mw-ui-button is silently ignored. |
(none) |
style |
— | Inline CSS applied directly to the button span, overriding default styles. | (none) |
hover |
— | Set to yes to render internal links using wikitext link syntax ([[...]]) instead of full URL syntax, enabling the wiki's standard hover and visited-link styling. Has no effect on external URLs. |
(off) |
Colors
[edit]The following named colors are recognized. Color names are case-insensitive and extra whitespace is ignored. The alias dark is treated as equivalent to deep (e.g., dark blue resolves to deep blue).
| Name | Hex value | Name | Hex value |
|---|---|---|---|
red |
#d33 | light red |
#ff7f7f |
deep red |
#8b0000 | orange |
#ff8c00 |
light orange |
#ffd1a3 | yellow |
#ffeb3b |
gold / deep yellow |
#d4af37 | light yellow |
#fff59d |
green |
#4caf50 | deep green |
#1b5e20 |
turquoise |
#40e0d0 | blue |
#3366cc |
deep blue |
#0d47a1 | light blue |
#90caf9 |
purple |
#7953a9 | deep purple |
#4a148c |
light purple |
#ce93d8 | pink |
#e91e63 |
magenta |
#c2185b | white |
#ffffff |
black |
#000000 | grey / gray |
#a2a9b1 |
silver |
#c8ccd1 | light grey |
#f8f9fa |
deep grey |
#54595d | beige |
#f8eaba |
deep beige |
#c0c090 | progressive |
#36c |
destructive |
#d32f2f | constructive |
#008000 |
neutral |
#f8f9fa | — | — |
Unrecognized color names are ignored and the default background is used. Raw hex values not in the table are accepted directly. As well, using the standard terms "Progressive," "Destructive," Constructive," or "Neutral" will redirect the color to the appropriate hue.
Text color
[edit]Text color (black or white) is automatically selected for legibility based on the relative luminance of the background color using the formula 0.299R + 0.587G + 0.114B. Values above 186 produce black text; values at or below 186 produce white text.
Border color
[edit]The border color matches the background color, with two exceptions: light grey (#f8f9fa) uses #a2a9b1, and beige (#f8eaba) uses #c0c090.
Behavior notes
[edit]- Self-links
- If the link target resolves to the current page, the button is rendered without a hyperlink (as a plain styled span). This mirrors standard MediaWiki self-link behavior.
- External URL validation
- If
urlis specified but the value does not begin withhttps://,http://, or//, an inline error message is shown instead of the button. - Empty link
- If the first positional parameter (or
url/link) is absent or blank, the template outputs nothing. - Hover mode
- When
hover=yesis set and the target is an internal page, the link is rendered using standard wiki bracket syntax, enabling the default MediaWiki hover and visited-link CSS. This option is ignored for external URLs. - Icon rendering
- Icons are rendered at 16px with no link on the image itself (
link=), followed by a non-breaking space before the label text.
Examples
[edit]Basic internal link
[edit]{{Universal Clickable Button|Main Page|Go to Main Page}}
Colored button
[edit]{{Universal Clickable Button|Wikipedia:About|About Wikipedia|color=blue}}
External URL
[edit]{{Universal Clickable Button|url=https://www.mediawiki.org|text=MediaWiki|color=green}}
Hex color
[edit]{{Universal Clickable Button|Wikipedia:Sandbox|Try the sandbox|color=#e65100}}
With icon
[edit]{{Universal Clickable Button|Wikipedia:Featured articles|Featured articles|color=gold|icon=Star_(feature).svg}}
With tooltip
[edit]{{Universal Clickable Button|Help:Contents|Help|color=progressive|tooltip=Go to the help centre}}
With hover mode
[edit]{{Universal Clickable Button|Wikipedia:Community portal|Community|color=purple|hover=yes}}
Destructive action style
[edit]{{Universal Clickable Button|Wikipedia:Articles for deletion|AfD|color=destructive}}
TemplateData
[edit]Renders a styled, clickable button linking to a wiki page or external URL.
| Parameter | Description | Type | Status | |
|---|---|---|---|---|
| Link target | 1 link url | The internal wiki page title or external URL to link to. Required. | String | required |
| Label | 2 text | The visible text displayed on the button. | String | optional |
| Color | 3 color colour | Background color. Accepts a named color, a 6-digit hex code, or a 3-digit hex code. Hash symbol is optional. | String | optional |
| Icon | icon | Filename of a wiki file to show as a 16px icon before the label. The File: prefix is optional. | File | optional |
| Tooltip | tooltip | Text for the HTML title attribute shown on hover. | String | optional |
| CSS class | class | Additional CSS class(es) to apply to the button element. | String | optional |
| Inline style | style | Inline CSS to apply directly to the button, overriding defaults. | String | optional |
| Hover mode | hover | Set to "yes" to use wikitext link syntax for internal links, enabling standard MediaWiki hover styling.
| String | optional |
local p = {}
local colorMap = {
red = "#d33",
["deep red"] = "#8b0000",
["light red"] = "#ff7f7f",
orange = "#ff8c00",
["light orange"] = "#ffd1a3",
yellow = "#ffeb3b",
gold = "#d4af37",
["deep yellow"] = "#d4af37",
["light yellow"] = "#fff59d",
green = "#4caf50",
["deep green"] = "#1b5e20",
turquoise = "#40e0d0",
turqoise = "#40e0d0",
blue = "#3366cc",
["deep blue"] = "#0d47a1",
["light blue"] = "#90caf9",
purple = "#7953a9",
["deep purple"] = "#4a148c",
["light purple"] = "#ce93d8",
pink = "#e91e63",
magenta = "#c2185b",
white = "#ffffff",
black = "#000000",
grey = "#a2a9b1",
gray = "#a2a9b1",
silver = "#c8ccd1",
["light grey"] = "#f8f9fa",
["deep grey"] = "#54595d",
beige = "#f8eaba",
["deep beige"] = "#c0c090",
["progressive"] = "#36c",
["destructive"] = "#bf3c2c",
["constructive"] = "#008000",
["neutral"] = "#f8f9fa"
}
local DEFAULT_BG = "#f8f9fa"
local DEFAULT_FG = "#000"
local DEFAULT_BORDER = "#a2a9b1"
local function normalizeKey(str)
if not str then return nil end
str = tostring(str):lower()
:gsub("^%s+", "")
:gsub("%s+$", "")
:gsub("%s+", " ")
str = str:gsub("^dark%s+", "deep ")
return str
end
local function resolveColor(input)
if not input then return nil end
local key = normalizeKey(input)
if colorMap[key] then return colorMap[key] end
if key:match("^#%x%x%x$") or key:match("^#%x%x%x%x%x%x$") then return key end
if key:match("^%x%x%x$") or key:match("^%x%x%x%x%x%x$") then return "#" .. key end
return nil
end
local function expandHex(hex)
hex = hex:gsub("#", "")
if #hex == 3 then
return hex:sub(1,1) .. hex:sub(1,1)
.. hex:sub(2,2) .. hex:sub(2,2)
.. hex:sub(3,3) .. hex:sub(3,3)
elseif #hex == 6 then
return hex
end
return "808080"
end
local function getTextColor(bg)
local hex = expandHex(bg)
local r = tonumber(hex:sub(1,2), 16)
local g = tonumber(hex:sub(3,4), 16)
local b = tonumber(hex:sub(5,6), 16)
local lum = (0.299 * r + 0.587 * g + 0.114 * b)
return (lum > 186) and "#000" or "#fff"
end
local function pickBaseColor(args)
local raw = args.color or args.colour or args[3]
local resolved = resolveColor(raw)
if resolved then
local border
if resolved == "#f8f9fa" then
border = "#a2a9b1"
elseif resolved == "#f8eaba" then
border = "#c0c090"
else
border = resolved
end
return resolved, getTextColor(resolved), border
end
return DEFAULT_BG, DEFAULT_FG, DEFAULT_BORDER
end
local function isValidUrl(url)
if not url or url == "" then return false end
return url:match("^https?://") ~= nil
or url:match("^//") ~= nil
end
local function adjustColor(hex, factor)
local h = expandHex(hex)
local r = tonumber(h:sub(1,2), 16)
local g = tonumber(h:sub(3,4), 16)
local b = tonumber(h:sub(5,6), 16)
r = math.min(255, math.max(0, math.floor(r * factor)))
g = math.min(255, math.max(0, math.floor(g * factor)))
b = math.min(255, math.max(0, math.floor(b * factor)))
return string.format("#%02x%02x%02x", r, g, b)
end
local function getHoverColor(bg)
local hex = expandHex(bg)
local r = tonumber(hex:sub(1,2), 16)
local g = tonumber(hex:sub(3,4), 16)
local b = tonumber(hex:sub(5,6), 16)
local lum = (0.299 * r + 0.587 * g + 0.114 * b)
if bg == "#000000" or bg == "#000" then
return adjustColor(bg, 1.15)
elseif lum >= 220 and lum < 255 then
-- Near-white: lighten slightly
return adjustColor(bg, 1.08)
else
return adjustColor(bg, 0.82)
end
end
local function buildDisplay(args, bg, fg, border)
local span = mw.html.create("span")
span
:addClass("ucb-button")
:css({
["background-color"] = bg,
["color"] = fg,
["border-color"] = border,
["vertical-align"] = "middle"
})
local userClass = args.class and tostring(args.class):lower():gsub("^%s+", ""):gsub("%s+$", "") or nil
if userClass and userClass ~= "" and userClass ~= "mw-ui-button" then
span:addClass(userClass)
end
if args.tooltip and tostring(args.tooltip) ~= "" then
span:attr("title", tostring(args.tooltip))
end
if args.style then
span:cssText(args.style)
end
local text = args.text or args[2] or args[1] or ""
if args.icon and tostring(args.icon) ~= "" then
local filename = tostring(args.icon):gsub("^[Ff]ile:", "")
span:wikitext(string.format('[[File:%s|16px|link=]] ', filename))
end
span:wikitext(text)
return tostring(span)
end
function p.main(frame)
local args = frame:getParent().args
local styleLink = frame:extensionTag("templatestyles", "", {
src = "Module:Sandbox/TheUnit_72/UCB/styles.css"
})
local link = args.url or args[1] or args.link
if not link or tostring(link) == "" then
return ""
end
local isUrl = args.url ~= nil and tostring(args.url) ~= ""
if isUrl and not isValidUrl(tostring(args.url)) then
return '<span class="error">Universal Clickable Button: invalid or unsafe URL.</span>'
end
local bg, fg, border = pickBaseColor(args)
local useHover = not isUrl
and args.hover and tostring(args.hover):lower():gsub("^%s+", ""):gsub("%s+$", "") == "yes"
if not isUrl then
local currentTitle = mw.title.getCurrentTitle()
local targetTitle = mw.title.new(tostring(link))
if targetTitle and currentTitle
and targetTitle.prefixedText == currentTitle.prefixedText then
return styleLink .. buildDisplay(args, bg, fg, border)
end
end
local display = buildDisplay(args, bg, fg, border)
local linkHtml
if useHover then
linkHtml = string.format('[[%s|%s]]', tostring(link), display)
return styleLink .. string.format('<span class="plainlinks">%s</span>', linkHtml)
end
local href
if isUrl then
href = tostring(args.url)
else
local titleObj = mw.title.new(tostring(link))
href = titleObj and titleObj:fullUrl() or tostring(link)
end
linkHtml = string.format('[%s %s]', href, display)
return styleLink .. string.format('<span class="plainlinks">%s</span>', linkHtml)
end
return p