Install
$ agentstack add skill-chrisgliddon-bevy-skills-bevy-ui ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ● Shell / process execution Used
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Bevy 0.18 — UI
When to use this skill
- Spawning any
Node-based UI element (panels, buttons, text labels, overlays). - Handling
Buttoninteraction states (Hovered,Pressed,None). - Styling text with
TextFont,TextColor,TextShadow. - Setting
BackgroundColor,BorderColor,BorderRadiuson a widget. - Using palette constants from
bevy::color::palettes. - Wiring up
InputFocusfor accessibility / screen-reader integration. - Nesting child entities with
children![]or.with_children(...). - Debugging a button that shows the wrong color at frame 0 or frame 120.
Canonical pattern
Centered button — full-screen flex container, rounded pill button, text child.
use bevy::{input_focus::InputFocus, prelude::*};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.init_resource::()
.add_systems(Startup, setup)
.add_systems(Update, button_system)
.run();
}
fn setup(mut commands: Commands, asset_server: Res) {
commands.spawn(Camera2d);
commands.spawn((
// Full-screen flex container — centres the button.
Node {
width: percent(100),
height: percent(100),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
..default()
},
children![(
Button,
Node {
width: px(150),
height: px(65),
border: UiRect::all(px(5)),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
border_radius: BorderRadius::MAX,
..default()
},
// Spawn-time border is what appears at frame 0 — `Changed`
// does NOT fire on startup, so the `Interaction::None` arm below
// (which sets the border to BLACK) only runs after the first mouse
// event. This is the frame-0 invariant: see Gotchas #1 + references/gotchas.md.
BorderColor::all(Color::WHITE),
BackgroundColor(Color::BLACK),
children![(
Text::new("Button"),
TextFont {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 33.0,
..default()
},
TextColor(Color::srgb(0.9, 0.9, 0.9)),
TextShadow::default(),
)]
)],
));
}
fn button_system(
mut input_focus: ResMut,
mut query: Query,
>,
) {
for (entity, interaction, mut bg, mut border, mut button) in &mut query {
match *interaction {
Interaction::Pressed => {
input_focus.set(entity);
*bg = BackgroundColor(Color::srgb(0.35, 0.75, 0.35));
*border = BorderColor::all(Color::srgb(1.0, 0.0, 0.0));
button.set_changed(); // signal accessibility system
}
Interaction::Hovered => {
input_focus.set(entity);
*bg = BackgroundColor(Color::srgb(0.25, 0.25, 0.25));
*border = BorderColor::all(Color::WHITE);
button.set_changed();
}
Interaction::None => {
input_focus.clear();
*bg = BackgroundColor(Color::BLACK);
*border = BorderColor::all(Color::BLACK);
// No set_changed() for None — not required by the a11y system.
}
}
}
}
Topics
| Topic | Reference | |---|---| | Node, Val, FlexDirection, AlignItems, layout recipes | [references/layout.md](references/layout.md) | | Text::new, TextFont, TextColor, TextShadow, default font swap | [references/text.md](references/text.md) | | Button, Interaction, Changed, footguns | [references/interaction.md](references/interaction.md) | | BackgroundColor, BorderColor, BorderRadius constructors | [references/colors-and-borders.md](references/colors-and-borders.md) | | bevy::color::palettes::{basic,css,tailwind} | [references/palettes.md](references/palettes.md) | | InputFocus, init_resource, set / clear | [references/accessibility.md](references/accessibility.md) | | children![] vs .with_children(...) | [references/children-macro.md](references/children-macro.md) | | Cross-cutting invariants, frame-0 trap, black-border bug | [references/gotchas.md](references/gotchas.md) |
Gotchas
Changeddoes NOT fire at frame 0. The button shows its
spawn-time components (e.g. BorderColor::all(Color::WHITE)) on the first frame, not the output of button_system. Screenshot-based parity tests taken at frame 0 must account for this. See [references/gotchas.md](references/gotchas.md).
- Call
button.set_changed()inHoveredandPressedarms. This is a
manual dirty-marker required so the accessibility system re-processes the button. Omitting it causes screen readers to miss focus changes. See [references/interaction.md](references/interaction.md).
See also
bevy-cameras— spawn aCamera2dalongside any UI scene.bevy-ecs-queries—Changed,Added, query filters used inbutton_system.bevy-fluent— localized UI text viaFluentText.bevy-cargo-features— feature flags; theuicollection enables all UI crates.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: chrisgliddon
- Source: chrisgliddon/bevy-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.