Categories
Challenge category configuration, colors, icons, and customization.
Each challenge category in rCTF has its own color palette, icon, and display name configured in apps/web/src/lib/utils/categories.ts. The system uses dynamic CSS variable injection to apply category-specific colors to components. A category’s color key (e.g., red) maps to CSS variables like --category-foreground-l0, which components reference through Tailwind classes such as text-category-foreground-l0.
Default categories#
Each of the default categories uses a color specified in Category colors.
A handful of category aliases (binary → pwn, rev → reverse, cryptography → crypto) make naming a bit more flexible.
Note (How category styling works)
The getCategoryStyle(color) function generates inline CSS that binds a color’s variables to generic category tokens:
getCategoryStyle('red')// Returns:// "--category-foreground-l0: var(--foreground-red-l0);// --category-foreground-l1: var(--foreground-red-l1);// --category-background-l0: var(--background-red-l0);// --category-background-l1: var(--background-red-l1);// --category-background-l1-hover: var(--background-red-l1-hover);"Components apply this style to a container, then use generic classes:
<div style={getCategoryStyle(config.color)}> <span class="text-category-foreground-l0">{challenge.name}</span> <span class="bg-category-background-l0 text-category-foreground-l1"> {config.name} </span></div>Each color has five variables defined in app.css for both light and dark modes. These are --background-l0, --background-l1, --background-l1-hover, --foreground-l0, and --foreground-l1.
Adding a custom category#
Add an entry to categoryConfigs with a name, icon component, Iconify name (for exports), and color key:
import { IconCpuFilled } from '$lib/icons'
export const categoryConfigs: Record<string, CategoryConfig> = { // ... existing hardware: { name: 'Hardware', icon: IconCpuFilled, iconName: 'tabler:cpu-filled', color: 'teal', // Use an existing color, or add a new one },}In lib/icons/index.ts, export the icon:
export { default as IconAlertCircleFilled } from '@iconify-svelte/tabler/alert-circle-filled'export { default as IconCameraFilled } from '@iconify-svelte/tabler/camera-filled'export { default as IconCpuFilled } from '@iconify-svelte/tabler/cpu-filled'export { default as IconPhotoFilled } from '@iconify-svelte/tabler/photo-filled'export { default as IconAlertTriangleFilled } from '@iconify-svelte/tabler/alert-triangle-filled'Add your category to categoryOrder to control its position in the challenge list:
export const categoryOrder = [ 'sanity', 'pwn', 'reverse', 'crypto', 'forensics', 'blockchain', 'web', 'misc', 'ppc', 'koth', 'osint', 'hardware',]Categories not in this list show up alphabetically after the listed ones.
If your category uses a color not already in the palette, add variables to app.css following the existing pattern. Define --background-{color}-l0, --background-{color}-l1, --background-{color}-l1-hover, --foreground-{color}-l0, and --foreground-{color}-l1 for both :root and [data-theme='dark']. Don’t forget to map the colors in @theme inline too.
Utility functions#
The following utility functions help with category configuration: