improve notification accessibility
- use role="status" for info messages, role="alert" for errors - add aria-live attribute (polite for info, assertive for errors) - move phx-click to close button for better keyboard navigation - add close buttons to shop flash messages - add aria-hidden to decorative icons Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
8af5cbf41e
commit
5e03dccb69
@ -2491,6 +2491,22 @@
|
||||
color: hsl(0 70% 50%);
|
||||
}
|
||||
|
||||
.shop-flash-close {
|
||||
margin-left: auto;
|
||||
padding: 0.25rem;
|
||||
cursor: pointer;
|
||||
opacity: 0.5;
|
||||
transition: opacity 150ms ease;
|
||||
border-radius: 0.25rem;
|
||||
|
||||
&:hover { opacity: 0.8; }
|
||||
&:focus-visible {
|
||||
opacity: 1;
|
||||
outline: 2px solid var(--t-accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Transition classes for JS.hide flash dismiss */
|
||||
.fade-out { transition: opacity 200ms ease-out; }
|
||||
.fade-out-from { opacity: 1; }
|
||||
|
||||
@ -45,8 +45,8 @@ defmodule BerrypodWeb.CoreComponents do
|
||||
<div
|
||||
:if={msg = render_slot(@inner_block) || Phoenix.Flash.get(@flash, @kind)}
|
||||
id={@id}
|
||||
phx-click={JS.push("lv:clear-flash", value: %{key: @kind}) |> hide("##{@id}")}
|
||||
role="alert"
|
||||
role={if(@kind == :error, do: "alert", else: "status")}
|
||||
aria-live={if(@kind == :error, do: "assertive", else: "polite")}
|
||||
class="admin-banner"
|
||||
{@rest}
|
||||
>
|
||||
@ -62,7 +62,12 @@ defmodule BerrypodWeb.CoreComponents do
|
||||
<p>{msg}</p>
|
||||
</div>
|
||||
<div class="admin-alert-spacer" />
|
||||
<button type="button" class="admin-alert-close" aria-label={gettext("close")}>
|
||||
<button
|
||||
type="button"
|
||||
class="admin-alert-close"
|
||||
aria-label={gettext("close")}
|
||||
phx-click={JS.push("lv:clear-flash", value: %{key: @kind}) |> hide("##{@id}")}
|
||||
>
|
||||
<.icon name="hero-x-mark" class="size-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -1132,19 +1132,13 @@ defmodule BerrypodWeb.ShopComponents.Content do
|
||||
|
||||
def shop_flash_group(assigns) do
|
||||
~H"""
|
||||
<div id="shop-flash-group" aria-live="polite" class="shop-flash-group">
|
||||
<div id="shop-flash-group" class="shop-flash-group">
|
||||
<%= if msg = Phoenix.Flash.get(@flash, :info) do %>
|
||||
<div
|
||||
id="shop-flash-info"
|
||||
class="shop-flash shop-flash--info"
|
||||
role="alert"
|
||||
phx-click={
|
||||
Phoenix.LiveView.JS.push("lv:clear-flash", value: %{key: :info})
|
||||
|> Phoenix.LiveView.JS.hide(
|
||||
to: "#shop-flash-info",
|
||||
transition: {"fade-out", "fade-out-from", "fade-out-to"}
|
||||
)
|
||||
}
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
>
|
||||
<svg
|
||||
class="shop-flash-icon shop-flash-icon--info"
|
||||
@ -1154,10 +1148,27 @@ defmodule BerrypodWeb.ShopComponents.Content do
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" />
|
||||
</svg>
|
||||
<p>{msg}</p>
|
||||
<button
|
||||
type="button"
|
||||
class="shop-flash-close"
|
||||
aria-label="Close"
|
||||
phx-click={
|
||||
Phoenix.LiveView.JS.push("lv:clear-flash", value: %{key: :info})
|
||||
|> Phoenix.LiveView.JS.hide(
|
||||
to: "#shop-flash-info",
|
||||
transition: {"fade-out", "fade-out-from", "fade-out-to"}
|
||||
)
|
||||
}
|
||||
>
|
||||
<svg width="16" height="16" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<% end %>
|
||||
<%= if msg = Phoenix.Flash.get(@flash, :error) do %>
|
||||
@ -1165,13 +1176,7 @@ defmodule BerrypodWeb.ShopComponents.Content do
|
||||
id="shop-flash-error"
|
||||
class="shop-flash shop-flash--error"
|
||||
role="alert"
|
||||
phx-click={
|
||||
Phoenix.LiveView.JS.push("lv:clear-flash", value: %{key: :error})
|
||||
|> Phoenix.LiveView.JS.hide(
|
||||
to: "#shop-flash-error",
|
||||
transition: {"fade-out", "fade-out-from", "fade-out-to"}
|
||||
)
|
||||
}
|
||||
aria-live="assertive"
|
||||
>
|
||||
<svg
|
||||
class="shop-flash-icon shop-flash-icon--error"
|
||||
@ -1181,6 +1186,7 @@ defmodule BerrypodWeb.ShopComponents.Content do
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
@ -1189,6 +1195,22 @@ defmodule BerrypodWeb.ShopComponents.Content do
|
||||
/>
|
||||
</svg>
|
||||
<p>{msg}</p>
|
||||
<button
|
||||
type="button"
|
||||
class="shop-flash-close"
|
||||
aria-label="Close"
|
||||
phx-click={
|
||||
Phoenix.LiveView.JS.push("lv:clear-flash", value: %{key: :error})
|
||||
|> Phoenix.LiveView.JS.hide(
|
||||
to: "#shop-flash-error",
|
||||
transition: {"fade-out", "fade-out-from", "fade-out-to"}
|
||||
)
|
||||
}
|
||||
>
|
||||
<svg width="16" height="16" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user