Skip to content

Appearance

Logos, accent color, favicon, login background, the theme visitors land on, and a slot for your own CSS. Every field is optional, and an empty one falls back to the NexCTF default rather than showing nothing.

The settings live under Admin → Settings → Appearance.

Setting Type Default
Logo URL Image Empty, the CTF name is shown as text
Logo URL (dark mode) Image Empty, falls back to the main logo
Accent Color Color #3b82f6
Favicon URL Image Empty, the bundled NexCTF icon
Login Background URL Image Empty, the card sits on the page background
Default Theme Choice system
Custom CSS Text Empty

Save applies the whole category at once, and a dot on the tab marks unsaved changes. Everything but Default Theme repaints open pages immediately over the public event stream; a page that missed the push picks the change up within 60 seconds. The Admin API reads and writes the same values under appearance.logo_url, appearance.logo_url_dark, appearance.accent_color, appearance.favicon_url, appearance.login_background_url, appearance.default_theme and appearance.custom_css.

Two fields, one per theme. The dark one applies only when the dark theme is active and only when it is set; otherwise the main logo serves both. The fallback runs one way: setting only the dark logo leaves the light theme showing the CTF name as text.

The logo replaces the CTF name, so a lockup carrying your event name is the right asset. It is scaled to fit, never cropped:

Where Height Maximum width
Player navigation bar 28 px 160 px
Admin sidebar 24 px 112 px
Home page 64 px 320 px
Login card 64 px No limit
Verification and password reset email 48 px No limit

A lockup around 320 × 64 covers every slot, and SVG stays sharp at all of them.

Email always uses the light logo

Transactional emails are rendered on a white background, so they take Logo URL and never the dark variant. Some mail clients will not render SVG, which is the one case for supplying a PNG. An uploaded logo works here: its site-relative URL is made absolute against the instance's public address before the message goes out.

Transparent backgrounds

The logo is drawn straight onto the page and the sidebar, both of which change color with the theme, so a PNG with a white box baked in shows that box in dark mode. Export with a transparent background, or fill the dark variant field.

Accent color

One color for buttons, badges, links, active tabs and focus rings. It must be six-digit hex with a leading #: three-digit shorthand, rgb() and CSS color names are ignored and fall back to the built-in NexCTF blue. The picker beside the field always produces a valid value.

The shade is adjusted before use. NexCTF measures it against each background it will be drawn on and shifts it, darker on light surfaces and lighter on dark ones, until it clears:

Use Target contrast
Accent as text, and as a fill behind text 4.5:1, WCAG AA for body text
Focus rings and other interface outlines 3:1, WCAG AA for interface elements

The two themes are adjusted independently, so a pale color comes out noticeably darker in light mode while staying close to itself in dark mode. Text placed on an accent fill is picked to match, so no choice of accent can produce an unreadable button.

Favicon

Shown in the browser tab and in bookmarks. Setting it replaces every icon the app declares, at every size, including the Apple touch icon. Clearing it restores the bundled NexCTF icon and its PNG fallbacks at 16, 32, 48 and 180 pixels. One SVG covers everything; a raster image should be 180 × 180.

Login background

A full-bleed image behind the login card, covering the viewport and centered. It also covers the two screens that replace the login form, account disabled and email not verified. The card is opaque, so a busy image stays readable. Keep the file small, since it loads before the visitor has a session.

Default theme

Which theme a visitor gets before they pick one.

Value Effect
system Follow the operating system setting, and keep following it when it changes.
light Light theme.
dark Dark theme.

It only applies to visitors who have never picked a theme

The theme toggle stores the visitor's choice in their browser, and that choice wins. Anyone administering an instance has almost certainly used the toggle, so check this setting in a private window. A visitor who has already chosen cannot be overridden: it is an accessibility preference, not a branding decision.

Custom CSS

Arbitrary CSS, injected into every page as a <style> element after the app's own stylesheet. The escape hatch for what the fields above do not cover: the weight of a heading, the corner radius of a card, hiding an element you do not use.

Custom CSS is public

It is served to every visitor, signed in or not, as part of the public instance information. Treat it as published content.

The app's colors are CSS custom properties, so retheming a surface is usually one redefinition rather than a hunt for selectors:

/* Tint every card surface with the accent, in dark mode. */
.dark {
  --card: color-mix(in oklab, var(--nexctf-surface), var(--nexctf-accent) 6%);
}

Nothing validates what you type. The browser drops malformed rules one at a time rather than rejecting the block, so a stray brace silently takes the rest with it. Clearing the field restores the stock appearance immediately.

Uploading images

The four image fields take a pasted URL or an upload. Upload stores the file in object storage, marks it public and fills in its URL, so the asset is served from the same origin as the app and appears in the file manager like any other public file. An external URL works too, with the usual caveats: the host has to stay up for as long as the event runs, and it sees a request from every visitor.