MediaWiki:Common.css: Difference between revisions
From LinuxizeNet
(Timeless dark: header and tab link colours (Timeless hardcodes #000)) |
(Restrict icon invert to icon-only elements; stop double-treating search button) |
||
| Line 275: | Line 275: | ||
body.skin-timeless #simpleSearch, | body.skin-timeless #simpleSearch, | ||
body.skin-timeless #searchInput | body.skin-timeless #searchInput { | ||
background: var( --lz-surface-2 ); | background: var( --lz-surface-2 ); | ||
border-color: var( --lz-line ); | border-color: var( --lz-line ); | ||
| Line 332: | Line 331: | ||
} | } | ||
/* Monochrome skin icons. | /* Monochrome skin icons. | ||
* | |||
* Only elements that are PURELY an icon may go in this list. A filter applies | |||
* to an element's text as well as its background image, so filtering anything | |||
* that renders a label inverts the label too — and because the computed | |||
* `color` is unchanged, a contrast scan reports it as fine while it looks | |||
* wrong on screen. That is how #mw-header-container h2 ("Datanasov" plus an | |||
* avatar glyph) slipped through once. Check filtered elements by eye. | |||
* | |||
* Excluded on purpose: #searchButton (re-grounded above — filtering it as | |||
* well flipped it back to light), and the footer's "Powered by MediaWiki" | |||
* badge, which is a real logo. */ | |||
body.skin-timeless .mw-echo-notifications-badge, | body.skin-timeless .mw-echo-notifications-badge, | ||
body.skin-timeless #mw-searchButton | body.skin-timeless #mw-searchButton { | ||
filter: var( --lz-icon-filter ); | filter: var( --lz-icon-filter ); | ||
} | } | ||
Revision as of 20:34, 9 August 2026
/* ============================================================================
* MediaWiki:Common.css — wiki.linuxize.net
* MediaWiki 1.41.5. Default skin: Timeless (since 2026-08-09).
* Vector legacy is still selectable, so its rules are kept and scoped to
* `body.skin-vector`.
*
* Loads via the `site.styles` module, after the skin's own styles, so
* equal-specificity selectors win without !important. !important is used only
* where it overrides an inline style attribute in page wikitext (section 9).
*
* Sections:
* 1. Tokens
* 2. Vector legacy chrome <- inert under Timeless
* 3. Vector legacy responsive layout <- scoped to body.skin-vector
* 4. Content typography
* 5. Code blocks (SyntaxHighlight / Pygments)
* 6. TOC
* 7. Tables
* 8. Card grids (index pages)
* 9. Legacy inline-style box neutraliser
* 10. Print
* ========================================================================= */
/* ---------------------------------------------------------------------------
* 1. TOKENS
* Light is the base; the two dark blocks redefine tokens only.
*
* Any change to a colour here must be applied in THREE places or the themes
* drift apart: :root, the prefers-color-scheme block, the [data-theme="dark"]
* block. There is a parity check for this in the README.
* ------------------------------------------------------------------------ */
:root {
--lz-ground: #faf7f1;
--lz-surface: #ffffff;
--lz-surface-2: #f2ebdf;
--lz-ink: #201b14;
--lz-ink-2: #574d40;
--lz-ink-3: #8b7f6d;
--lz-line: #e4dbc9;
--lz-line-strong: #cfc2aa;
--lz-accent: #8a5b12;
--lz-accent-hov: #6d4709;
--lz-accent-tint: #f3e7ce;
--lz-visited: #6b4a7a;
--lz-red: #a12f2f;
/* Timeless draws a red/blue/green tricolour under the header and a teal
* rule above the footer. Recoloured here into a warm ramp from the site
* palette — the device is kept, the clashing hues are not. */
--lz-band-1: #7a4e0e;
--lz-band-2: #a9752a;
--lz-band-3: #c9a45c;
--lz-band-rule: #a9752a;
/* Code panel — a warm tint that belongs to the page, not a black slab
* dropped onto it. Sits just below the white content surface so the panel
* reads as inset without fighting the cream ground. */
--lz-code-bg: #f6f0e5;
--lz-code-bg-2: #ebe1cd;
--lz-code-ink: #2a2318;
--lz-code-line: #e0d5be;
--lz-code-chrome: #665d4d;
--lz-code-chrome-hover-bg: #e3d8c2;
--lz-code-chrome-hover-ink: #2a2318;
--lz-code-ok: #4a6a1f;
--lz-code-ok-line: #8fa565;
--lz-code-link: #8a5b12;
--lz-code-link-hover: #6d4709;
/* Syntax inks. Dark on light here; the dark blocks below invert them.
* Contrast against --lz-code-bg is measured in the browser after any
* change to these — see the note above section 5. */
--lz-syn-comment: #6f6552;
--lz-syn-keyword: #96540a;
--lz-syn-builtin: #7a520f;
--lz-syn-var: #1a5570;
--lz-syn-string: #46661d;
--lz-syn-op: #a04a18;
--lz-syn-punct: #6b6152;
--lz-syn-num: #a04a18;
--lz-syn-err: #a12f2f;
--lz-shadow: 0 1px 2px rgba( 32, 27, 20, 0.05 ), 0 8px 24px -12px rgba( 32, 27, 20, 0.18 );
/* Skin icons are monochrome dark SVG data-URIs, so they vanish on a dark
* ground. Flipping them via a token keeps this theme-driven instead of
* duplicating selectors inside each dark block. */
--lz-icon-filter: none;
--lz-mono: ui-monospace, "SF Mono", SFMono-Regular, "JetBrains Mono", Menlo, Consolas, "Liberation Mono", monospace;
--lz-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
@media ( prefers-color-scheme: dark ) {
:root:not( [ data-theme="light" ] ) {
--lz-ground: #14110c;
--lz-surface: #1b1711;
--lz-surface-2: #231e16;
--lz-ink: #ece5d8;
--lz-ink-2: #b3a894;
--lz-ink-3: #877c6a;
--lz-line: #322b21;
--lz-line-strong: #4a4133;
--lz-accent: #dda94d;
--lz-accent-hov: #f0c273;
--lz-accent-tint: #2c2417;
--lz-visited: #c3a0d4;
--lz-red: #e08585;
--lz-shadow: 0 1px 2px rgba( 0, 0, 0, 0.4 ), 0 8px 24px -12px rgba( 0, 0, 0, 0.7 );
--lz-icon-filter: invert( 0.82 );
--lz-band-1: #e0b45e;
--lz-band-2: #b78a35;
--lz-band-3: #7d5f22;
--lz-band-rule: #b78a35;
/* The code panel sinks BELOW the surface here. Two dark surfaces this
* close together only separate by ~1.1:1, so the border and the
* header strip carry the delineation, not the fill.
* Keep every value here identical to the [data-theme="dark"] block
* below — a viewer on system-dark never stamps the attribute. */
--lz-code-bg: #0c0a06;
--lz-code-bg-2: #171208;
--lz-code-ink: #e9e1d2;
--lz-code-line: #3d3323;
--lz-code-chrome: #9c8f79;
--lz-code-chrome-hover-bg: #2e281d;
--lz-code-chrome-hover-ink: #e9e1d2;
--lz-code-ok: #b8c98a;
--lz-code-ok-line: #4d5a35;
--lz-code-link: #e6bd6d;
--lz-code-link-hover: #f5d79b;
--lz-syn-comment: #7d7263;
--lz-syn-keyword: #dda94d;
--lz-syn-builtin: #e6bd6d;
--lz-syn-var: #8fc0d6;
--lz-syn-string: #b8c98a;
--lz-syn-op: #d98e5a;
--lz-syn-punct: #a2947e;
--lz-syn-num: #d98e5a;
--lz-syn-err: #e08585;
}
}
/* Explicit choice, set by the toggle in Common.js. Wins over the OS in both
* directions because it is stamped on <html> and matches on attribute. */
:root[ data-theme="dark" ] {
--lz-ground: #14110c;
--lz-surface: #1b1711;
--lz-surface-2: #231e16;
--lz-ink: #ece5d8;
--lz-ink-2: #b3a894;
--lz-ink-3: #877c6a;
--lz-line: #322b21;
--lz-line-strong: #4a4133;
--lz-accent: #dda94d;
--lz-accent-hov: #f0c273;
--lz-accent-tint: #2c2417;
--lz-visited: #c3a0d4;
--lz-red: #e08585;
--lz-shadow: 0 1px 2px rgba( 0, 0, 0, 0.4 ), 0 8px 24px -12px rgba( 0, 0, 0, 0.7 );
--lz-icon-filter: invert( 0.82 );
--lz-band-1: #e0b45e;
--lz-band-2: #b78a35;
--lz-band-3: #7d5f22;
--lz-band-rule: #b78a35;
/* Must stay identical to the prefers-color-scheme block above. */
--lz-code-bg: #0c0a06;
--lz-code-bg-2: #171208;
--lz-code-ink: #e9e1d2;
--lz-code-line: #3d3323;
--lz-code-chrome: #9c8f79;
--lz-code-chrome-hover-bg: #2e281d;
--lz-code-chrome-hover-ink: #e9e1d2;
--lz-code-ok: #b8c98a;
--lz-code-ok-line: #4d5a35;
--lz-code-link: #e6bd6d;
--lz-code-link-hover: #f5d79b;
--lz-syn-comment: #7d7263;
--lz-syn-keyword: #dda94d;
--lz-syn-builtin: #e6bd6d;
--lz-syn-var: #8fc0d6;
--lz-syn-string: #b8c98a;
--lz-syn-op: #d98e5a;
--lz-syn-punct: #a2947e;
--lz-syn-num: #d98e5a;
--lz-syn-err: #e08585;
}
/* ---------------------------------------------------------------------------
* 2. VECTOR LEGACY CHROME
* Vector 2010 hardcodes a blue/grey palette and positions #mw-panel
* absolutely at 10em. Re-ground it, then unpick the fixed widths in §3.
* ------------------------------------------------------------------------ */
body {
background-color: var( --lz-ground );
color: var( --lz-ink );
font-family: var( --lz-sans );
font-size: 0.9375rem;
line-height: 1.65;
}
/* The gradient strip behind the tab row. */
#mw-page-base {
background-color: var( --lz-ground );
background-image: none;
height: 3.5em;
}
#mw-head-base {
background: transparent;
}
/* Main content column — colours only, safe under any skin. */
#content.mw-body {
background: var( --lz-surface );
color: var( --lz-ink );
font-size: 1rem;
}
/* Layout below is Vector-legacy-specific and must stay scoped to it.
* Vector legacy pins #content at margin-left:10em and lets it run to the
* viewport edge, so code blocks stretch past 1400px on a wide monitor; the
* max() caps the measure and centres it without encroaching on the sidebar
* rail. Timeless lays out its own content container, so applying this there
* nests a second bordered box inside the first and double-indents it. */
body.skin-vector #content.mw-body {
border: 1px solid var( --lz-line );
border-radius: 0.5rem;
padding: 1.75em 2em 2em;
margin-top: 0;
margin-left: max( 10em, calc( ( 100% - 64rem ) / 2 ) );
margin-right: 1em;
max-width: 64rem;
}
body.skin-vector #footer {
margin-left: max( 10em, calc( ( 100% - 64rem ) / 2 ) );
margin-right: 1em;
max-width: 64rem;
}
/* --- Timeless chrome surfaces ---
* Timeless paints its chrome from its own cool grey ramp: #eaecf0 page ground,
* #f8f9fa sidebar boxes, #ffffff header and content, #c8ccd1 input borders.
* Repointing those at our tokens does two jobs at once — it warms the chrome
* to match the palette in light mode, and it is what makes dark mode possible
* at all. These eight surfaces were exactly the ones that stayed light and
* produced the 32 low-contrast elements measured on 2026-08-09.
*
* Deliberately NOT wrapped in a dark-only guard: the warm surfaces are an
* improvement in light mode too. */
body.skin-timeless #mw-header-container {
background: var( --lz-surface );
}
body.skin-timeless #mw-content-container {
background: var( --lz-ground );
}
body.skin-timeless #mw-content,
body.skin-timeless #site-navigation,
body.skin-timeless #site-tools,
body.skin-timeless #page-tools,
body.skin-timeless #catlinks-sidebar,
body.skin-timeless .sidebar-chunk {
background: var( --lz-surface );
border-color: var( --lz-line );
}
body.skin-timeless #simpleSearch,
body.skin-timeless #searchInput {
background: var( --lz-surface-2 );
border-color: var( --lz-line );
color: var( --lz-ink );
}
body.skin-timeless #searchInput::placeholder {
color: var( --lz-ink-3 );
}
/* Portlet headings in both sidebars. */
body.skin-timeless #mw-site-navigation h3,
body.skin-timeless #mw-related-navigation h3,
body.skin-timeless .sidebar-chunk h3 {
color: var( --lz-ink-2 );
border-bottom-color: var( --lz-line );
}
/* Dropdowns (user menu, "More") float above the page on their own surface.
* #personal-inner must be matched by ID — Timeless styles it with an ID
* selector, which outranks any class selector we could write here. */
body.skin-timeless .dropdown,
body.skin-timeless #personal-inner,
body.skin-timeless #p-variants-desktop .dropdown,
body.skin-timeless .mw-portlet .dropdown {
background: var( --lz-surface );
border-color: var( --lz-line );
color: var( --lz-ink );
}
body.skin-timeless #p-personal-label,
body.skin-timeless #personal-inner h3 {
color: var( --lz-ink-2 );
}
/* Timeless hardcodes color:#000 on the header region and on the page-header
* tab links. `body { color }` cannot reach either, so on a dark ground they
* rendered black-on-near-black at 1.18:1. */
body.skin-timeless #mw-header-container,
body.skin-timeless #p-banner,
body.skin-timeless #p-banner a,
body.skin-timeless #user-tools,
body.skin-timeless #personal,
body.skin-timeless #personal-extra {
color: var( --lz-ink );
}
body.skin-timeless #mw-page-header-links a {
color: var( --lz-ink-2 );
}
body.skin-timeless #mw-page-header-links li.selected a {
color: var( --lz-ink );
font-weight: 600;
}
/* Monochrome skin icons.
*
* Only elements that are PURELY an icon may go in this list. A filter applies
* to an element's text as well as its background image, so filtering anything
* that renders a label inverts the label too — and because the computed
* `color` is unchanged, a contrast scan reports it as fine while it looks
* wrong on screen. That is how #mw-header-container h2 ("Datanasov" plus an
* avatar glyph) slipped through once. Check filtered elements by eye.
*
* Excluded on purpose: #searchButton (re-grounded above — filtering it as
* well flipped it back to light), and the footer's "Powered by MediaWiki"
* badge, which is a real logo. */
body.skin-timeless .mw-echo-notifications-badge,
body.skin-timeless #mw-searchButton {
filter: var( --lz-icon-filter );
}
/* Theme toggle, mounted into the header by Common.js. */
body.skin-timeless .lz-theme-toggle {
margin: 0 0.35em;
}
/* --- Timeless accent bands ---
* Timeless ships a red / blue / green tricolour strip under the header
* (three stacked divs inside #mw-header-hack and .color-bar) and a teal rule
* above the footer on #mw-content-container. Against a warm amber palette
* those three hues read as someone else's brand. Recoloured into a warm ramp,
* deep to light, left to right: the structural device survives, the clash
* does not.
*
* Only border-*-color is set on the tab underline, never border-width, so the
* broad `li.selected` selector cannot accidentally draw a border on a list
* item in article content. */
body.skin-timeless .color-left {
background-color: var( --lz-band-1 );
}
body.skin-timeless .color-middle {
background-color: var( --lz-band-2 );
}
body.skin-timeless .color-right {
background-color: var( --lz-band-3 );
}
body.skin-timeless #mw-content-container {
border-bottom-color: var( --lz-band-rule );
}
body.skin-timeless li.selected {
border-bottom-color: var( --lz-accent );
}
/* --- Timeless footer ---
* Timeless paints its footer as white text on its own dark ground. The `body`
* background token above replaces that ground with cream, which left white on
* cream at 1.07:1 — invisible. Repaint the whole footer from our tokens.
* Measured after the fix: body text 6.98:1, links 4.94:1 — both pass AA.
* (Provisional — the proper Timeless port lands with the rest of the chrome.) */
body.skin-timeless #mw-footer,
body.skin-timeless #mw-footer-container {
background: var( --lz-surface-2 );
color: var( --lz-ink-2 );
border-top: 1px solid var( --lz-line );
}
body.skin-timeless #mw-footer li,
body.skin-timeless #mw-footer #footer-info li,
body.skin-timeless #mw-footer #footer-places li {
color: var( --lz-ink-2 );
}
body.skin-timeless #mw-footer a {
color: var( --lz-accent );
}
body.skin-timeless #mw-footer a:hover {
color: var( --lz-accent-hov );
}
a,
.mw-body a {
color: var( --lz-accent );
text-decoration-color: rgba( 138, 91, 18, 0.35 );
text-underline-offset: 0.15em;
}
a:hover,
.mw-body a:hover {
color: var( --lz-accent-hov );
text-decoration-color: currentColor;
}
.mw-body a:visited {
color: var( --lz-visited );
}
.mw-body a.new,
a.new {
color: var( --lz-red );
}
a.external,
.mw-body a.external {
background-image: none;
padding-right: 0;
}
/* External links get a typographic marker instead of the sprite, which does
* not exist in a dark palette. */
.mw-parser-output a.external::after {
content: " \2197";
font-size: 0.85em;
color: var( --lz-ink-3 );
}
:focus-visible {
outline: 2px solid var( --lz-accent );
outline-offset: 2px;
border-radius: 2px;
}
/* --- left sidebar --- */
#mw-panel.vector-legacy-sidebar {
padding-top: 1em;
}
#mw-panel .vector-menu-portal .vector-menu-heading {
font-family: var( --lz-mono );
font-size: 0.68rem;
font-weight: 600;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var( --lz-ink-3 );
background: none;
border-bottom: 0;
padding-bottom: 0.4em;
}
#mw-panel .vector-menu-portal .vector-menu-content-list li a {
display: block;
padding: 0.25em 0.6em;
border-left: 2px solid transparent;
border-radius: 0.3rem;
color: var( --lz-ink-2 );
font-size: 0.8125rem;
}
#mw-panel .vector-menu-portal .vector-menu-content-list li a:hover {
background: var( --lz-surface-2 );
color: var( --lz-ink );
text-decoration: none;
}
#mw-panel .vector-menu-portal .vector-menu-content-list li a:visited {
color: var( --lz-ink-2 );
}
/* --- top tabs (Page / Discussion / Read / Edit / History) --- */
/* Vector legacy draws these with a sprite; replace with flat shapes so they
* survive a dark ground. */
#mw-head .vector-menu-tabs,
#mw-head .vector-menu-tabs ul,
#mw-head .vector-menu-tabs li,
#mw-head .vector-menu-tabs li.selected {
background-image: none;
}
#mw-head .vector-menu-tabs li {
background: transparent;
}
#mw-head .vector-menu-tabs li a,
#mw-head .vector-menu-tabs li.selected a {
font-family: var( --lz-mono );
font-size: 0.75rem;
letter-spacing: 0.02em;
color: var( --lz-ink-3 );
background: transparent;
padding: 0 0.9em;
}
#mw-head .vector-menu-tabs li a:hover {
color: var( --lz-accent );
}
#mw-head .vector-menu-tabs li.selected a,
#mw-head .vector-menu-tabs li.selected a:visited {
color: var( --lz-ink );
font-weight: 600;
box-shadow: inset 0 -2px 0 var( --lz-accent );
}
#p-personal .vector-menu-content-list li a {
font-family: var( --lz-mono );
font-size: 0.72rem;
color: var( --lz-ink-3 );
}
#p-personal .vector-menu-content-list li a:hover {
color: var( --lz-accent );
}
/* --- search --- */
#p-search .vector-search-box-inner input,
#simpleSearch #searchInput {
background: var( --lz-surface-2 );
border: 1px solid var( --lz-line );
border-radius: 0.35rem;
color: var( --lz-ink );
font-family: var( --lz-mono );
font-size: 0.8rem;
padding: 0.35em 0.6em;
}
#simpleSearch #searchInput::placeholder {
color: var( --lz-ink-3 );
}
/* --- theme toggle, injected by Common.js --- */
.lz-theme-toggle {
display: inline-flex;
align-items: center;
gap: 0.35em;
padding: 0.15em 0.55em;
background: transparent;
border: 1px solid var( --lz-line );
border-radius: 0.3rem;
color: var( --lz-ink-2 );
font-family: var( --lz-mono );
font-size: 0.72rem;
cursor: pointer;
}
.lz-theme-toggle:hover {
color: var( --lz-accent );
border-color: var( --lz-line-strong );
}
/* --- footer --- */
#footer {
border-top: 1px solid var( --lz-line );
margin-top: 2rem;
padding-top: 1rem;
}
#footer li,
#footer a {
font-family: var( --lz-mono );
font-size: 0.72rem;
color: var( --lz-ink-3 );
}
#footer #footer-icons {
filter: grayscale( 1 ) opacity( 0.6 );
}
/* --- category links --- */
.catlinks {
background: var( --lz-surface-2 );
border: 1px solid var( --lz-line );
border-radius: 0.4rem;
padding: 0.6em 0.9em;
font-size: 0.85rem;
}
.catlinks a {
font-family: var( --lz-mono );
font-size: 0.8rem;
}
/* ---------------------------------------------------------------------------
* 3. RESPONSIVE LAYOUT
*
* NOTE: these queries do nothing on phones until the skin stops emitting
* <meta name="viewport" content="width=1000">. Fix that with either
* $wgVectorResponsive = true; in LocalSettings.php (preferred), or
* the viewport patch at the top of Common.js (no server access).
* ------------------------------------------------------------------------ */
@media screen and ( max-width: 850px ) {
/* SCOPED TO VECTOR LEGACY. Timeless is responsive on its own; turning its
* <body> into a flex column and reassigning order to every child would
* wreck its layout. Selectors below that exist only in Vector legacy
* (#mw-navigation, #mw-panel, #mw-head, #mw-page-base, #left-navigation,
* #right-navigation) are inert under Timeless and left unscoped; the ones
* that also match in Timeless are scoped explicitly.
*
* Vector legacy's source order is #content, then #mw-navigation, then
* #footer, and it positions the nav absolutely on top. Unpinning alone
* drops the whole navigation below the article and leaves #p-personal
* overlapping the page title. Make <body> a flex column instead and
* reorder, which is the only way to get nav above content without
* touching the skin's HTML. */
body.skin-vector {
display: flex;
flex-direction: column;
}
body.skin-vector > * {
order: 5;
}
#mw-navigation {
order: 1;
}
body.skin-vector #content.mw-body {
order: 2;
}
body.skin-vector #footer {
order: 3;
}
/* Decorative strips that only exist to sit behind the absolute header. */
#mw-page-base,
#mw-head-base {
display: none;
}
/* Unpin the header and the personal bar.
* flow-root is load-bearing: #left-navigation and #right-navigation are
* floats, and #mw-panel below becomes a flex container — i.e. a new block
* formatting context — so it shifts sideways to dodge those floats unless
* #mw-head contains them. Without this the whole sidebar renders inset
* about 235px from the left. */
#mw-head {
position: static;
width: auto;
float: none;
display: flow-root;
margin: 0.5em 0 0;
padding: 0 0.75em;
}
body.skin-vector #p-personal {
position: static;
width: auto;
}
body.skin-vector #p-personal .vector-menu-content-list {
display: flex;
flex-wrap: wrap;
gap: 0 0.9em;
padding: 0;
margin: 0;
}
body.skin-vector #p-personal .vector-menu-content-list li {
margin: 0;
}
#left-navigation,
#right-navigation {
float: none;
margin-left: 0;
}
body.skin-vector #p-search {
float: none;
margin: 0.5em 0;
}
body.skin-vector #simpleSearch,
body.skin-vector #p-search .vector-search-box-inner input {
width: 100%;
}
/* Sidebar collapses to one compact row of links under the header.
* At 375px tall the full desktop sidebar pushed the article entirely
* below the fold, so Tools is dropped here — it is editor plumbing
* (What links here, Upload file, Cite this page), not reader navigation,
* and it stays available on desktop. */
#mw-panel.vector-legacy-sidebar {
position: static;
width: auto;
clear: both;
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0 1rem;
margin: 0.4em 0 0;
padding: 0.5em 0.75em 0.6em;
border-top: 1px solid var( --lz-line );
}
#mw-panel #p-tb {
display: none;
}
#mw-panel .vector-menu-portal {
margin: 0;
flex: 0 1 auto;
}
#mw-panel .vector-menu-portal .vector-menu-heading {
display: none;
}
#mw-panel .vector-menu-portal .vector-menu-content {
margin: 0;
padding: 0;
}
#mw-panel .vector-menu-content-list {
display: flex;
flex-wrap: wrap;
gap: 0 0.25rem;
margin: 0;
padding: 0;
}
#mw-panel .vector-menu-portal .vector-menu-content-list li {
margin: 0;
}
/* The 160px Tux is a desktop luxury; keep a small mark as the home link. */
#mw-panel #p-logo,
#mw-panel #p-logo a {
width: 2rem;
height: 2rem;
min-height: 0;
margin: 0;
background-size: contain;
background-repeat: no-repeat;
background-position: left center;
}
body.skin-vector #content.mw-body,
body.skin-vector #footer {
margin-left: 0.75em;
margin-right: 0.75em;
max-width: none;
border-radius: 0.5rem;
padding: 1.25em 1.1em 1.75em;
}
/* Long unbreakable paths and commands must not widen the page. */
.mw-parser-output {
overflow-wrap: break-word;
}
}
@media screen and ( max-width: 550px ) {
#content.mw-body {
padding: 1em 0.85em 1.5em;
}
.mw-parser-output .mw-highlight pre {
font-size: 0.75rem;
}
}
/* ---------------------------------------------------------------------------
* 4. CONTENT TYPOGRAPHY
* Monospace for headings — this wiki's subject matter is commands, and the
* mono display voice ties the page to the code blocks it is wrapped around.
* ------------------------------------------------------------------------ */
#firstHeading,
.mw-body h1,
.mw-body h2,
.mw-body h3,
.mw-body h4 {
font-family: var( --lz-mono );
font-weight: 600;
letter-spacing: -0.015em;
line-height: 1.25;
text-wrap: balance;
color: var( --lz-ink );
}
#firstHeading {
font-size: clamp( 1.6rem, 1.2rem + 1.6vw, 2.2rem );
border-bottom: 0;
padding-bottom: 0;
margin-bottom: 0.15em;
}
/* Vector legacy puts the tagline directly under the title. */
#siteSub {
display: block;
font-family: var( --lz-mono );
font-size: 0.72rem;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var( --lz-ink-3 );
margin-bottom: 1.5em;
padding-bottom: 1em;
border-bottom: 1px solid var( --lz-line );
}
.mw-body h2 {
font-size: 1.3rem;
margin-top: 2.2em;
padding-bottom: 0.3em;
border-bottom: 1px solid var( --lz-line );
}
.mw-body h3 {
font-size: 1.06rem;
margin-top: 1.9em;
}
.mw-body h4 {
font-size: 0.95rem;
margin-top: 1.6em;
color: var( --lz-ink-2 );
}
/* Headings need to clear the sticky-ish header when jumped to from the TOC. */
.mw-parser-output :is( h2, h3, h4 )[ id ],
.mw-parser-output .mw-headline {
scroll-margin-top: 1.5rem;
}
.mw-editsection {
font-family: var( --lz-mono );
font-size: 0.7rem;
font-weight: 400;
color: var( --lz-ink-3 );
}
.mw-editsection a {
color: var( --lz-ink-3 );
}
.mw-editsection a:hover {
color: var( --lz-accent );
}
/* Hold running prose near a 68ch measure. Code, tables and card grids opt
* out below so they can use the full column. */
.mw-parser-output > p,
.mw-parser-output > ul,
.mw-parser-output > ol,
.mw-parser-output > dl {
max-width: 68ch;
}
.mw-parser-output p {
margin: 0.85em 0;
}
.mw-parser-output li {
margin-bottom: 0.3em;
}
.mw-parser-output li::marker {
color: var( --lz-ink-3 );
}
.mw-parser-output hr {
border: 0;
border-top: 1px solid var( --lz-line );
margin: 2em 0;
}
.mw-parser-output blockquote {
margin: 1.2em 0;
padding: 0.1em 0 0.1em 1em;
border-left: 3px solid var( --lz-line-strong );
color: var( --lz-ink-2 );
}
/* Inline code — distinct from the dark block panels. */
.mw-parser-output code,
.mw-parser-output tt,
.mw-parser-output kbd,
.mw-parser-output samp {
font-family: var( --lz-mono );
font-size: 0.875em;
padding: 0.12em 0.35em;
background: var( --lz-surface-2 );
border: 1px solid var( --lz-line );
border-radius: 0.25rem;
color: var( --lz-ink );
overflow-wrap: break-word;
}
/* ---------------------------------------------------------------------------
* 5. CODE BLOCKS — SyntaxHighlight (Pygments) is already installed, so the
* token spans below already exist in the rendered HTML.
* Panels are dark in both themes; commands are the most scannable thing on
* the page and the palette does not have to be maintained twice.
* ------------------------------------------------------------------------ */
.mw-parser-output .mw-highlight {
position: relative;
margin: 1.2em 0;
background: var( --lz-code-bg );
border: 1px solid var( --lz-code-line );
border-radius: 0.45rem;
overflow: hidden;
}
.mw-parser-output .mw-highlight pre {
margin: 0;
padding: 0.85em 1em;
overflow-x: auto;
background: transparent;
border: 0;
color: var( --lz-code-ink );
font-family: var( --lz-mono );
font-size: 0.82rem;
line-height: 1.65;
tab-size: 4;
}
/* Language chip + copy button are injected by Common.js. */
.mw-parser-output .mw-highlight .lz-chead {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
padding: 0.35em 0.4em 0.35em 0.8em;
background: var( --lz-code-bg-2 );
border-bottom: 1px solid var( --lz-code-line );
}
.lz-lang {
font-family: var( --lz-mono );
font-size: 0.68rem;
font-weight: 600;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var( --lz-code-chrome );
}
.lz-copy {
padding: 0.2em 0.6em;
background: transparent;
border: 1px solid var( --lz-code-line );
border-radius: 0.25rem;
color: var( --lz-code-chrome );
font-family: var( --lz-mono );
font-size: 0.68rem;
letter-spacing: 0.04em;
cursor: pointer;
}
.lz-copy:hover {
background: var( --lz-code-chrome-hover-bg );
border-color: var( --lz-line-strong );
color: var( --lz-code-chrome-hover-ink );
}
.lz-copy.lz-done {
color: var( --lz-code-ok );
border-color: var( --lz-code-ok-line );
}
/* Pygments token palette — warm-dominant, one cool hue for variables.
* Theme-aware: these were hardcoded for a dark ground back when the panel was
* dark in both themes. Now the light theme uses a warm tint that belongs to
* the page, so the inks invert with it. Measured contrast against the panel
* ground is recorded in the README; re-measure after changing any of them. */
.mw-highlight .c,
.mw-highlight .c1,
.mw-highlight .cm,
.mw-highlight .cp {
color: var( --lz-syn-comment );
font-style: italic;
}
.mw-highlight .k,
.mw-highlight .kd,
.mw-highlight .kn,
.mw-highlight .kt {
color: var( --lz-syn-keyword );
}
.mw-highlight .nb,
.mw-highlight .nf,
.mw-highlight .nc {
color: var( --lz-syn-builtin );
}
.mw-highlight .nv,
.mw-highlight .vi,
.mw-highlight .na {
color: var( --lz-syn-var );
}
.mw-highlight .s,
.mw-highlight .s1,
.mw-highlight .s2,
.mw-highlight .sb,
.mw-highlight .se {
color: var( --lz-syn-string );
}
.mw-highlight .o,
.mw-highlight .ow {
color: var( --lz-syn-op );
}
.mw-highlight .p {
color: var( --lz-syn-punct );
}
.mw-highlight .m,
.mw-highlight .mi,
.mw-highlight .mf {
color: var( --lz-syn-num );
}
.mw-highlight .w {
color: inherit;
}
.mw-highlight .err {
color: var( --lz-syn-err );
background: transparent;
}
/* Unhighlighted <pre> (no lang= given, or a leading-space block in wikitext)
* gets the same panel treatment. Deliberately a descendant selector, not a
* child one — several pages nest these inside a wrapper div. The
* .mw-parser-output .mw-highlight pre rule above is more specific, so
* highlighted blocks keep their transparent inner background. */
.mw-parser-output pre {
background: var( --lz-code-bg );
border: 1px solid var( --lz-code-line );
border-radius: 0.45rem;
color: var( --lz-code-ink );
font-family: var( --lz-mono );
font-size: 0.82rem;
line-height: 1.65;
padding: 0.85em 1em;
overflow-x: auto;
}
/* Some index pages use a leading-space block as a link container, so links
* have to stay legible against the dark panel in both themes. */
.mw-parser-output pre a,
.mw-parser-output pre a:visited {
color: var( --lz-code-link );
}
.mw-parser-output pre a:hover {
color: var( --lz-code-link-hover );
}
.mw-parser-output pre a.new {
color: var( --lz-syn-err );
}
/* ---------------------------------------------------------------------------
* 6. TOC
* The floating bordered box becomes a quiet block with mono numbering.
* ------------------------------------------------------------------------ */
.mw-parser-output #toc,
.mw-parser-output .toc {
display: block;
max-width: 68ch;
background: var( --lz-surface-2 );
border: 1px solid var( --lz-line );
border-radius: 0.45rem;
padding: 0.9em 1.1em;
font-size: 0.9rem;
}
.mw-parser-output .toctitle h2,
.mw-parser-output #toc h2 {
font-family: var( --lz-mono );
font-size: 0.7rem;
font-weight: 600;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var( --lz-ink-3 );
border-bottom: 0;
margin: 0 0 0.5em;
padding: 0;
}
.mw-parser-output #toc ul {
margin: 0;
padding: 0;
list-style: none;
}
.mw-parser-output #toc ul ul {
margin-left: 1em;
}
.mw-parser-output #toc li {
margin-bottom: 0.15em;
}
.mw-parser-output #toc a {
text-decoration: none;
color: var( --lz-ink-2 );
}
.mw-parser-output #toc a:hover {
color: var( --lz-accent );
text-decoration: underline;
}
.mw-parser-output #toc .tocnumber {
font-family: var( --lz-mono );
font-size: 0.78rem;
color: var( --lz-ink-3 );
margin-right: 0.3em;
}
/* ---------------------------------------------------------------------------
* 7. TABLES
* ------------------------------------------------------------------------ */
.mw-parser-output table.wikitable {
background: var( --lz-surface );
color: var( --lz-ink );
border: 1px solid var( --lz-line );
border-collapse: collapse;
border-radius: 0.45rem;
margin: 1.2em 0;
font-size: 0.9rem;
font-variant-numeric: tabular-nums;
overflow: hidden;
}
.mw-parser-output table.wikitable > * > tr > th,
.mw-parser-output table.wikitable > * > tr > td {
border: 0;
border-bottom: 1px solid var( --lz-line );
padding: 0.5em 0.85em;
text-align: left;
}
.mw-parser-output table.wikitable > * > tr > th {
background: var( --lz-surface-2 );
font-family: var( --lz-mono );
font-size: 0.72rem;
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var( --lz-ink-2 );
white-space: nowrap;
}
.mw-parser-output table.wikitable > * > tr:last-child > td {
border-bottom: 0;
}
/* Wide tables scroll inside their own container, never the page body.
* Wrap the table in <div class="lz-scroll"> ... </div> in wikitext. */
.lz-scroll {
overflow-x: auto;
max-width: 100%;
}
/* ---------------------------------------------------------------------------
* 8. CARD GRIDS — index pages
*
* Two levels of adoption:
* (a) Template:Card -> .lz-card with a description and a command chip.
* (b) Zero content edits: add class="lz-cards" to an existing bullet list
* and each <li> becomes a title-only card.
* ------------------------------------------------------------------------ */
.mw-parser-output .lz-cardgrid,
.mw-parser-output ul.lz-cards {
display: grid;
grid-template-columns: repeat( auto-fill, minmax( 15rem, 1fr ) );
gap: 0.7rem;
max-width: none;
margin: 1em 0 2em;
padding: 0;
list-style: none;
}
.mw-parser-output ul.lz-cards li {
margin: 0;
list-style: none;
}
.mw-parser-output .lz-card,
.mw-parser-output ul.lz-cards > li {
display: flex;
flex-direction: column;
gap: 0.3rem;
padding: 0.8rem 0.95rem;
background: var( --lz-surface );
border: 1px solid var( --lz-line );
border-radius: 0.45rem;
}
.mw-parser-output .lz-card:hover,
.mw-parser-output ul.lz-cards > li:hover {
border-color: var( --lz-accent );
box-shadow: var( --lz-shadow );
}
.mw-parser-output .lz-card a,
.mw-parser-output ul.lz-cards > li > a {
font-weight: 600;
font-size: 0.93rem;
line-height: 1.3;
color: var( --lz-ink );
text-decoration: none;
}
.mw-parser-output .lz-card:hover a,
.mw-parser-output ul.lz-cards > li:hover > a {
color: var( --lz-accent );
}
.mw-parser-output .lz-card-desc {
font-size: 0.8rem;
line-height: 1.45;
color: var( --lz-ink-3 );
}
.mw-parser-output .lz-card-cmd {
font-family: var( --lz-mono );
font-size: 0.74rem;
color: var( --lz-accent );
overflow-wrap: break-word;
}
/* Template:Card always emits both slots; collapse the unused ones so the
* flex gap does not leave a hole. Avoids needing ParserFunctions #if. */
.mw-parser-output .lz-card-desc:empty,
.mw-parser-output .lz-card-cmd:empty {
display: none;
}
/* --- Main Page hero ---
* "Main Page" is a useless <h1>, so it is hidden and the hero carries its own
* real <h1> instead — hiding the skin's title without replacing it would leave
* the document with no top-level heading. */
body.page-Main_Page #firstHeading,
body.page-Main_Page #siteSub {
display: none;
}
.mw-parser-output .lz-hero {
margin: 0 0 2.25rem;
padding: 0 0 1.5rem;
border-bottom: 1px solid var( --lz-line );
}
.mw-parser-output .lz-hero h1 {
margin: 0 0 0.5rem;
font-family: var( --lz-mono );
font-size: clamp( 1.5rem, 1.1rem + 1.7vw, 2.2rem );
font-weight: 600;
letter-spacing: -0.02em;
line-height: 1.15;
color: var( --lz-accent );
border-bottom: 0;
text-wrap: balance;
}
.mw-parser-output .lz-hero p {
max-width: 62ch;
margin: 0;
color: var( --lz-ink-2 );
font-size: 1.02rem;
}
/* Quiet footnote block — the contribute line at the foot of the Main Page. */
.mw-parser-output .lz-footnote {
max-width: 62ch;
margin: 2.5rem 0 0;
padding-top: 1.25rem;
border-top: 1px solid var( --lz-line );
font-size: 0.9rem;
color: var( --lz-ink-3 );
}
.mw-parser-output .lz-footnote p {
margin: 0;
max-width: none;
}
/* Section rules above each card group. */
.mw-parser-output h2.lz-section {
display: flex;
align-items: center;
gap: 0.7rem;
font-size: 0.78rem;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var( --lz-ink-3 );
border-bottom: 0;
padding-bottom: 0;
}
.mw-parser-output h2.lz-section::after {
content: "";
flex: 1;
height: 1px;
background: var( --lz-line );
}
/* ---------------------------------------------------------------------------
* 9. LEGACY INLINE-STYLE BOX
*
* Every article's wikitext currently hardcodes:
* <div style="margin-top:1.5em; border:3px solid #ae8c55; border-radius:.5em;
* padding:1em 1.5em; font-size:1.2em">
* Inline styles outrank the cascade, so !important is required here. This is
* a bridge — the real fix is migrating those pages to Template:Box, after
* which this block can be deleted.
* ------------------------------------------------------------------------ */
.mw-parser-output div[ style*="#ae8c55" ] {
margin-top: 0 !important;
padding: 0 !important;
border: 0 !important;
border-radius: 0 !important;
font-size: 1em !important;
}
/* The Copperplate banner on Main Page. */
.mw-parser-output div[ style*="#936c29" ] {
color: var( --lz-ink ) !important;
font-family: var( --lz-mono ) !important;
font-size: 1.75rem !important;
letter-spacing: -0.02em;
}
.mw-parser-output div[ style*="Copperplate" ] span,
.mw-parser-output span[ style*="Copperplate" ] {
font-family: var( --lz-mono ) !important;
}
/* Intended replacement, once Template:Box exists. */
.mw-parser-output .lz-note {
display: flex;
gap: 0.75rem;
max-width: 68ch;
margin: 1.3em 0;
padding: 0.85em 1em;
background: var( --lz-accent-tint );
border-left: 3px solid var( --lz-accent );
border-radius: 0 0.35rem 0.35rem 0;
font-size: 0.94rem;
color: var( --lz-ink-2 );
}
.mw-parser-output .lz-note p {
margin: 0;
max-width: none;
}
.mw-parser-output .lz-note::before {
content: "!";
flex: none;
font-family: var( --lz-mono );
font-weight: 700;
color: var( --lz-accent );
}
.mw-parser-output .lz-note.lz-warn {
background: rgba( 161, 47, 47, 0.08 );
border-left-color: var( --lz-red );
}
.mw-parser-output .lz-note.lz-warn::before {
content: "\26A0";
color: var( --lz-red );
}
/* ---------------------------------------------------------------------------
* 10. PRINT & MOTION
* ------------------------------------------------------------------------ */
@media print {
.mw-parser-output .mw-highlight,
.mw-parser-output > pre {
background: #fff;
border: 1px solid #999;
color: #000;
}
.mw-parser-output .mw-highlight pre {
color: #000;
}
.lz-chead,
.lz-theme-toggle {
display: none;
}
}
@media ( prefers-reduced-motion: reduce ) {
* {
transition: none !important;
animation: none !important;
scroll-behavior: auto !important;
}
}