After some testing I decided not to use the windows that Electron creates by default. I decided to create my own titlebar, with Windows-style control buttons. There are many tutorial guides on the internet. I was inspired by a Ronnie Dutta project.

Update the dependencies

A little note before starting with this post. I’m not starting from scratch but I still use my el3um4s/memento-svelte-electron-typescript template. At first, as usual, I make sure I have all the dependencies updated to the latest version:

npm run check-updates

Create a window without a titlebar

First I create a window without a titlebar by setting the frame property to false:

Add basic styles

For aesthetic reasons I add some basic styles to the tailwind.pcss file:

The only odd class is overflow-y-hidden. It is used to hide the scrollbar from the Electron window. I’ll be using a custom scrollbar attached to the main section of the page.

When I start the app (with npm run dev) I get a window like this:

What’s the problem? A window without a titlebar has no close buttons and cannot be moved. It’s time to add a custom titlebar.

Add a custom titlebar

Doing some testing the best way seems to be to create a component src/frontend/Componentes/MainWithTitlebar.svelte in which to insert both the titlebar and the main section of the page.

I write the basic code:

Then I add the component to App.svelte:

Obviously this does not cause any visible changes. I need to add some styles to my component:

I set the header height to 32px using Tailwind’s h-8 class and set the underlying page height accordingly:

It is not enough to set a titlebar to be able to move a window. Fortunately, Electron allows you to enable this possibility quite easily. Just add the CSS style -webkit-app-region: drag:

Add window control buttons

Now the window can move. But I also want to be able to close, minimize and zoom it. I need some buttons:

I set the button area as no-drag to make it easier to click on the buttons. And speaking of buttons, in the code above I used some writing but maybe it’s better to use icons. Tailwind allows you to easily use heroicons icons. I take advantage of it and create some Svelte components to display the icons. This, for example, is the IconClose.svelte component:

After creating the icons I insert them in the titlebar:

Add the title to the window

There can be several ways to add the title to a window. A simple way is this:

Customize the scrollbar

Electron shows the Chrome scrollbar by default. I can change its style with some CSS code:

This is the result:

Enable buttons

Nothing happens if I click on the buttons, also because I haven’t added any functions. I resolve immediately:

Obviously the functions must be filled with code. What can I use? I need to use a specific API. I create the src/electron/IPC/windowControl.ts file:

Register the new API on src/electron/preload.ts:

Finally I allow the main Electron window to use the API. I edit the src/electron/index.ts file:

This allows me to go back to the component I’m working on (MainWithTitlebar.svelte) and add the missing functions:

Now I can use the various buttons to minimize, maximize and close the window.

Reset the window size

However, there is an anomalous behaviour. When I maximize the window I would like to replace the maximize icon with another one. And maybe when I click I can restore the original window size.

To achieve this I can take advantage of the <svelte:window> element. By inserting it into my Svelte component I can intercept some events related to the window without leaving the component itself.

Why do I have to do this? Because I haven’t found an easier way to tell when the window is full screen. Then I have to use a trick: I check the size of the window. If the window is at least as big as the screen then I assume it is maximized. Otherwise no.

In Svelte, $: marks a statement as reactive: this greatly simplifies the necessary code.

Now I just have to add the function:

and then:

That’s all. Finally some useful links: