Skip to main content
Version: 0.0.5

Advanced Theming

This guide covers the complete theme pack structure including layouts, manifests, audio, and icon configuration.

Theme Pack Structure

A complete theme pack contains:

~/.config/histui/themes/mytheme/
├── theme.css # Required: CSS styling
├── layout.xml # Optional: Widget layout and sizing
├── manifest.toml # Optional: Metadata, audio, and icon config
└── sounds/ # Optional: Audio files
├── notify.wav
└── critical.ogg

Layout Configuration (layout.xml)

The layout file defines which widgets appear and their arrangement.

Basic Structure

<popup min-width="250" max-width="400" min-height="0" max-height="600">
<header>
<icon size="48" />
<box orientation="vertical">
<summary />
<appname />
</box>
<stack-count />
</header>
<body />
<progress />
<image />
<actions />
</popup>
AttributeTypeDescription
min-widthintMinimum popup width in pixels
max-widthintMaximum popup width in pixels
min-heightintMinimum popup height in pixels
max-heightintMaximum popup height in pixels

Available Elements

ElementAttributesDescription
<icon>size="48"Application icon (Nerd Font fallback)
<summary>Notification title
<body>Notification message body
<appname>Application name
<timestamp>Time since notification
<progress>Progress bar (if hint provided)
<image>Notification image
<actions>Action buttons
<stack-count>Badge showing stacked notification count
<header>Container for header elements
<box>orientationContainer with vertical/horizontal layout

Layout Examples

Minimal (no icons):

<popup min-width="150" max-width="250" min-height="0" max-height="200">
<summary />
<body />
<progress />
</popup>

Compact (smaller icons):

<popup min-width="200" max-width="300" min-height="0" max-height="400">
<header>
<icon size="32" />
<summary />
<stack-count />
</header>
<body />
<progress />
<actions />
</popup>

Detailed (with timestamp):

<popup min-width="250" max-width="450" min-height="0" max-height="700">
<header>
<icon size="48" />
<box orientation="vertical">
<summary />
<box orientation="horizontal">
<appname />
<timestamp />
</box>
</box>
<stack-count />
</header>
<body />
<progress />
<image />
<actions />
</popup>

Theme Manifest (manifest.toml)

The manifest file configures metadata, audio, and icon settings.

Complete Reference

# Theme metadata
name = "My Theme"
description = "A custom notification theme"
author = "Your Name"
version = "1.0.0"

# Icon configuration
[icon]
size = 48 # Default icon size (can be overridden in layout.xml)

# Audio per urgency level
[audio.low]
path = "sounds/subtle.wav" # Path relative to theme directory
volume = 0.5 # Volume 0.0-1.0
repeat_count = -1 # -1 = once, 0 = until dismissed, N = N times

[audio.normal]
path = "sounds/notify.wav"
volume = 0.8
repeat_count = -1

[audio.critical]
path = "sounds/alert.ogg"
volume = 1.0
repeat_count = 0 # Repeat until dismissed
repeat_delay = "10s" # Delay between repeats

Audio Repeat Behavior

repeat_countBehavior
-1Play once, no repeat
0Repeat until notification dismissed
NRepeat N times

Supported Audio Formats

FormatExtensionNotes
WAV.wavPCM only (16-bit signed little-endian). IEEE Float format is not supported
Ogg Vorbis.oggFully supported
MP3.mp3Fully supported
Converting WAV files

If your WAV file doesn't play, it may be in IEEE Float format. Convert to PCM:

ffmpeg -i input.wav -acodec pcm_s16le -ar 44100 -ac 2 output.wav

Icon Configuration

histuid uses a multi-tier icon resolution system:

  1. Application-provided icon (from notification)
  2. GTK icon theme (freedesktop icons)
  3. Nerd Font symbol (fallback for missing icons)

Icon Aliases

Map application names to icon names for better resolution:

# ~/.config/histui/icon-aliases.toml

[aliases]
# Map app name to standard icon name
zapzap = "whatsapp"
telegram-desktop = "telegram"
firefox-esr = "firefox"
vesktop = "discord"
my-custom-app = "application-default-icon"

Nerd Font Symbols

When GTK icons aren't available, histuid displays Nerd Font symbols. Override the default symbols by pasting actual glyph characters:

# ~/.config/histui/icon-aliases.toml

[symbols]
# Override app symbols (icon name -> Nerd Font glyph)
# Copy glyphs from https://www.nerdfonts.com/cheat-sheet
spotify = "󰓇" # nf-md-spotify
discord = "󰙯" # nf-md-discord

# Override urgency fallbacks
critical = "󰀦" # nf-md-alert
normal = "󰂚" # nf-md-bell
low = "󰋼" # nf-md-information
undefined = "󰂜" # nf-md-bell-outline

