{# Rendered through flashes() (flask_construct_ui/flashes.py), once per request: by the layout, or wherever the page placed it. The wrapper is always present, even with nothing in it. aria-live sits on it, not role="alert" per message, so messages that land in it via a fragment swap are announced; `assertive` only for errors — a success confirmation should not interrupt the reader. It is up-hungry so a swap that targets a narrower fragment still delivers this request's messages, which get_flashed_messages() has already taken out of the session; core.js keeps what is on screen when the response brings nothing new. #} {% with messages = get_flashed_messages(with_categories=true) %}
{% for category, message in messages %} {% set alert_type = 'info' %} {% set alert_icon = 'notifications' %} {% if category == 'error' or category == 'danger' %} {% set alert_type = 'danger' %}{% set alert_icon = 'cancel' %} {% elif category == 'warning' %} {% set alert_type = 'warning' %}{% set alert_icon = 'notifications' %} {% elif category == 'success' %} {% set alert_type = 'success' %}{% set alert_icon = 'check' %} {% endif %}
{# The icon plus its visually-hidden name carries the category, so it does not rely on colour. #} {{ icon(alert_icon, label=construct.t("alert-" ~ alert_type)) }} {{ t(message) }} {# Dismissal is the reader's choice; nothing removes a message before it has been read. #}
{% endfor %}
{% endwith %}