DEV Community

Aditya Gaurav
Aditya Gaurav

Posted on Originally published at github.com

Building a Vercel-Style Glass HUD for KDE Plasma 6

Most desktop widgets look like they were designed in 2012.

When I wanted a countdown widget on my Fedora desktop to track milestones, everything in the store was either an unmaintained Plasma 5 port or a flat grey box. I wanted something closer to Vercel's obsidian glass aesthetic: deep translucent cards, crisp tabular numbers, and specular edge lighting that catches your eye without cluttering the screen.

So I spent the last few days building Ticking from scratch in pure QML and Kirigami for KDE Plasma 6.

Here are four practical lessons from taking it from a rough local script to a published widget on the KDE Store.

1. The double frame trap

If you build a custom card in QML with rounded corners, drop shadows, and glass opacity, you will probably notice an ugly grey outline surrounding your widget when you drop it on the desktop.

Plasma 6 wraps every applet in a default system frame (PlasmaCore.Types.DefaultBackground). Because my root item drew its own full-bleed HUD card, Plasma drew a second frame around my frame.

The fix was a single line in main.qml:

Plasmoid.backgroundHints: PlasmaCore.Types.NoBackground
Enter fullscreen mode Exit fullscreen mode

That drops the system container and lets your custom glass HUD float directly over your wallpaper.

2. Why your custom icon turns into a question mark

I drew a custom SVG logo for the widget with a gradient chrono track and placed it in contents/icons/org.adi_il.ticking.svg. It rendered fine inside the applet, but inside KDE's Widget Explorer drawer, it showed up as a giant question mark.

The reason is subtle. The Widget Explorer is a separate system shell process. It asks the system icon loader (KIconLoader) for the icon name listed in metadata.json. Because that custom SVG only existed inside the plasmoid package folder, the system loader could not resolve it.

If you want your widget to look clean in the system drawer on every Linux distribution out of the box, use a standard freedesktop icon name in metadata.json:

{
  "KPlugin": {
    "Icon": "chronometer",
    "Id": "org.adi_il.ticking"
  }
}
Enter fullscreen mode Exit fullscreen mode

Keep your custom SVG inside contents/icons/ for internal views, but give metadata.json a standard fallback like chronometer or preferences-system-time.

3. The progress bar math problem

Ticking includes a percentage bar tracking the journey toward your target date. Initially, the code set the start date to the moment the widget was installed if no start date was configured.

That created an annoying bug for anyone who installed the widget late. If a deadline was two days away and you installed the widget today, your progress showed 0.000% instead of 95%.

To fix this, I added dual graphical date pickers in the settings dialog: one for the Start Date and one for the End Date. Both automatically normalize to midnight (00:00:00 UTC), so users pick the calendar days and get an accurate elapsed percentage without fiddling with UTC timestamps.

4. Adaptive timers save laptop batteries

A live countdown with sub-second milliseconds needs to tick every 40ms (~25 FPS) for smooth animation. But running a 40ms timer continuously while the widget is collapsed in a panel will drain laptop battery for zero benefit.

In main.qml, I bound the timer interval to the widget visibility state:

Timer {
    id: tickerTimer
    interval: {
        var isVisible = (Plasmoid.expanded || Plasmoid.formFactor === PlasmaCore.Types.Planar);
        if (!isVisible) return 1000;
        if (root.stopwatchData.running || Plasmoid.configuration.showMilliseconds) return 40;
        return 1000;
    }
    running: true
    repeat: true
    onTriggered: root.updateAllMetrics()
}
Enter fullscreen mode Exit fullscreen mode

When collapsed in the panel, it throttles to one second. When opened or floating on the desktop, it runs at high precision.


Ticking is open source on GitHub and live on the KDE Store:

Top comments (0)