Rebuilding our settings panels — and open-sourcing the part underneath
In v2.5.4 we rebuilt every settings panel on Discord. Here's why the Back button kept ending up in a different place on every panel, how we fixed it for good, and why the layer underneath is now out as an open-source package called KageKit.

|
|
Published: August 19, 2026Hi — Tettu here, the engineer behind BotShade.
In v2.5.4 we rebuilt every settings panel on Discord, across every feature that has one. The release notes say “the layout and button positions are now consistent”, which is true but doesn’t explain how they got inconsistent in the first place. That’s what this post is for.
At the end there’s also the part about the layer underneath, which is now an open-source package.
Table of contents:
- Why buttons drift out of place
- Making the wrong layout unwritable
- What the rebuild turned up
- Extracting it as KageKit
- It’s out
Why buttons drift out of place
The BotShade bot has 19 features, and most of them show a settings panel on Discord. Run /protection settings and you get the protection settings; run /ticket settings and you get the ticket settings.
These were written by the same people in the same repository — and yet the Back button sat in a different place depending on which one you opened. In some panels it lived inside a card; in others, at the very bottom of the message. The way settings were grouped and coloured varied too.
The reason is simple: Discord’s newer component system (Components V2) lets you build anything. And “anything” means that every time you build a panel, you re-decide all of this:
- do the filter tabs go inside a card, or above them?
- which red means “you cannot undo this”?
- does opening a sub-menu stack a new message, or edit the one that’s already there?
Make those calls 19 times, on 19 different days, and they will not match. You can write the answers down as a convention — but a convention still runs when you break it, so on a busy day it gets broken.
Making the wrong layout unwritable
So we turned the convention from prose into structure. We decided where things go, once, and left the caller to declare only the contents.
Concretely, a panel is now expressed in four layers:
- Tabs — “Running / Ended / All” and the like. These change what the page is showing, which is a statement about the page as a whole, so they sit above the cards, outside them.
- Cards — the settings themselves. Genuinely different subjects get their own card, and the card’s colour (green, grey, red) carries its state or its danger.
- Pager — moves through the contents inside a card. It renders as ⏮ ◀ [ 3 / 12 ] ▶ ⏭, and pressing the middle lets you jump to a page directly.
- Action bar — Reload, Back, Close. These act on the whole message, so they go at the very bottom, outside every card.
The important part is that this order isn’t a choice. You don’t tell the layer where the tabs go and where Back goes; you hand it tabs, cards and a Back handler, and the structure places them. Putting them in the wrong place stopped being something you can express.
The other thing that paid off immediately: the layer now checks the budget before sending. Discord caps a message at 40 components, and going over gets you rejected — with an error that doesn’t say what was too much. That used to be the kind of mistake you found by shipping it. Now it stops beforehand and tells you what to cut.
What the rebuild turned up
Moving 19 features across one at a time turned into an audit of the panels we already had. Things we hadn’t noticed surfaced while rearranging them.
- In the welcome message panel, the setting rows leaked into the preview’s container — but only when a body had been configured. It came from drawing the card boundaries by hand, and disappeared the moment the panel became declarative.
- Colours written as raw numbers were scattered everywhere, and the same “success green” had drifted to slightly different values in different places. They now come from one source.
- It was possible to render a button that did nothing when pressed. Buttons with no handler now render visibly disabled instead.
None of these had bug reports against them. They were just quietly wrong — which makes them the most satisfying kind of thing to find in a rebuild.
Extracting it as KageKit
Everything above — tabs, cards, pager, action bar — contains nothing specific to BotShade. Anyone building settings panels with Components V2 runs into exactly the same decisions.
So we split that part out of BotShade into a standalone package called KageKit. The name comes from kage, the Japanese word for “shadow” — keeping the thread from BotShade’s Shade while landing somewhere that doesn’t collide with existing libraries.
While splitting it out, we made sure KageKit contains none of BotShade’s wording, colours, or plan-based limits. Those are injected from the outside, and there’s a check that fails the build if any of them leak back in. BotShade’s brand colour is something BotShade hands to it — KageKit itself has no colours of its own.
Its only dependencies are discord.py and the Python standard library.
It’s out
KageKit is published under the MIT licence.
- Source: github.com/VorEdgeJP/kagekit
- Package: discord-kagekit
pip install discord-kagekit
It runs on Python 3.11+ with discord.py 2.7+. The current version is 0.1.0 and it’s still beta — its production track record is exactly one thing: migrating 19 features for v2.5.4.
Bug reports, and “I wish it had a component for X” requests, are welcome as GitHub issues.
If you use BotShade, this is honestly not a story with much in it for you beyond “the settings panels got nicer to use”. But we built this because we needed it, and if it saves someone else the same trouble, we’d rather it were out there.
Thoughts and feedback are welcome via Support.
See you next time.