All Mods Resource Packs Data Packs Shaders Modpacks Plugins
MenuKit

MenuKit

by trevlar • Downloads: 0 • Followers: 0
Description
Gallery
Changelog
Versions

MenuKit

MenuKit is a UI library for Fabric mods. It has the parts of a Minecraft interface (buttons, sliders, tabs, panels, HUD readouts, and settings controls) and places them on the HUD, on vanilla screens, or on screens a mod opens itself. A mod builds its menus and overlays with it instead of writing vanilla screen code from scratch.

I built it for my own mods, then opened it up so other modders can use it too. If you're a player and another mod asked you to install MenuKit, put it in your mods folder alongside that mod and you're done. On its own it changes very little. Buttons get a pressed look while you hold them down, single chest, barrel, ender chest, and shulker box screens are trimmed to the inventory screen's height so your inventory sits in the same place on all of them, and Controls gets a Move panels key that does nothing until you bind it.

The UI draws on the client. MenuKit also loads on a server, because libraries built on it run server code, but a mod that only draws UI can stay client-only. For slots, there is SlotKit, a separate library built on MenuKit that finds and acts on the slots in any menu and builds real slots and custom menus.

Features

  • One set of components for five contexts, which are the HUD, container screens, other vanilla screens, anchors, and standalone screens. An anchor is a rectangle a mod names and supplies on any screen, such as the pause menu's column of buttons.
  • Buttons, toggles, checkboxes, radio buttons, sliders, dropdowns (single and multi-select), text fields, labels, info boxes, tooltips, icons, item displays (with an optional colored outline), progress bars, dividers, scroll containers, runtime rows (Flow, plain or drawn as cards), collapsible sections, tabs, color fields, item pickers, and confirm and alert dialogs.
  • Every builder uses the same method names (at, size, visibleWhen, disabledWhen, tooltip, state, onClick). Every control that shows a value reads and writes a field the mod owns.
  • Components are grouped into panels. A panel renders, takes input, and shows or hides as a unit, and one disabledWhen grays the whole panel.
  • A panel's placement is one declaration on the panel, and it can differ by game mode, so a panel can sit somewhere else in creative. The panel sizes itself to fit at any GUI scale, wrapping and scrolling as needed.
  • Several mods can add UI to the same screen. Panels that share a region stack in priority order instead of overlapping, in the same order on every launch. Mods that work together can also share one panel, which wraps into a second column past a count the mod sets.
  • What a panel covers is inert. An opaque panel takes every point of its rectangle, and a see-through one takes only its controls. A slot or widget under a panel does not highlight, take a click, or show a tooltip.
  • Controls have vanilla's click sound, narration, and keyboard focus.
  • Each element draws for the kind of screen it is on. Inventories and chests get the Panel look, and Options, Controls, and similar screens get the Default look, which is vanilla's pause-menu look. Text is colored by role (heading, body, muted, or disabled), so it reads on either kind.
  • On container screens, a chest and the boxes opened below it can stack in one scrolling list, above an inventory panel that stays put. Other mods' buttons and recipe viewers keep working around both.
  • Tabs like the creative inventory's can go on every container screen except the creative inventory itself, one per box the player has open if a mod wants, in groups that slide open under a header.
  • A grid of slots scrolls inside its panel when the window is too short for it, and a mod can turn this on for a vanilla chest's own rows.
  • A mod can change what a slot's item draws as, such as a shulker box whose lid opens or a different item in its place, timed with its own easing.
  • A mod can draw marks under or over the item in any container slot, such as a padlock or a faded ghost of what belongs there, with a tooltip of its own. Several mods' marks on one slot all show.
  • When one container screen opens in place of another, the frame moves from the old size and place to the new one instead of jumping.
  • Panels can sit behind everything on a screen, so they never block other mods' buttons.
  • Players can move panels. A Move panels key, unbound by default, lets them drag any panel a mod hasn't fixed in place, and MenuKit remembers where they put it.
  • A color field lets a settings page pick a color, from Minecraft's 16 dye colors or from a full picker with a hex field.
  • An item picker holds one item or a list of them as a setting value. Its dialog lists every item and variant the way the creative inventory does, and edits names, counts, enchantments, and other components. One shared matcher compares stacks, and nothing is ever taken from the player's inventory.
  • A widget made by another library, such as a config library's own control, can sit inside a panel. It scrolls and clips with the panel, and gets clicks, typed keys, focus, and narration.
  • Slots clip to the scroll area they sit in, so a slot half past the edge draws, hovers, and takes clicks only where it shows. Slot libraries report their slots to MenuKit, and SlotKit does.
  • Lists that follow a list the mod owns as it changes, with optional add, remove, reorder, search, and sort controls, and a button that opens a menu of actions for a row.
  • Text fields cancel an edit with Escape, without closing the screen.
  • A cycle strip beside the hotbar is available to any mod that turns a ring of stacks through the held slot. It slides on each turn.
  • A status column on the HUD, between the hearts and the hunger bar, holds small icons from several mods, stacked.
  • Tooltips can be hidden, docked, wrapped, kept on screen, or scrolled through one shared check.

Works with most resource packs

Menus built with MenuKit follow the resource pack you play with, the way Minecraft's own screens do. A panel's frame, background, and slots come from the pack's container art, buttons, toggles, checkboxes, and dropdowns from the pack's own buttons, and text changes color only where the pack's colors would make it hard to read. Mods built on MenuKit get this with no work of their own, and with no pack installed nothing looks different.

  • Dark mode packs, high-resolution packs, and packs that recolor or restyle containers all work. Art too detailed to cut up cleanly, such as a fully painted background, keeps MenuKit's own look.
  • In a container list, each container keeps its own art, so a shulker box below a chest looks the way it does on its own screen. Where a pack hides container titles, the boxes get their names back once there's more than one.
  • With OptiGUI installed, packs that give each shulker box color its own screen apply per box, so a lime box below a chest gets the lime art.
  • Pack authors can theme MenuKit directly with its own sprites: menukit:panel_raised for panels and the menukit:control_raised set for buttons. Resource packs lists them.

Example

A HUD readout, registered once from the client entry point.

HudPanel.builder("mymod:readout", ModeScope.BOTH)
        .region(InsideRegion.CENTER)
        .padding(4)
        .style(PanelStyle.NONE)
        .visibleWhen(() -> Minecraft.getInstance().gui.screen() == null)
        .text(0, 0, () -> "Hello from MenuKit")
        .build();

The text renders directly below the crosshair during play and disappears while any screen is open.

Getting started

Minecraft 26.3 is unobfuscated, so MenuKit goes on the plain implementation configuration.

repositories {
    maven { url 'https://api.modrinth.com/maven' }
}
dependencies {
    implementation 'maven.modrinth:menukit:7.5.0+26.3'
}

Declare it in fabric.mod.json with both bounds, separated by a space (why).

"depends": { "menukit": ">=7.5.0 <8.0.0" }

Players install MenuKit separately. The Trevlar mods list it as a required dependency on their Modrinth pages rather than bundling it.

The sources in the repository build as a project of a parent Gradle workspace that is not in the repository, so they don't build on their own yet.

Requirements

  • Fabric API, which MenuKit uses for its entry points and events.

Compatibility

  • Minecraft 26.3, with Fabric Loader 0.15.0 or newer.
  • Required on the client. Optional on the server, unless a mod built on MenuKit needs it there.
  • Older releases of Inventory Plus, Keybindery, and a few other Trevlar mods built for earlier MenuKit versions are marked incompatible. Update them alongside MenuKit.

Used by

How to use

This guide follows the order a mod usually meets MenuKit, from a first panel to settings screens. Each part links to the full developer docs.

The public API

The API is every type in a package named api (com.trevlar.menukit.api.*), and the version contract covers those. Every other package is internal, marked @ApiStatus.Internal, and can change in any release. A task that seems to need an internal type is a gap in the API, so open an issue for it.

Package Holds
api MK, the HUD facade, which registers HUD panels and notifications, sends a notification, and reads or sets the recipe book. Declarations, the one freeze that MenuKit and every library built on it declare through.
api.element Every element, and the contexts they render and take input in
api.layout, api.dialog Rows and columns placed once, and confirm, alert, and edit dialogs
api.panel Panel, placement (Placement, PanelPosition and the regions) and ModeScope, the adapters that put panels on vanilla screens and around anchors, the container list (ContainerRegion), container tabs (ContainerTabs), screen frames (ScreenFrames), slot item looks and decorations (SlotItemLooks, SlotDecorations), shared panels (SharedPanels), and MKScreen
api.hud HUD panels, notifications, and the cycle strip
api.host For a library that ships its own screens. It holds the host of a screen's own panels, the hook that routes input to slots drawn in panels, and SlotClip, which clips those slots to a scroll area

A first panel on the HUD

HudPanel.builder(id, scope) registers a panel on the game window, as in the example above. The scope is the game mode it shows in: ModeScope.SURVIVAL, CREATIVE, or BOTH. It sits on one of nine InsideRegion spots, 4 pixels in from the edges it touches. .offset(dx, dy) nudges it, and .priority(n) orders it among other panels on the same spot. With .region(OutsideRegion.X) a HUD panel sits on that side of the vanilla hotbar instead. HUD panels draw only and take no input. Register them from the client initializer, since registration closes when the client finishes starting. Getting started walks through it, and Recipes has a HUD bar and an icon beside the hotbar.

Panels and elements on a container screen

A Panel holds an ordered list of elements, and panels don't nest. Every element is built with X.builder(), the settings it needs, and build(), and doesn't change after that. A control that shows a value takes state(get, set) over the mod's own field and stores nothing itself. A ScreenPanelAdapter puts the panel on container screens. With no target it shows on every container screen, and .on(InventoryScreen.class) or .onPlayerInventory() narrows it. Panel ids are global, so prefix each one with the mod id. To show a panel only some of the time, give it a visibleWhen rather than removing its adapter. Concepts lists every element and the shared builder methods.

Placement and regions

A panel declares where it sits with Panel.builder(id).in(scope, placement), once for each game mode it shows in: ModeScope.BOTH, or SURVIVAL and CREATIVE apart, so a panel can sit somewhere else in creative. Placement.screen(position) places it against the screen, Placement.around(anchor, position) around an anchor's box, and Placement.at(anchor, dx, dy) at a fixed offset from the box. In a position, PanelPosition.region(OutsideRegion) places it outside the screen's frame along one edge, screenAnchor(InsideRegion) on one of nine screen spots, center() as an overlay on top, pixel(...) at an origin read every frame, and main() as a standalone screen's centered frame. .priority(n) orders panels that share a region (the default is 100), .gap(n) sets the space before a panel, and .offset(dx, dy) nudges one panel after it is placed. A panel wraps to the width its placement leaves and scrolls when it is taller than its room. The Mode scope, Placement, and Region sections of Concepts have the details.

Other screens and anchors

VanillaScreenPanelAdapter puts a panel on a vanilla screen that isn't a container, such as the pause screen or Options, and needs .on(...) or .onAny(). An Anchor is a rectangle a mod names and supplies per screen with Anchor.of(id, screen -> box), and a panel placed with Placement.around(anchor, position) sits around it by the same region rules as a menu frame, put on screen by an AnchorPanelAdapter. A null box means the anchor isn't on that screen. Adapters and anchors are declared at init and can't be added or removed during play. Recipes anchors a row under the pause menu's buttons.

What a panel covers

A shown panel claims the points it covers, so nothing behind it highlights, takes a click, or shows a tooltip. Panels are opaque by default and claim their whole rectangle. .opaque(false) makes a panel see-through, and then only its buttons and other controls block what is behind them. .popover() draws a panel above every ordinary panel on the screen, and a modal dialog still opens above that. The Claims section of Concepts covers the order.

Containers above the inventory

ContainerRegion.section(id, scope) adds a section to a container screen's container list, which stacks the chest's own grid and any boxes opened below it in one scrolling column above the player's inventory. The slots are the menu's own, and the screen's bounds are the rectangle around both panels, so other mods' buttons and recipe viewers go around them. A section's source(...), the placed container or item it shows, gives it that container's art under a resource pack, and its name. ContainerRegion.CONTAINERS, ContainerRegion.INVENTORY, and ContainerRegion.anchor(sectionId) are anchors for a button by the list, by the inventory, or by one box. The Container region section of Concepts covers it, and Recipes builds one.

For boxes the player opens while the screen is up, ContainerRegion.sections(kind, scope, keys) declares a kind of section with one section per key, read every frame. ContainerRegion.fitted(scope, screens) makes the list and the inventory panel one frame, as tall as the shown sections need, and with tabs and a visibleWhen on each section a mod can show one container at a time, the way the creative inventory shows one page. A hidden section's slots are off on the client, and onHiddenLanding(...) runs when a shift-click lands an item in one. See Show one container at a time under tabs.

Tabs on container screens

ContainerTabs.tab(id, scope) puts a tab like the creative inventory's on an edge of a container screen: above or below the container list, or above or below the inventory panel. Every mod's tabs on one edge share one row, ordered by order. MenuKit draws the row, makes room for it, and pages it with arrows when it overflows. Everything else is the mod's, read every frame: onClick, the icon (an item, a sprite, or the mod's own drawing with icon(Icon)), tooltip, visibleWhen, selectedWhen, and disabledWhen. MenuKit keeps no selection. Where the container region isn't up, the top and bottom rows stand on the screen's own frame. ContainerTabs.keyed(id, scope, keys) makes one tab per key, and a keyed list with a header(...) is a group whose sub-tabs slide out from under the header while expandedWhen(...) holds. ContainerTabs.groups(id, scope, groups, keys) declares groups that come and go at runtime, one per group key, each with its own header and sub-tabs, in the order the mod gives. A group whose key is one of another group's keys nests inside it. ContainerTabs.frameStaysWhen(scope, screens, when) keeps the screen's frame at vanilla's place while tab rows come and go, moving it only when a row wouldn't fit. See Container tabs.

A grid of slots that scrolls

SlotScroll.builder() makes a grid of slots that is as tall as its live rows while there is room and scrolls once there isn't, never under three rows. The cells are a slot library's elements, one per slot, made from each cell's index and place with cells(count, cell). narrows() drops a column while it scrolls so the scrollbar fits in the panel's width, for a bundle. For a vanilla screen's own grid, ScreenFrames.scrollsWhenShort(scope, screens) scrolls its rows inside a frame that fits the window, and leaves the screen exactly as vanilla draws it whenever it fits. See Scroll a slot grid when it doesn't fit.

What a slot's item draws as

SlotItemLooks.declare(id, look) lets a mod say, per slot and per frame, which ItemAnimation a slot's item draws with and how far through it is. The count, durability bar, highlight, tooltip, and clicks stay vanilla's. ItemAnimation.OPEN_LID opens a shulker box's or chest's lid in the resource pack's model, posed(...) scales and moves the item as it plays, and Transition.of(millis, easing) times it per key. A look can also draw another stack in the item's place. See Slot item looks.

SlotDecorations.under(id, decoration) and SlotDecorations.over(id, decoration) draw on every container slot, empty or not, without a mixin of the mod's own. A slot draws its background, then the under decorations, then the item and its count, then the over decorations, so a ghost of the item that belongs in an empty slot goes under and a padlock goes over. Every mod's decorations draw, in id order. A decoration can answer a tooltip for the part of the slot under the mouse, in place of the item's own. See Slot decorations.

Panels behind everything

Panel.Builder.background() makes a panel part of the screen's own background. Everything else on the screen, other mods' buttons included, draws over it and stays clickable, and the panel gets a click or the wheel only after everything else declined it. The container list's two panels are built this way. See the background panel recipe.

Shared panels and moving panels

Mods that work together can put their elements in one panel. Each joins it from its client initializer with SharedPanels.join(id, contribution) under an agreed id, and MenuKit builds one panel from every contribution, in order. Contribution.Builder.wrapAfter(scope, n) caps how many contributions the shared panel holds in a row, or in a column for a vertical panel, for a game mode, and the rest wrap. With one mod installed it is an ordinary panel. Players can move panels once they bind MenuKit's Move panels key: they drag a panel where they want it, for both game modes or only the current one, and MenuKit keeps the position by panel id. A mod marks a panel fixed() where its place is part of what it means. The Shared panels and Moving panels sections of Concepts cover both.

Standalone screens and screen kinds

A mod's own screen subclasses MKScreen. Elements draw for the kind of screen they are on. ScreenKind.PANEL is the Panel look, MenuKit's gray, and ScreenKind.DEFAULT is the Default look, vanilla's pause-menu look. Panels injected into vanilla screens take the kind from the screen. A standalone screen declares its kind once, with super(title, ScreenKind.DEFAULT, panels), and one that declares nothing is a Panel screen. Text takes its color from a role (TextRole.HEADING, BODY, MUTED, or DISABLED), so it reads on both kinds. Recipes builds a whole settings screen in the pause-menu look.

Layout, tabs, and sections

Row and Column place elements once, at the panel's first layout pass. Flow is the runtime row, and Flow.spacer() pins a control to the right edge of a settings row at any width. Flow.Builder.wrapAfter(n) caps how many children a column or row holds before it wraps, and keepLast() keeps the last ones in a vertical Flow's first column. Flow.builder().boxed() draws a card behind a row, so a list of them lines up like vanilla's world list. Tabs has three modes, WRAP, SIDE_SCROLL, and SIDEBAR, and the selected tab is the mod's own state. tabColumn(false) on a SIDEBAR menu hides the tab column so a one-page menu looks like its tabbed siblings, and fitShownTab() sizes a Tabs to the tab it shows. Section is a collapsible group. Other mods can add tabs to a named menu with Tabs.addTo(id, tab). The Tabs, Section, and Layout helpers sections of Concepts cover them.

Color settings

ColorField shows a swatch and opens a dialog when clicked, with the 16 dye colors on a Basic tab and a full picker with a hex field on a Custom tab. The value is an int, 0xRRGGBB, or 0xAARRGGBB with alpha(true). Changes are written at once, Done keeps them, and Cancel or Escape puts back the color from before. On the HUD there is no dialog, only the swatch. See the color setting recipe.

Item settings

ItemPicker takes one lens. state holds one item by kind, stack one stack, stacks a list of stacks, and rule and rules stacks that each carry their own match mode. match(...) sets how stacks compare, by the kind of item only (ITEM, the default), by the exact stack (EXACT), or by the item plus at least the components the rule sets (CONTAINS). The dialog has a Search tab and, for EXACT and CONTAINS, an Edit tab for components in /give syntax. In play, rule.matches(stack) or ItemMatch.matches(...) does the comparing, and ItemMatch.CODEC saves a rule. ItemMatch is common code, so it works on a server too. See the item setting recipe.

Widgets from other libraries

WidgetHost.builder().widget(supplier).size(w, h) puts any vanilla-style widget in a panel. The host moves it with the panel and its scroll, clips it to its box, and passes it clicks, drags, the wheel, typed keys, focus, and narration. A widget that isn't a vanilla LayoutElement needs a place(...) hook, and one that opens a list below itself needs overflow() to draw it over everything. See the widget host recipe.

Tooltips

MKTooltip.hideWhen(...) hides every tooltip, or only item tooltips or only the others, while a condition holds. wrapWhen, clampWhen, and avoidCoverWhen keep tooltips on screen and out of the way, and onWheel with scrollBy lets a mod scroll a long one. All of them are off unless a mod registers them from client init. The Claims section of Concepts explains each switch.

