diff --git a/DESIGN_DUIDELINES.md b/DESIGN_DUIDELINES.md
index b0372ef..18864b5 100644
--- a/DESIGN_DUIDELINES.md
+++ b/DESIGN_DUIDELINES.md
@@ -286,11 +286,11 @@ Notes:
- warning: 6–8s
- error: 8–12s (or manual dismiss for critical errors)
- **MUST:** Keep a dismiss button for accessibility and user control.
+- **Status:** Not yet implemented. See [feature-roadmap](docs/feature-roadmap.md) → Flash: Auto-dismiss and consistency.
-### 9.3 Variants + special “email copied”
+### 9.3 Variants (unified)
- Supported semantic variants: `info`, `success`, `warning`, `error`.
-- **Special case:** clipboard “Email copied” uses a **soft/light blue** tone distinct from normal info.
-- **MUST:** Model this as `tone="soft"` (or similar prop) on the flash component, not hard-coded colors in views.
+- **MUST:** Use the same variants for all flash types, : e.g. `success` for copy success, no separate tone or styling. This keeps flash UX consistent across the app.
### 9.4 Accessibility
- Flash must work with screen readers (live region behavior belongs in the flash component implementation).
diff --git a/docs/feature-roadmap.md b/docs/feature-roadmap.md
index b699560..66b46eb 100644
--- a/docs/feature-roadmap.md
+++ b/docs/feature-roadmap.md
@@ -191,6 +191,11 @@
- ❌ Mobile navigation
- ❌ Context-sensitive help
- ❌ Onboarding tooltips
+- ❌ **Flash: Auto-dismiss and consistency** (Design Guidelines §9)
+ - Auto-dismiss: info/success 4–6s, warning 6–8s, error 8–12s; dismiss button kept for accessibility.
+ - Implement via JS hook (e.g. `FlashAutoDismiss`) + `data-dismiss-ms` (or `data-kind`) on flash component; on timeout push `lv:clear-flash` and hide element.
+ - LiveView: add shared `handle_event("lv:clear-flash", %{"key" => key}, socket)` (e.g. in `MvWeb` live_view quote) calling `clear_flash(socket, key)`.
+ - All flashes (including “Email copied”) use the same variants (info, success, warning, error); no special tone. See `DESIGN_DUIDELINES.md` §9.
---
diff --git a/lib/mv_web/components/core_components.ex b/lib/mv_web/components/core_components.ex
index 4f9d7af..1e8e7f3 100644
--- a/lib/mv_web/components/core_components.ex
+++ b/lib/mv_web/components/core_components.ex
@@ -60,7 +60,7 @@ defmodule MvWeb.CoreComponents do
id={@id}
phx-click={JS.push("lv:clear-flash", value: %{key: @kind}) |> hide("##{@id}")}
role="alert"
- class="z-50 toast toast-bottom toast-end"
+ class="pointer-events-auto"
{@rest}
>
+
<.flash kind={:success} flash={@flash} />
<.flash kind={:warning} flash={@flash} />
<.flash kind={:info} flash={@flash} />
diff --git a/lib/mv_web/controllers/auth_controller.ex b/lib/mv_web/controllers/auth_controller.ex
index 28f3846..20a76f5 100644
--- a/lib/mv_web/controllers/auth_controller.ex
+++ b/lib/mv_web/controllers/auth_controller.ex
@@ -31,7 +31,7 @@ defmodule MvWeb.AuthController do
|> store_in_session(user)
# If your resource has a different name, update the assign name here (i.e :current_admin)
|> assign(:current_user, user)
- |> put_flash(:info, message)
+ |> put_flash(:success, message)
|> redirect(to: return_to)
end
@@ -322,7 +322,7 @@ defmodule MvWeb.AuthController do
conn
|> clear_session(:mv)
- |> put_flash(:info, gettext("You are now signed out"))
+ |> put_flash(:success, gettext("You are now signed out"))
|> redirect(to: return_to)
end
end
diff --git a/lib/mv_web/live/auth/link_oidc_account_live.ex b/lib/mv_web/live/auth/link_oidc_account_live.ex
index b6c24b1..01bd57b 100644
--- a/lib/mv_web/live/auth/link_oidc_account_live.ex
+++ b/lib/mv_web/live/auth/link_oidc_account_live.ex
@@ -81,7 +81,7 @@ defmodule MvWeb.LinkOidcAccountLive do
socket
|> put_flash(
- :info,
+ :success,
dgettext("auth", "Account activated! Redirecting to complete sign-in...")
)
|> Phoenix.LiveView.redirect(to: ~p"/auth/user/oidc")
@@ -217,7 +217,7 @@ defmodule MvWeb.LinkOidcAccountLive do
{:noreply,
socket
|> put_flash(
- :info,
+ :success,
dgettext(
"auth",
"Your OIDC account has been successfully linked! Redirecting to complete sign-in..."
diff --git a/lib/mv_web/live/datafields_live.ex b/lib/mv_web/live/datafields_live.ex
index f7436ab..f922d22 100644
--- a/lib/mv_web/live/datafields_live.ex
+++ b/lib/mv_web/live/datafields_live.ex
@@ -64,12 +64,12 @@ defmodule MvWeb.DatafieldsLive do
{:noreply,
socket
|> assign(:active_editing_section, nil)
- |> put_flash(:info, gettext("Data field %{action} successfully", action: action))}
+ |> put_flash(:success, gettext("Data field %{action} successfully", action: action))}
end
@impl true
def handle_info({:custom_field_deleted, _custom_field}, socket) do
- {:noreply, put_flash(socket, :info, gettext("Data field deleted successfully"))}
+ {:noreply, put_flash(socket, :success, gettext("Data field deleted successfully"))}
end
@impl true
@@ -115,7 +115,7 @@ defmodule MvWeb.DatafieldsLive do
socket
|> assign(:settings, updated_settings)
|> assign(:active_editing_section, nil)
- |> put_flash(:info, gettext("Member field %{action} successfully", action: action))}
+ |> put_flash(:success, gettext("Member field %{action} successfully", action: action))}
end
@impl true
diff --git a/lib/mv_web/live/global_settings_live.ex b/lib/mv_web/live/global_settings_live.ex
index f3a61bc..485601a 100644
--- a/lib/mv_web/live/global_settings_live.ex
+++ b/lib/mv_web/live/global_settings_live.ex
@@ -357,20 +357,21 @@ defmodule MvWeb.GlobalSettingsLive do
errors_with_names = enrich_sync_errors(errors)
result = %{synced: synced, errors: errors_with_names}
+ {flash_kind, flash_message} =
+ if(errors_with_names == [],
+ do: {:success, gettext("Synced %{count} member(s) to Vereinfacht.", count: synced)},
+ else:
+ {:warning,
+ gettext("Synced %{count} member(s). %{error_count} failed.",
+ count: synced,
+ error_count: length(errors_with_names)
+ )}
+ )
+
socket =
socket
|> assign(:last_vereinfacht_sync_result, result)
- |> put_flash(
- :info,
- if(errors_with_names == [],
- do: gettext("Synced %{count} member(s) to Vereinfacht.", count: synced),
- else:
- gettext("Synced %{count} member(s). %{error_count} failed.",
- count: synced,
- error_count: length(errors_with_names)
- )
- )
- )
+ |> put_flash(flash_kind, flash_message)
{:noreply, socket}
@@ -409,7 +410,7 @@ defmodule MvWeb.GlobalSettingsLive do
|> assign(:oidc_client_secret_set, present?(fresh_settings.oidc_client_secret))
|> assign(:oidc_configured, Mv.Config.oidc_configured?())
|> assign(:vereinfacht_test_result, test_result)
- |> put_flash(:info, gettext("Settings updated successfully"))
+ |> put_flash(:success, gettext("Settings updated successfully"))
|> assign_form()
{:noreply, socket}
diff --git a/lib/mv_web/live/group_live/form.ex b/lib/mv_web/live/group_live/form.ex
index 5f781a7..d9999d3 100644
--- a/lib/mv_web/live/group_live/form.ex
+++ b/lib/mv_web/live/group_live/form.ex
@@ -128,7 +128,7 @@ defmodule MvWeb.GroupLive.Form do
socket =
socket
- |> put_flash(:info, gettext("Group saved successfully."))
+ |> put_flash(:success, gettext("Group saved successfully."))
|> push_navigate(to: redirect_path)
{:noreply, socket}
diff --git a/lib/mv_web/live/group_live/index.ex b/lib/mv_web/live/group_live/index.ex
index b6c8277..70358e0 100644
--- a/lib/mv_web/live/group_live/index.ex
+++ b/lib/mv_web/live/group_live/index.ex
@@ -76,24 +76,24 @@ defmodule MvWeb.GroupLive.Index do
<:col :let={group} label={gettext("Members")} class="text-right">
{group.member_count || 0}
- <:action :let={group}>
- <.button
- variant="ghost"
- size="sm"
- navigate={~p"/groups/#{group.slug}"}
- >
- {gettext("View")}
-
- <%= if can?(@current_user, :update, Mv.Membership.Group) do %>
+ <:action :let={group}>
<.button
variant="ghost"
size="sm"
- navigate={~p"/groups/#{group.slug}/edit"}
+ navigate={~p"/groups/#{group.slug}"}
>
- {gettext("Edit group")}
+ {gettext("View")}
- <% end %>
-
+ <%= if can?(@current_user, :update, Mv.Membership.Group) do %>
+ <.button
+ variant="ghost"
+ size="sm"
+ navigate={~p"/groups/#{group.slug}/edit"}
+ >
+ {gettext("Edit group")}
+
+ <% end %>
+
<% end %>
diff --git a/lib/mv_web/live/group_live/show.ex b/lib/mv_web/live/group_live/show.ex
index 46766ef..7e2d57f 100644
--- a/lib/mv_web/live/group_live/show.ex
+++ b/lib/mv_web/live/group_live/show.ex
@@ -150,7 +150,7 @@ defmodule MvWeb.GroupLive.Show do