# Override category fallbacks
notification = "󰂚" # nf-md-bell
im = "󱃲" # nf-md-chat
Symbol Format

Use actual Unicode characters in your TOML file, not escape sequences. Copy glyphs directly from the Nerd Fonts cheat sheet.

Built-in Aliases

histuid includes aliases for 350+ common applications:

  • Messaging: Discord, Slack, Telegram, WhatsApp, Signal
  • Browsers: Firefox, Chrome, Brave, Edge, Opera
  • Email: Thunderbird, Evolution, Geary
  • Media: Spotify, VLC, MPV
  • Development: VS Code, terminals, Git clients
  • And many more...

Icon Alias Generator

The icon aliases are generated using a combination of upstream metadata and AI-powered app mapping. See Icon Aliases for the full generator documentation.

Quick usage from project root:

# Regenerate aliases using existing knowledge base
task generate:icons:output

# Or manually from the generator directory
cd contrib/generate-icon-aliases
go build .
./generate-icon-aliases --output ../../internal/icon/aliases_default.toml

Font Configuration

CSS Variables

Themes should use these variables for fonts. Note that GTK CSS uses * (universal selector) instead of :root:

* {
--histui-font-family: inherit; /* System font */
--histui-font-size: 14px;
}

.notification-popup {
font-family: var(--histui-font-family);
font-size: var(--histui-font-size);
}

Override Priority

  1. CLI flags (highest): histuid --font "Ubuntu" --font-size 16
  2. Config file: theme.font = "Ubuntu"
  3. Theme CSS: * { --histui-font-family: "Ubuntu"; }

CSS Animations

GTK4 supports CSS animations via @keyframes.

Pulsing Glow (Critical Notifications)

@keyframes critical-pulse {
0%, 100% {
text-shadow: 0 0 4px alpha(@error_color, 0.4);
}
50% {
text-shadow: 0 0 12px alpha(@error_color, 0.8),
0 0 20px alpha(@error_color, 0.4);
}
}

.notification-popup.urgency-critical .notification-summary {
color: @error_color;
animation: critical-pulse 2s ease-in-out infinite;
}

Slide-In Effect

@keyframes slide-in {
from {
opacity: 0;
transform: translateX(100px);
}
to {
opacity: 1;
transform: translateX(0);
}
}

.notification-popup {
animation: slide-in 0.3s ease-out;
}

Animation Properties

PropertyValuesDescription
animation-namekeyframes nameWhich animation to use
animation-duration2s, 500msHow long one cycle takes
animation-timing-functionease, ease-in, ease-out, linearEasing curve
animation-iteration-count1, infiniteHow many times to run
animation-delay0s, 200msDelay before starting

Compositor Integration

Hyprland Blur

For translucent notifications with blur:

# hyprland.conf
layerrule = blur, histui-notification
layerrule = ignorealpha 0.5, histui-notification

Color Mixing

GTK4 CSS supports color-mix() for solid blended colors:

/* Muted danger background - solid color, not transparent */
.notification-popup.urgency-critical {
background-color: color-mix(in srgb, @error_color 8%, @window_bg_color);
border-color: color-mix(in srgb, @error_color 40%, @borders);
}
FunctionResultUse Case
alpha(@color, 0.1)Semi-transparentCompositor blur
color-mix(in srgb, @color 10%, @base)Solid blendedMuted backgrounds

Complete Theme Example

~/.config/histui/themes/custom/
├── theme.css
├── layout.xml
├── manifest.toml
└── sounds/
├── notify.ogg
└── alert.ogg

layout.xml:

<popup min-width="250" max-width="400" min-height="0" max-height="600">
<header>
<icon size="48" />
<box orientation="vertical">
<summary />
<appname />
</box>
<stack-count />
</header>
<body />
<progress />
<image />
<actions />
</popup>

manifest.toml:

name = "Custom"
description = "Custom theme with sounds and animations"
version = "1.0.0"

[icon]
size = 48

[audio.normal]
path = "sounds/notify.ogg"
volume = 0.7

[audio.critical]
path = "sounds/alert.ogg"
volume = 1.0
repeat_count = 3
repeat_delay = "5s"

theme.css:

* {
--histui-font-family: "Inter", sans-serif;
--histui-font-size: 14px;
}

window {
background-color: transparent;
}

.notification-popup {
background-color: alpha(#1e1e2e, 0.9);
color: #cdd6f4;
border-radius: 12px;
border: 1px solid #45475a;
padding: 12px;
margin: 8px;
font-family: var(--histui-font-family);
animation: fade-in 0.2s ease-out;
}

@keyframes fade-in {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}

.notification-popup.urgency-critical {
background-color: color-mix(in srgb, #f38ba8 10%, #1e1e2e);
border-color: #f38ba8;
}

See Also