ModalAction
ModalAction is the compact compatibility model for one button in a
Modal footer. It deliberately keeps the original
four-field struct-literal shape. Use ModalFooterAction with ModalLayout when
the action needs an explicit ButtonVariant. It exists so modal buttons stay explicit and state-driven. Instead of letting the dialog close itself implicitly, each action carries an ActionEnvelope that re-enters your normal reducer loop. That means "Cancel," "Delete," and "Save" are all just named application intents. The reducer decides what each one really does, including whether the modal should close.
Example
use fission::prelude::*;
let dialog = Modal {
id: WidgetId::explicit("save_confirm"),
title: "Save these changes?".into(),
content: Text::new("Everyone on the project will see the updated values.").into(),
is_open: view.state().show_save_dialog,
on_dismiss: Some(close_dialog.clone()),
actions: vec![
ModalAction {
label: "Cancel".into(),
on_press: Some(close_dialog),
is_primary: false,
semantics_identifier: Some("save-dialog.cancel".into()),
},
ModalAction {
label: "Save".into(),
on_press: Some(confirm_save),
is_primary: true,
semantics_identifier: Some("save-dialog.save".into()),
},
],
width: None,
motion: None,
backdrop_semantics_identifier: None,
close_semantics_identifier: None,
surface_semantics_identifier: None,
};
In that example, the modal does not close itself. Your reducers decide whether close_dialog or confirm_save should change the state.
Field table
| | | |
|---|
| | | Keep it short and action-oriented. |
| | Action dispatched when the footer button is pressed. | A button without an action is visible but inert. |
| | Chooses the compact action's visual hierarchy. | true uses the primary hierarchy. false uses the neutral secondary hierarchy. |
| | Stable identifier for the generated button semantic node. | Useful for tests and accessibility inspection; it does not replace the visible action label. |
Hierarchy and responsive behavior
Each ModalAction becomes a normal themed Button,
so its default, hover, active, focus, typography, and sizing behavior comes from
the active button recipe. ModalAction does not currently expose a disabled
field; omit unavailable actions or guard them in the reducer. is_primary
maps to ButtonVariant::Primary when true and ButtonVariant::SecondaryGray
when false. For a destructive, outline, tertiary, or otherwise explicit hierarchy, use the
retained footer model:
let footer = ModalFooter::new(vec![ModalFooterAction {
label: "Delete".into(),
on_press: Some(confirm_delete),
variant: ButtonVariant::Destructive,
semantics_identifier: Some("delete-dialog.delete".into()),
}]);
ModalFooterAction belongs to ModalFooter and therefore to ModalLayout; it
does not add fields to the source-compatible ModalAction API.
On wide layouts the modal renders actions in vector order after a flexible
spacer. Below the modal's action-stack breakpoint it stacks the actions and
moves primary or destructive actions ahead of less-emphasized actions, while
preserving relative order within each group. This makes emphasis, rather than a
fragile assumption about vector position, determine the narrow layout. Verify
the result with the actual translated labels.
The generated button owns keyboard and accessibility activation. Enter and
Space dispatch on_press while it is focused. ModalAction has no independent
motion setting; any transition belongs to the containing Modal, while button
state feedback follows the normal button contract.
Specific advice
Keep the footer focused. Most modals should have one primary action and, at most, one secondary escape action. If you find yourself adding many buttons, the problem is usually bigger than a footer can explain and the interaction probably wants a full screen or routed page instead.
Also remember that is_primary is a visual emphasis choice, not business
logic. The reducer should still treat every action explicitly. The same rule
applies to an explicit ModalFooterAction::variant.
Production checklist
For ModalAction, review the fields that change behavior before treating the widget as finished: label, on_press, is_primary, and semantics_identifier. The goal is to make the product rule visible in state and actions, not hidden inside ad-hoc construction code.
Bind on_press to explicit reducer actions and test that the reducer handles unavailable, duplicate, or invalid input safely.
Check the semantics tree for the user-facing label or role that makes this widget understandable without relying only on pixels.
Add at least one component or harness test that confirms the visible text, semantic role, action dispatch, and layout constraint that matter for this widget in context.
If a screen starts repeating the same ModalAction setup, extract a named component around this widget. That keeps the reference API small while making product code easier to read and safer for generated code to copy.