The cycle strip

HudCycleStrip draws a small hotbar just right of the vanilla one, showing a ring of stacks that turns through the held slot. A mod writes a source that returns the ring for a hotbar slot, registers it once with HudCycleStrip.register(...), and calls slide after the ring turns. Cycle strip has the full guide.

Slots

MenuKit keeps no slot code. A mod that needs slot groups, created slots, or custom menus depends on SlotKit as well. A slot library that places slots in panels reports each one with SlotClip.shown, so the slot clips to its scroll area. SlotKit's slot elements already do this.

Limits and versioning

Limits lists what MenuKit doesn't do and the open gaps. Versioning explains what a version number promises and why both bounds matter. The reference is the generated javadoc for the api packages. Every Java sample in the README and in docs/ is compiled against the release before it ships.

Upgrading

To 7.5.0

Mods built for 7.4.0 work as before, with nothing to change. A mod that uses anything new in 7.5.0 declares "menukit": ">=7.5.0 <8.0.0". Upgrading.

To 7.4.0

Mods built for 7.3.0 work as before, with two exceptions. A switch over OutsideRegion needs cases for the new LEFT_CENTER and RIGHT_CENTER, and a custom element that draws past its own box isn't drawn while that box is wholly out of view. The forms that don't say which game mode a panel is for, such as Panel.Builder.position(p) (now in(ModeScope.BOTH, Placement.screen(p))), are deprecated and go in 8.0.0. A resource pack that themed MenuKit's buttons through menukit:panel_raised themes them through menukit:control_raised now. A mod that uses anything new in 7.4.0 declares "menukit": ">=7.4.0 <8.0.0". Upgrading has every replacement and both checks.

Older releases, from 7.3.0 back to 3.0.0, are in Upgrading from older versions.

License

MIT. See LICENSE. Report issues at github.com/trevorschoeny/menukit/issues.

How to Install MenuKit (Mod)

MenuKit by trevlar is verified for Fabric across Minecraft versions 1.21, 1.21.1, 1.21.10, 1.21.11, 1.21.2, 1.21.3, 1.21.4, 1.21.5 (latest release: 7.5.1+26.3, file size: 997 KB). Follow the steps below to install it safely from ModCraft.tv:

  1. Click the Download button above and select your Minecraft version (1.21, 1.21.1, 1.21.10, 1.21.11, 1.21.2, 1.21.3, 1.21.4, 1.21.5) and platform (Fabric).
  2. Make sure you have the compatible loader or environment installed for Fabric.
  3. Move the downloaded MenuKit file into your Minecraft .minecraft/mods folder and launch the game.

All Versions

+ Add Version
Name Game version Platforms Published Downloads Actions
26.3
Fabric
4d ago 1
26.3
Fabric
9d ago 1
26.3
Fabric
12d ago 1
26.3
Fabric
16d ago 1
26.3
Fabric
20d ago 1
26.3
Fabric
23d ago 1
26.3
Fabric
24d ago 1
26.3
Fabric
25d ago 1
26.3
Fabric
26d ago 1
26.2
Fabric
26d ago 1
26.2
Fabric
1mo ago 1
26.2
Fabric
1mo ago 1
26.2
Fabric
1mo ago 1
26.2
Fabric
2mo ago 1
26.2
Fabric
2mo ago 1
26.2
Fabric
2mo ago 1
26.2
Fabric
2mo ago 1
1.21.11
Fabric
4mo ago 1
1.21.11
Fabric
6mo ago 1
1.21.11
Fabric
7mo ago 1
1.21.x
Fabric
7mo ago 1
## 7.4.0

