Skip to main content

KageKit

A declarative UI layer for discord.py Components V2.

Components V2 lets you build anything, which is the problem: every panel ends up re-deciding where the tabs go, which red means "you cannot undo this", and whether a sub-menu should stack a new ephemeral message. KageKit turns those decisions into structure — you describe the panel, and the layout is not yours to get wrong.

from kagekit import Action, ActionBar, Card, Page, Pager, SettingRow, Tab, TabBar, Text

await Page(
    tabs=TabBar(
        tabs=[Tab("Pending", "pending"), Tab("Done", "done")],
        active="pending",
        on_change=self.switch_tab,          # (interaction, value)
    ),
    body=[
        Card(title="🎫 Ticket settings", intent="brand", children=[
            SettingRow("Category", category.name, emoji="📂",
                       action="Change", on_click=self.edit_category),
            SettingRow("Notify", channel.mention, emoji="🔔",
                       action="Change", on_click=self.edit_notify),
        ]),
        Card(title="Delete every ticket", intent="danger", children=[
            Text("This cannot be undone."),
        ]),
    ],
    pager=Pager(current=3, total=12, on_change=self.goto),   # (interaction, page)
    actions=ActionBar(on_reload=self.reload, on_back=self.back),
).edit(interaction)

Install

pip install discord-kagekit

Requires Python 3.11+ and discord.py 2.7+. KageKit imports nothing but discord.py and the standard library.

The layout contract

┌ TabBar ─────────────  outside the container, at the very top
│  [Pending] [Running] [Done]
├ Card ───────────────  Container(accent = intent)
│  ## Heading / setting rows
├ Card ───────────────  a different subject gets its own card
├ Pager ──────────────  ⏮  ◀  [ 3 / 12 ]  ▶  ⏭
└ ActionBar ──────────  🔄 Reload   ◀️ Back   ✖️ Close   outside, at the very bottom
  • Section switches (pending / running / done) change what the page is showing, so they sit above the cards, not inside one.
  • Reload / Back / Close act on the message as a whole, so they sit at the bottom, outside every card.
  • Paging is its own row: the center button reads current / total and opens a modal to jump; the outer buttons step one page and jump to either end. It is separate from the ActionBar because it moves through the contents inside a card.
  • Content that is genuinely different gets its own card, with the accent carrying the intent.
  • Operations edit the same message in place. Stacking a new ephemeral per step is not something the layer offers.

What it guarantees

  • More than five tabs become a select instead of overflowing the five-button row.
  • The active tab renders primary and unclickable; a control with no handler renders disabled rather than as a button that does nothing.
  • A Text after a SettingRow does not leak into that row's Section — a recurring bug when panels are hand-built.
  • The budget is checked before sending: exceeding Discord's 10 top-level slots or 40 components raises LimitError telling you what to cut, instead of an opaque API rejection.

Cards

Child What it is
SettingRow one setting: **{emoji} {label}**: {value} plus a button on the right
Text plain text (write ## yourself for a heading)
Heading a sub-heading; thumbnail= hangs an image off it
Divider a separator line
Actions([Action(...)]) a button row for that card's subject; wraps past five
Select string / role / channel / user / mentionable; always its own row
Control a raw discord.ui.Item, for persistent views the above can't express

Theming

KageKit ships neutral: English labels, no branding, no limits. Everything product-specific is injected.

from kagekit import ComponentBuilder, Labels, Limits, Theme

theme = Theme(
    labels=Labels(back="戻る", close="閉じる", reload="更新"),
    footer="Made with example.com",     # appended as subtext on the last card
)
cb = ComponentBuilder(theme=theme)

Theme.status(enabled) returns the shared 🟢 Enabled / ⚪ Disabled wording, and state_intent(enabled) returns the matching card intent, so state reads the same in text and in colour across every panel.

Palette

Discord's stock colours are highly saturated; four cards in a row become a stack of neon bars. The default palette keeps the hue and drops the saturation to a value that stays legible on both Discord themes.

Token Value Meaning
brand #6F63E0 ordinary panels, the primary action
success #3F9E72 done, enabled
warning #C79141 recoverable warning
danger #C2504F destructive and irreversible
info #4A87A8 informational, in progress
neutral #6E7480 neutral, disabled

DISCORD_CLASSIC keeps the old stock values if you want them back.

The low layer

ComponentBuilder is the method-chaining builder the declarative layer is built on. It is public: reach for it when you need something Page does not model (a persistent custom_id view, a bespoke message), and use render_card(cb, card) to drop a declarative card into a hand-built view.

Limits

Discord caps a message at 10 top-level components and 40 in total, and an action row at 5 buttons. A card that exists only to hold a heading costs about 3 components — worth knowing before you split one panel into six cards.

Contributing

This repository is generated from the bot it lives in — see CONTRIBUTING.md before opening a pull request.

Writing panels with an AI agent? AGENTS.md is the contract in one page.

License

MIT

Metadata

Release files for discord-kagekit 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for discord-kagekit 0.1.0
File Size Uploaded
discord_kagekit-0.1.0.tar.gz 24.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for discord-kagekit 0.1.0
File Interpreter ABI Platform
discord_kagekit-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 48.3 kB

Release files / discord_kagekit-0.1.0.tar.gz

Download URL discord_kagekit-0.1.0.tar.gz
Size 24.6 kB
Tags Source
SHA-256 checksum
How to use checksums
dbc8298d71a93210d466191807aa6b63858f1b075e394a53ff8547b6b2f60897
BLAKE2b-256 checksum
How to use checksums
b89160ae12603ed202416f2a4808f9b10ac9a134e75780b6e432458acc2f65b3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 18, 2026.

Transparency log

Release files / discord_kagekit-0.1.0-py3-none-any.whl

Download URL discord_kagekit-0.1.0-py3-none-any.whl
Size 23.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
75e15e93d73b97622051671a96b7415df96f3f4e560b505f9bb527184726a6c3
BLAKE2b-256 checksum
How to use checksums
fcc9a6957fae434fcbecc1f96a649dbfdccd5d217f77ed2e0723b65814a658b2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 18, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page