INC-2026-04-04: Resolution
Resolution
CSS Changes Applied
Removed all box-shadow declarations (GTK3 renders these in software):
/* REMOVED - multi-line box-shadow from window#waybar */
/* REMOVED - box-shadow from tooltip */
/* REMOVED - box-shadow from #custom-power:hover */
/* REMOVED - box-shadow from @keyframes glow-pulse (replaced with opacity) */
/* REMOVED - box-shadow from @keyframes border-glow-cycle */
Disabled all infinite CSS animations:
/* DISABLED: glow-pulse, text-flicker, subtle-breathe, border-glow-cycle */
/* Conditional animations retained: pulse on .urgent and .critical (only fires on state) */
Disabled all transition: all properties:
/* DISABLED: transition: all 0.2s/0.3s ease on 4 selectors */
Verification
| Sensor | Before | After |
|---|---|---|
x86_pkg_temp |
86°C |
56°C |
acpitz |
81°C |
54°C |
SEN1/SEN2 |
53-54°C |
46-48°C |
waybar CPU |
96.1% |
0.0% |
CPU frequency |
800 MHz (throttled) |
Normal scaling |
-
Waybar at 0.0% CPU after 30 seconds
-
Temperatures within normal range (CPU 56°C idle)
-
Fan noise returned to baseline
-
No CSS parse errors in waybar log
-
All modules functional (workspaces, clock, media, battery, network, etc.)
CLI Reference: Diagnostic Commands Used
This incident was diagnosed entirely from the terminal. The commands below are organized by technique, with explanations of the advanced patterns.
Process Substitution — <(command)
Process substitution creates a temporary file descriptor from a command’s output, allowing commands that expect filenames to read from pipelines instead.
# Merge two command outputs side-by-side with paste
# <(cmd1) creates fd for thermal zone names
# <(cmd2) creates fd for temperatures converted from millidegrees
paste <(cat /sys/class/thermal/thermal_zone*/type) \
<(awk '{printf "%.1f°C\n", $1/1000}' /sys/class/thermal/thermal_zone*/temp)
paste normally takes two files. Process substitution feeds it two "virtual files" — one with zone names, one with converted temperatures. The awk divides millidegree readings by 1000 and formats to one decimal place.
# Ephemeral waybar config via process substitution
# waybar -c expects a file path — <(echo '...') creates one on the fly
waybar -c <(echo '{"layer":"top","modules-center":["clock"],"clock":{"format":"%H:%M"}}') \
-s <(echo '* {font-size:14px; color:#cdd6f4; background:#1e1e2e;}') &disown 2>/dev/null
This runs waybar with an inline config and CSS without creating any temp files. The -c and -s flags both receive /proc/self/fd/NN paths pointing to the echo output. Critical for isolation testing — no files to clean up.
Command Substitution — $(command)
# Kill a process by name — $(pgrep) returns the PID inline
kill $(pgrep -x waybar)
# Use PID in ps — pgrep finds it, xargs passes it to ps
pgrep -x waybar | xargs ps -o pid,pcpu,etime --no-headers -p
Batch-Mode top with awk Filtering
# top -b = batch mode (non-interactive, stdout)
# -n1 = single snapshot
# awk filters to rows 7-12 (skips header, shows top processes)
top -b -n1 | awk 'NR>=7 && NR<=12'
# Filter top output for a specific process
top -b -n1 | awk '/waybar/ {print $1, $9, $10, $12}'
# $1=PID, $9=%CPU, $10=%MEM, $12=COMMAND
Thermal Zone Reading with awk Math
# Linux exposes temps in millidegrees (86000 = 86.0°C)
# awk converts inline with printf formatting
awk '{printf "%.1f°C\n", $1/1000}' /sys/class/thermal/thermal_zone*/temp
# CPU frequency in MHz (kernel reports in kHz)
awk '{printf "%.0f MHz\n", $1/1000}' /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq | sort -rn | head -5
# Power draw in watts (kernel reports in microwatts)
awk '{printf "%.1f W\n", $1/1000000}' /sys/class/power_supply/BAT*/power_now
nvidia-smi Queries
# Full dashboard view
nvidia-smi
# Structured CSV query — specific fields, no headers
nvidia-smi --query-gpu=temperature.gpu,power.draw,fan.speed,utilization.gpu \
--format=csv,noheader
# Per-process GPU monitoring (single snapshot)
nvidia-smi pmon -c 1
Process Hunting
# Top CPU consumers — command ps to bypass aliases
command ps aux --sort=-%cpu | awk 'NR<=15 {printf "%-8s %5s %5s %s\n", $1, $3, $4, $11}'
# Runaway processes (>20% CPU sustained)
command ps aux --sort=-%cpu | awk '$3>20'
# Check if a process is running
pgrep -x waybar && echo "running" || echo "not running"
sysfs Exploration
# CPU governor, max/current frequency
grep . /sys/devices/system/cpu/cpu0/cpufreq/scaling_{governor,max_freq,cur_freq}
# AC power status — awk ternary for human-readable output
cat /sys/class/power_supply/AC*/online | \
awk '{print ($1==1) ? "AC Connected" : "On Battery"}'
# Find fan RPM sensors across all hwmon devices
find /sys/class/hwmon -name 'fan*_input' -exec sh -c \
'echo "$(cat $(dirname {})/name): $(cat {}) RPM"' \;
CSS Forensics with grep
# Count expensive CSS properties
grep -cE 'box-shadow|backdrop-filter|blur|opacity|border-radius|transition|alpha\(' ~/.config/waybar/style.css
# Find specific properties with line numbers
grep -nE 'transition:' ~/.config/waybar/style.css
# Find uncommented animation properties (exclude CSS comments)
grep -n 'animation:' ~/.config/waybar/style.css | grep -v '/\*'
# Find unclosed CSS comments (opened but not closed on same line)
awk '/\/\*/ && !/\*\// {print NR": "$0}' ~/.config/waybar/style.css
sed for CSS Property Invalidation
# Prefix property name with underscore — GTK ignores unknown properties
# Safer than commenting (no nesting issues)
sed -i 's/box-shadow:/_box-shadow:/g' ~/.config/waybar/style.css
Attempting to wrap CSS properties in /* … */ comments with sed is fragile. Multi-line values and nested comments cause parse errors. Prefer the underscore-prefix technique or edit the file directly.
|
Journal & Kernel Log Queries
# Kernel messages filtered for thermal events
journalctl -k --since "1 hour ago" --no-pager | grep -iE 'thermal|throttl|trip'
# dmesg with ISO timestamps, filtered for hardware warnings
dmesg --time-format iso | tail -50 | grep -iE 'thermal|fan|throttl|error|warn'
Background Process Management
# Start waybar in background, detach from terminal
waybar &disown 2>/dev/null
# Chain: start waybar, wait 30s, then check CPU
waybar &disown 2>/dev/null && sleep 30 && top -b -n1 | awk '/waybar/ {print $1, $9, $10, $12}'
# Kill + restart + verify in one pipeline
kill $(pgrep -x waybar) 2>/dev/null && waybar &disown 2>/dev/null
Pattern: Isolation Testing
The most valuable technique from this incident — binary search by swapping components:
# Test 1: Minimal config (is waybar itself the problem?)
waybar -c <(echo '{"layer":"top","modules-center":["clock"],"clock":{"format":"%H:%M"}}') \
-s <(echo '* {font-size:14px;}') &disown 2>/dev/null
# Test 2: Full modules + minimal CSS (modules or CSS?)
waybar -s <(echo '* {font-size:14px; color:#cdd6f4; background:#1e1e2e;}') &disown 2>/dev/null
# Test 3: Full modules + full CSS minus one module (which module?)
sed -i '11s/"custom\/media", //' ~/.config/waybar/config
waybar &disown 2>/dev/null
Each test changes exactly one variable. 30 seconds of runtime with top -b -n1 gives a definitive CPU reading. This methodology identified the CSS as the sole culprit in 3 tests.