This release is additions: the container region, background panels, Panel-look panels that follow resource packs, a placement per game mode, panels several mods share, and panels players can move. Nothing was removed, and mods built for 7.3.0 work as before except in the two cases [Upgrading](https://github.com/trevorschoeny/menukit/blob/main/docs/upgrading-7.md#two-things-to-check) names. The forms with no game mode are deprecated. A resource pack that themed MenuKit's buttons through menukit:panel_raised themes them through menukit:control_raised now.

### Added

  • One scrolling column above the inventory on container screens. ContainerRegion.section(id, scope) adds a section to the screen's container region: the chest's own grid, a box opened below it, another below that, stacked in one column above the player's inventory. There is one region per screen, however many mods add to it. The screen becomes two panels, each with its own frame: the container list, with its scrollbar inside its frame, and the inventory panel below it, which never scrolls. The list grows before it scrolls. While the two panels fit, they stay centered on the screen as one block, the list's top moving up and the inventory panel down by half the growth each (on a window too short for vanilla's own margins, the side with less room holds until the block is centered). Only once they reach the screen's top and bottom margins does the list scroll, by pixels, with the shared scrollbar. A resize lays it out again, and the list is never shorter than the screen's own container part. The screen's leftPos, topPos, imageWidth and imageHeight are the rectangle around both panels whenever the layout changes, so panels on the menu frame, other mods' buttons and recipe viewers go around both, and a click in the gap between them with an item held drops nothing; vanilla's inventory slots and label go with the inventory panel, and a mod reading leftPos + slot.x lands on the slot. ContainerRegion.CONTAINERS and ContainerRegion.INVENTORY are anchors for each panel. ContainerRegion.anchor(sectionId) is an anchor for one section, so a button can sit by each box in the list and hides when its section is hidden or scrolled out of view. The wheel scrolls the region from anywhere on the screen, but only on a turn nothing else used, so over a slot a mod that moves items with the wheel keeps it, and so does a recipe viewer's list. A shift-click into one of its slots scrolled out of view scrolls there, and carrying an item to its top or bottom edge scrolls it, faster nearer the edge. Both panels are the screen's background. Everything else on the screen, other mods' buttons and recipe viewers included, draws over them and over the region's slots, and they block none of it. While the region is up, the screen's background runs as usual, other mods' hooks in it included. Only vanilla's own container texture and the screen's title are left out, and the panels and the sectio
...
## 7.4.0

This release is additions: the container region, background panels, Panel-look panels that follow resource packs, a placement per game mode, panels several mods share, and panels players can move. Nothing was removed, and mods built for 7.3.0 work as before except in the two cases [Upgrading](https://github.com/trevorschoeny/menukit/blob/main/docs/upgrading-7.md#two-things-to-check) names. The forms with no game mode are deprecated. A resource pack that themed MenuKit's buttons through menukit:panel_raised themes them through menukit:control_raised now.

### Added

  • One scrolling column above the inventory on container screens. ContainerRegion.section(id, scope) adds a section to the screen's container region: the chest's own grid, a box opened below it, another below that, stacked in one column above the player's inventory. There is one region per screen, however many mods add to it. The screen becomes two panels, each with its own frame: the container list, with its scrollbar inside its frame, and the inventory panel below it, which never scrolls. The list grows before it scrolls. While the two panels fit, they stay centered on the screen as one block, the list's top moving up and the inventory panel down by half the growth each (on a window too short for vanilla's own margins, the side with less room holds until the block is centered). Only once they reach the screen's top and bottom margins does the list scroll, by pixels, with the shared scrollbar. A resize lays it out again, and the list is never shorter than the screen's own container part. The screen's leftPos, topPos, imageWidth and imageHeight are the rectangle around both panels whenever the layout changes, so panels on the menu frame, other mods' buttons and recipe viewers go around both, and a click in the gap between them with an item held drops nothing; vanilla's inventory slots and label go with the inventory panel, and a mod reading leftPos + slot.x lands on the slot. ContainerRegion.CONTAINERS and ContainerRegion.INVENTORY are anchors for each panel. ContainerRegion.anchor(sectionId) is an anchor for one section, so a button can sit by each box in the list and hides when its section is hidden or scrolled out of view. The wheel scrolls the region from anywhere on the screen, but only on a turn nothing else used, so over a slot a mod that moves items with the wheel keeps it, and so does a recipe viewer's list. A shift-click into one of its slots scrolled out of view scrolls there, and carrying an item to its top or bottom edge scrolls it, faster nearer the edge. Both panels are the screen's background. Everything else on the screen, other mods' buttons and recipe viewers included, draws over them and over the region's slots, and they block none of it. While the region is up, the screen's background runs as usual, other mods' hooks in it included. Only vanilla's own container texture and the screen's title are left out, and the panels and the sectio
...
## 7.4.0

This release is additions: the container region, background panels, Panel-look panels that follow resource packs, a placement per game mode, panels several mods share, and panels players can move. Nothing was removed, and mods built for 7.3.0 work as before except in the two cases [Upgrading](https://github.com/trevorschoeny/menukit/blob/main/docs/upgrading-7.md#two-things-to-check) names. The forms with no game mode are deprecated. A resource pack that themed MenuKit's buttons through menukit:panel_raised themes them through menukit:control_raised now.

### Added

  • One scrolling column above the inventory on container screens. ContainerRegion.section(id, scope) adds a section to the screen's container region: the chest's own grid, a box opened below it, another below that, stacked in one column above the player's inventory. There is one region per screen, however many mods add to it. The screen becomes two panels, each with its own frame: the container list, with its scrollbar inside its frame, and the inventory panel below it, which never scrolls. The list grows before it scrolls. While the two panels fit, they stay centered on the screen as one block, the list's top moving up and the inventory panel down by half the growth each (on a window too short for vanilla's own margins, the side with less room holds until the block is centered). Only once they reach the screen's top and bottom margins does the list scroll, by pixels, with the shared scrollbar. A resize lays it out again, and the list is never shorter than the screen's own container part. The screen's leftPos, topPos, imageWidth and imageHeight are the rectangle around both panels whenever the layout changes, so panels on the menu frame, other mods' buttons and recipe viewers go around both, and a click in the gap between them with an item held drops nothing; vanilla's inventory slots and label go with the inventory panel, and a mod reading leftPos + slot.x lands on the slot. ContainerRegion.CONTAINERS and ContainerRegion.INVENTORY are anchors for each panel. ContainerRegion.anchor(sectionId) is an anchor for one section, so a button can sit by each box in the list and hides when its section is hidden or scrolled out of view. The wheel scrolls the region from anywhere on the screen, but only on a turn nothing else used, so over a slot a mod that moves items with the wheel keeps it, and so does a recipe viewer's list. A shift-click into one of its slots scrolled out of view scrolls there, and carrying an item to its top or bottom edge scrolls it, faster nearer the edge. Both panels are the screen's background. Everything else on the screen, other mods' buttons and recipe viewers included, draws over them and over the region's slots, and they block none of it. While the region is up, the screen's background runs as usual, other mods' hooks in it included. Only vanilla's own container texture and the screen's title are left out, and the panels and the sectio
...
## 7.3.0

This release is additions, new names for the two looks, and one change to when slots shown in a panel are drawn. Nothing was removed, and mods built for 7.2.0 work as before.

### Added

  • Lists that change while the screen is open. ListView shows one row per item of a list your mod owns and follows it every frame: a new item gets its row at once, a removed one loses it, and every other row stays as it is, so a rename in progress keeps its text and the keyboard while the list refreshes. Rows are kept by a key you give, in your list's order. An optional selection, held by your mod, is set by clicking a row and moved with the arrow keys, and a row your code selects scrolls into view. A list in the middle of a page pushes what is below it down as rows are added or grow, and back up as they go, in the same frame: place what follows it as if it took no room, as after a closed Section. [Recipe](https://github.com/trevorschoeny/menukit/blob/main/docs/recipes.md#a-list-that-changes-while-the-screen-is-open).
  • Editing a list. ListView takes optional builder calls, and a list that uses none of them is unchanged. add(label, action) puts an add button per kind in the list's toolbar, and the new row is scrolled into view; add(label, tooltip, action) gives the button a hover tooltip saying what the kind is, fixed or read while hovered, and remove(label, tooltip, ...) does the same for the remove button. remove(...) adds a "- Remove item" button and move(...) up and down arrows, both acting on the selected row and gray where they can't act; pinned(...) rows can't be removed or moved, and confirmRemove(...) asks first, naming the item. After a remove the next row is selected, and Alt+Up and Alt+Down move the selected row. search(...) adds a search field and sort(...) a sort picker with a direction button. controlsInRows() puts up, down and a remove cross in each row instead, for short lists like a config list of text fields. addDisabledWhen(...) grays the add buttons and removeDisabledWhen(...) the remove, the toolbar's and each row's cross, for a list at its longest or shortest. disabledReason(...) explains a grayed list. The toolbar is list.toolbar(), placed above the scroll area so it stays put; it is as wide as its container gives it, so its right end is the scroll area's outer edge, while a toolbar the list draws at its own top lines up with the rows. title(...) puts the list's heading on the toolbar's line, at its left edge, so the heading and the buttons share one line; read every frame when given a supplier, so it can show a live count, and cut with "..." before the buttons. title(title, tooltip) gives the heading a hover tooltip, such as a config option's description, fixed or read while hovered; it shows instead of the whole heading when the heading is cut. The selected row is now a lighter card in the Panel look, and keeps its outline in the Default look. [Recipes](https://github.com/trevorschoeny/menuk
...
## 7.2.0

This release is additions, plus one change for mods that subclass ItemPicker. Nothing was removed, and other mods built for 7.1.0 work as before. An item picker a mod already has opens the new item dialog with no change on its side.

### Added

  • A color setting. ColorField shows a swatch and opens a dialog when clicked. The first tab holds Minecraft's 16 dye colors in rainbow order, shown by their dye items and named by color. The second is a full picker with a saturation and brightness square, a hue bar, a transparency bar when the value has alpha, and a hex field that only takes valid hex. Changes show at once, and Cancel puts back the color from before. The value is an int, 0xRRGGBB, or 0xAARRGGBB with alpha(true). The swatch and its dialog match the screen they are on, inventory or options. [Recipe](https://github.com/trevorschoeny/menukit/blob/main/docs/recipes.md#a-colour-setting).
  • Item settings that hold whole stacks, and lists of them. ItemPicker gains stack, stacks, rule, and rules beside its 6.1.0 state for one item. match(...) sets how stacks compare. ITEM compares the kind of item only, EXACT the exact stack, and CONTAINS the item plus at least the components the rule sets. So an empty rule means a plain item for EXACT and any variant for CONTAINS. playerChoosesMatch() lets the player pick Any or Exactly for each slot. ItemMatch is the one shared matcher. It compares stacks the way vanilla's item predicates do, so no mod writes its own, and ItemMatch.CODEC saves a rule. A list shows as a wrapping row of slots, with an empty one at the end to add to. [Recipe](https://github.com/trevorschoeny/menukit/blob/main/docs/recipes.md#an-item-setting).
  • A new item dialog. Clicking an item picker opens a dialog over the screen, in that screen's look, instead of a separate chooser screen. The setting's slots sit at the top.
  • The Search tab takes typing at once. It searches every item in the creative inventory's search tab, with each potion, enchanted book level, and other variant as its own entry. Clicking an item puts it in the selected slot, which stays selected.
  • The Edit tab edits the selected slot's name, count, and enchantments, and any other component as text in /give syntax, checked by vanilla's own parser with its error messages. Enchantments already on the item are listed under Added, each with its level, - and + buttons (from I to its highest level), and a remove button. Under Not Added are the ones that fit the item, each with an add button. One that conflicts with an enchantment already on the item is grayed and names the conflict.
  • Each filled slot, in the dialog and on the page, has a small remove button in its corner. In a list, the items after it move up.
  • The dialog is as wide and as tall as the tab it shows, so the whole grid is always visible. Cancel puts back the value from before. The dialog never touches the player's inventory.
  • A home for widgets made
...
## 7.1.0

Additions only. Nothing was removed, and mods built for 7.0.0 work as before.

### Added

  • The cycle strip. A small hotbar drawn just right of the vanilla one shows a ring of stacks turning through the held slot, and slides one cell each time the ring turns. Any mod that rotates stacks into the hand can register a source with HudCycleStrip, so its strip shows without any other mod installed. A ring can show as a row, as a column rising above the hotbar, or as both in a cross that shares the held cell. [The guide](https://github.com/trevorschoeny/menukit/blob/main/docs/cycle-strip.md) has the details.
  • HUD panels beside the hotbar. HudPanel.builder(...).region(OutsideRegion.X) places a HUD panel around the vanilla hotbar. LEFT_ALIGN_BOTTOM, for example, is a column of icons just left of it, rising from its bottom. Panels in one region stack 2 pixels apart in priority order, and a hidden one takes no room, so icons from several mods line up beside the hotbar without overlapping or leaving gaps. While vanilla draws the offhand slot beside the hotbar, the regions on that side start beyond it, so an icon never covers it. [Recipe](https://github.com/trevorschoeny/menukit/blob/main/docs/recipes.md#put-an-icon-beside-the-hotbar).
  • Item tooltips and other tooltips can be hidden separately. MKTooltip.hideWhen also takes a predicate of MKTooltip.Kind, asked once per tooltip with ITEM or OTHER, so a mod can offer "hide item tooltips" and "hide all other tooltips" as two settings. Tooltips for items in slots, the creative menu, the recipe book and chat, and items other mods draw through vanilla's item tooltip, are ITEM. The existing hideWhen(() -> ...) still hides both. [Recipe](https://github.com/trevorschoeny/menukit/blob/main/docs/recipes.md#hide-item-tooltips-separately).
  • A gap per panel. PanelPosition.region(...) and screenAnchor(...) take .gap(n), the space in pixels before that panel: from the frame, anchor box or hotbar, and from the sibling before it. .gap(0) lets panels sit flush, so slot panels stacked on a slot group keep vanilla's 18 pixel pitch. Panels that don't call it stay where they were (2 pixels in a region, the host's spacing on a screen spot). HudPanel.builder(...).gap(n) sets it on a HUD panel. A negative gap throws when the position is built.
  • Tab icons. Tabs.tab(id).icon(texture, textureSize, drawSize) draws a picture left of a tab's label, in every mode. The texture is any standalone square texture, such as a mod's icon loaded at runtime, and is read every frame. icon(sprite, drawSize) uses a GUI sprite instead. The tab widens to fit the icon, and an options screen's underline stays under the label. Tabs without an icon look as before. [Concepts](https://github.com/trevorschoeny/menukit/blob/main/docs/concepts.md#tabs).
  • Icons from a texture. Icon.builder().texture(id, textureSize) shows a standalone square texture, drawn whole at the icon's size, alongside the existing sprite(...)
...
## 7.0.0

MenuKit is a UI library only now. Everything about slots moved to SlotKit, a separate library built on MenuKit, and what 6.x deprecated is gone. Mods built for MenuKit 6 need an update. [Upgrading to 7.0.0](https://github.com/trevorschoeny/menukit/blob/main/docs/upgrading-7.md) has the steps, and tells you whether your mod needs SlotKit.

### Moved to SlotKit

  • Slot groups, categories, sets and Storage (api.slot), the window, addresses, slot operations, gates and vetoes (api.window), and SlotGroupPanelAdapter. SlotKit keeps their saved text, so choices a mod saved stay valid. A mod that uses any of them adds SlotKit and changes those imports.
  • MenuKit no longer patches slot clicks, drops, hoppers, dispensers or the inventory. SlotKit does.

### Removed

  • ControlStyle and its helpers, and style(ControlStyle) on Button, Dropdown and DropdownMulti. They did nothing since 6.1.0. Delete the call.
  • Tabs.SIDEBAR_SCROLLBAR_WIDTH. Use ScrollContainer.TRACK_WIDTH on a container screen, 6 on an options screen.
  • MKTooltip.MIN_WRAP_WIDTH. Nothing read it.
  • The window's panel and element handles and the VISIBILITY and OPACITY keys. A panel's own visibleWhen and opaque do the job.

### Changed

  • Declarations moved to com.trevlar.menukit.api.Declarations. Its requireOpen is public, so a library built on MenuKit freezes its own declarations with MenuKit's.

### Added

  • Anchor and AnchorPanelAdapter place a panel around a rectangle a mod names and supplies on any screen, such as the pause menu's buttons. Panels on one anchor stack in its regions.
  • api.host, for a library that ships its own screens. OwnHost lays out a screen's own panels and joins them to MenuKit's claims and draw order, and SlotScreenHook routes hover and clicks to slots drawn in panels.

## 6.2.0

Tooltips dock at the bottom or top of the screen instead of choosing a side of the cursor.

### Changed

  • MKTooltip.avoidCoverWhen(BooleanSupplier) now docks a tooltip. While it holds, every tooltip vanilla would place beside the cursor (vanilla's items, MenuKit elements, other mods) sits centred at the bottom of the screen, MKTooltip.EDGE (4 pixels) above the edge. When the cursor is within MKTooltip.DOCK_MARGIN (20 pixels, a bit more than a slot) of where that tooltip would sit, not only directly over it, it goes to the top centre instead. Nothing wraps or changes side any more, and what is on the screen no longer matters. clampWhen, wrapWhen, scrollBy and hideWhen work on a docked tooltip as before, with the title still first, and a tooltip taller than the screen behaves as it does under clampWhen. A tooltip vanilla places somewhere fixed, such as one anchored to a widget for the keyboard, stays there. While no registered condition holds, tooltips are placed exactly as vanilla places them. The method keeps its name and signature, so a mod that registers it needs no change.

### Deprecated

  • MKTooltip.MIN_WRAP_WIDTH. Nothing reads it now that avoidCoverWhen no longer wraps. It goes in 7.0.0.

Now runs on Minecraft 26.3.

## 6.1.0

Screen kinds and the pause-menu look. Every element now draws for the kind of screen it is on: MenuKit's grey look on container screens (inventories, chests), and vanilla's pause-menu look on options screens (Options, Video Settings, Controls, Create World). A settings page can let the player pick items with the new item picker. A column of buttons can wrap into more columns where the screen's frame ends, and a panel can pop up over every other panel on the screen.

### Added

  • ScreenKind (CONTAINER, OPTIONS) and one rule for where it comes from, ScreenKind.of(screen). A vanilla AbstractContainerScreen is a container screen, and any other vanilla screen is an options screen. A standalone MKScreen is the kind it declares when it is made (super(title, ScreenKind.OPTIONS, panels), read back with screenKind()), and one that declares none is a container screen, as before. Slot-group panels and the HUD are container. A dialog, or any panel on a screen, takes that screen's kind. The kind travels in RenderContext.kind(), so every element follows it, inside sections, scroll areas, tabs and rows, and inside elements built from other elements (Keybindery's key button).
  • Text colour by role: TextRole (HEADING, BODY, MUTED, DISABLED), TextLabel.Builder.role(...), and ScreenKind.textColor(role) and textShadow(). Text is dark grey on a container screen, and vanilla's white and greys with a shadow on an options screen. A label with no role is BODY, which on a container screen is the colour it already was. Checkbox, radio and section header labels follow the same table. ElementConstants.TEXT_MUTED names MenuKit's muted grey.
  • On an options screen, the controls use vanilla's sprites. Button and the dropdowns are vanilla's buttons, Checkbox is vanilla's checkbox, and Toggle is a vanilla button whose text is its state (its label, or vanilla's ON and OFF). A Section header uses the Controls screen's category colours, but reads from the left, in line with its content, rather than centred as vanilla's is. A Divider is vanilla's separator. A panel's RAISED background is vanilla's popup frame, so a dialog looks like one of vanilla's own.
  • ScrollContainer on an options screen is the band every Options screen has: vanilla's darker list background, a separator above and below, and vanilla's thin list scroller. Its content is inset a little wider than vanilla's lists, ScrollContainer.OPTIONS_INSET_X (14) from the left with the scrollbar lane on the right, and OPTIONS_INSET_Y (10) above the first child and below the last, scrolling with the content. Size content for it with the new ScrollContainer.viewportWidthFor(width, kind). A panel that auto-scrolls on an options screen lays its elements out inside the inset itself.
  • Tabs in WRAP and SIDE_SCROLL on an options screen is Create World's tab bar: white labels, the selected one underlined and opening into a page of the list background, and the separ
...
## 6.0.0

One of everything. MenuKit had grown two engines, two slot-group identities, five copies of panel placement and two ways to build every element. This release keeps one of each and names it once. It adds little and breaks a lot, one time: every mod that uses MenuKit migrates. The README's "To 6.0.0" section lists the changes most mods meet.

### Breaking: the public API is the api packages

The public API is com.trevlar.menukit.api.element, .layout, .dialog, .panel, .hud, .slot and .window. Every other package is internal and outside the version contract, which versioning.md now states. Every import changes.

### Breaking: renames

| 5.x | 6.0.0 |
|---|---|
| MKHudPanel, MKHudNotification, MKHudIcon, MKHudSlot | HudPanel, HudNotification, HudIcon, HudSlot |
| MKFocus, MKText | Focus, Text |
| showWhen(...) on a panel, a HUD panel or a container panel | visibleWhen(...), the same word as on every element. Panel.Builder takes it too. |
| getId() | id(): Panel.id(), HudNotification.id(), and an element's declId() |
| WORLD_PICKUP | INVENTORY_INSERT, named for what it gates. Its id is kept. |
| SlotGroupId.Vanilla | SlotGroupId.Category, with isVanilla() |

MKScreen and MKTooltip keep the prefix because the plain names are vanilla's.

### Breaking: declarations freeze

Once every mod has initialised, a late define, veto, declare, register, addTo, adapter or HUD panel throws, naming the call. Changing a slot at runtime is still fine. Nothing depends on load order. Two mods declaring the same thing is an error at the second. Where several coexist, their order is sorted: operations by namespace, panels by priority, then mod id, then registration order within a mod.

### Breaking: one placement, declared on the panel

Where a panel sits is declared once, on the panel, with position(PanelPosition...), .priority(n) and .offset(dx, dy). The adapters take only (panel) or (panel, padding). A HUD panel is an ordinary Panel placed with .region(InsideRegion.X). Each context (container screen, other vanilla screen, slot group, HUD, standalone screen) is one host that sorts, places, draws and routes its panels.

The claim rule is the same everywhere. An opaque panel takes its whole rectangle and a see-through one only its controls, so a slot-group panel and a standalone screen's own panels now block what they cover. A vanilla screen gets dialogs and dimming.

### Breaking: elements are built by builders

Every element has a builder and does not change after build(). The names are the same on every builder: at, size, visibleWhen, disabledWhen, tooltip, state(get, set), onClick, style, opaque, declId. A control that shows a value reads the mod's field every frame and stores nothing. The positional constructors and spec(...) factories are gone. Input methods take an InputContext, and one ChildDispatch routes input in every container.

#
...
5.1.0: An inventory mod can now list every slot operation in the game, show each one to the player by name, and block the ones they pick on the slots they pick. This release adds API and breaks nothing.

Vanilla's slot actions are split into one operation per gesture: click to take, click to place, shift-click out, shift-click in, double-click collect, drag fill, hotbar swap, offhand swap, drop one, drop stack, and item pickup. All of them are on by default. MenuKit enforces them itself, so a mod that depends on MenuKit alone gets them without Containers. Q, Ctrl-Q and F count as the same operations when no screen is open.

Every operation has a display name and a one-line description, read from lang keys (slot_operation.<namespace>.<path>), so a settings screen can list them. SlotOperations.all() returns vanilla's operations and every operation other mods have defined.

A lock is a veto. SlotOperations.veto(rule) refuses an operation on a slot and never overrides what the slot's own mod declared. SlotOperations.allows(...) is the single check that vanilla's seams use and that your own operations should use too. A veto knows which player is acting, so a per-player lock can tell a LAN guest's click from the host's.

Clicks your mod sends count as your operation when you wrap them in SlotOperations.as(...). A lock that blocks shift-click then leaves alone the restock that happens to use one. A click sent without the wrapper counts as the gesture it looks like.

When a slot refuses a click, the client does not send it, so the refusal also holds on a server that does not run MenuKit.

Rules set for a slot category now reach vanilla slots. SlotOperations.inherent(PLAYER_HOTBAR, SHIFT_CLICK_IN, FALSE) keeps shift-clicked items out of the hotbar and still lets them into the main inventory.

Slot groups can be listed from the title screen with SlotGroups.all(). Each group has a player-facing name and a stable id you can save. SlotGroupSet shows several groups as one row, which suits a mod that adds many small groups of the same kind.

5.0.0: Slots now say what they are and what may be done to them, and placement names its parts. Three changes, one of them breaking.

Slot groups are a registry. Created slot groups appear next to the vanilla categories, and the registry answers without a menu open. SlotGroupCategories.all() lists every category that exists. SlotGroupCategories.of(menu) lists the slots in each category on an open menu. SlotGroupCategories.categoriesBySlot(menu) answers per slot. A mod mints its own category when no vanilla one fits.

Operations are a named, open vocabulary. An operation is a bulk or shortcut action on a slot. Vanilla ships three: shift-click (QUICK_MOVE), double-click collect (COLLECT), and drag-fill (DRAG_FILL). Any mod defines its own operation as a BehaviorKey and publishes it with SlotOperations.define, so other mods find it through SlotOperations.all(). A slot resolves an operation from its own declaration first, then its group, then the group's category, then the key's default. That order is specificity, not load order, so two mods declaring at different levels no longer depend on which loaded first. SlotOperations.inherent(category, operation, value) sets a category-wide answer once. COLLECT and DRAG_FILL are window keys on any slot kind; Containers enforces them at vanilla's own seams.

Placement has two named parts. A reference is the rectangle a panel is measured against. A region is where the panel sits relative to it. Four region enums are now two, named for the vocabulary instead of the reference they implied. OutsideRegion holds the eleven placements outside a rectangle. InsideRegion holds the nine spots on one. Reference replaces ScreenBounds and SlotGroupBounds, which were the same record.

A panel anchored to a slot group is measured from that one group, not from every slot sharing its category. A created group can no longer move a panel anchored to a vanilla one. SlotGroupPanelAdapter.onGroup(SlotGroupId...) anchors to a created group by id.

Breaking: MenuRegion and SlotGroupRegion are OutsideRegion. HudRegion and ScreenRegion are InsideRegion. ScreenBounds and SlotGroupBounds are Reference. HudRegion.CENTER_CROSSHAIR_CLEARANCE is RegionConstants.CENTER_CROSSHAIR_CLEARANCE. Replace the type names; the constant names inside each enum are unchanged. The registry and operations add API and break nothing on their own.

4.0.0: Vanilla draws every slot. MenuKit no longer runs a slot pass of its own on container screens. A created slot's panel writes the position into vanilla's Slot.x and Slot.y before the slot pass, and vanilla draws the item, count, hover and ghost icon for created and vanilla slots alike. The practical result: a mod that decorates slots through an extractSlot mixin now marks created slots too, without depending on MenuKit. A panel still hides exactly what it covers, like paint: flow panels are composited between the vanilla slots and the created slots inside vanilla's own slot pass, so a panel over part of a vanilla slot hides that part and no more, and a created slot's item still draws over the chrome of the panel that hosts it.

Frame composition order on container screens is declared in one class, ContainerScreenLayers. Flow panels sit below the slot pass; overlay panels and the modal dim sit above it. Panels on recipe-book screens no longer render twice per frame, and the recipe-book render mixin is gone.

Breaking: CreatedSlotResolver now returns the in-menu Slot rather than a position. SlotRendering keeps only the frame helper. Update any code that called either.

3.1.0: Button and Toggle accept a secondary-click handler (onSecondaryClick) that receives a Click with the button and modifier state (right, middle, shift+right); clicks with no handler still fall through to vanilla. Both also take a per-frame tint supplier (tint) for consumer-driven state such as a pinned mode. No breaks.

3.0.0: the Java package is now com.trevlar.menukit (was com.trevorschoeny.menukit); update imports. MenuKit and MenuKit: Containers now share one repository and one set of docs at github.com/trevorschoeny/menukit. No class or method names changed.

3.1.0: Button and Toggle accept a secondary-click handler (onSecondaryClick) that receives a Click with the button and modifier state (right, middle, shift+right); clicks with no handler still fall through to vanilla. Both also take a per-frame tint supplier (tint) for consumer-driven state such as a pinned mode. No breaks.
3.0.0: the Java package is now com.trevlar.menukit (was com.trevorschoeny.menukit); update imports. MenuKit and MenuKit: Containers now share one repository and one set of docs at github.com/trevorschoeny/menukit. No class or method names changed.

MenuKit Icon
Download MenuKit
Select game version
Select platform