+ """
+ end
+
+ defp get_labels(value, options) do
+ values = List.wrap(value) |> Enum.map(&to_string/1)
+
+ options
+ |> Enum.filter(fn {_label, option_value} -> to_string(option_value) in values end)
+ |> Enum.map(fn {label, _value} -> label end)
+ end
+
+ defp get_options(assigns) do
+ case Map.get(assigns.field_options, :options) do
+ options when is_function(options) -> options.(assigns)
+ options -> options
+ end
+ end
+end
diff --git a/lib/backpex/html/form.ex b/lib/backpex/html/form.ex
index dd21ce354..ee2575d1f 100644
--- a/lib/backpex/html/form.ex
+++ b/lib/backpex/html/form.ex
@@ -23,7 +23,7 @@ defmodule Backpex.HTML.Form do
attr :type, :string,
default: "text",
- values: ~w(checkbox color date datetime-local email file hidden month number password
+ values: ~w(checkbox checkgroup color date datetime-local email file hidden month number password
range radio search select tel text textarea time toggle url week)
attr :field, Phoenix.HTML.FormField, doc: "a form field struct retrieved from the form, for example: @form[:email]"
@@ -142,6 +142,31 @@ defmodule Backpex.HTML.Form do
"""
end
+ def input(%{type: "checkgroup"} = assigns) do
+ ~H"""
+
From ac76ebea160b818752b9df0464445956d3a18adc Mon Sep 17 00:00:00 2001
From: Florian Arens <60519307+Flo0807@users.noreply.github.com>
Date: Wed, 22 Apr 2026 15:18:48 +0200
Subject: [PATCH 02/10] Remove redundant Backpex.HTML alias in Checkgroup
The `alias Backpex.HTML` is already injected by `use Backpex.Field`
via BackpexWeb's field helpers. Drop the duplicate alias; sibling
fields like Select and Text rely on the injected alias as well.
---
lib/backpex/fields/checkgroup.ex | 2 --
1 file changed, 2 deletions(-)
diff --git a/lib/backpex/fields/checkgroup.ex b/lib/backpex/fields/checkgroup.ex
index 1e959b115..a50462548 100644
--- a/lib/backpex/fields/checkgroup.ex
+++ b/lib/backpex/fields/checkgroup.ex
@@ -33,8 +33,6 @@ defmodule Backpex.Fields.Checkgroup do
"""
use Backpex.Field, config_schema: @config_schema
- alias Backpex.HTML
-
@impl Backpex.Field
def render_value(assigns) do
options = get_options(assigns)
From e494a60fda83ed07885eab24541c0c20de542aa5 Mon Sep 17 00:00:00 2001
From: Florian Arens <60519307+Flo0807@users.noreply.github.com>
Date: Wed, 22 Apr 2026 15:18:57 +0200
Subject: [PATCH 03/10] Fix type coercion in Checkgroup checked= comparison
Compare option values and the current selection as strings, matching
the coercion already performed by Checkgroup.get_labels/2. Without
this, atom or integer option values never register as checked when
the wrapped value is stored as strings (or vice versa).
---
lib/backpex/html/form.ex | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/backpex/html/form.ex b/lib/backpex/html/form.ex
index ebd709c0e..062f0c5c1 100644
--- a/lib/backpex/html/form.ex
+++ b/lib/backpex/html/form.ex
@@ -156,7 +156,7 @@ defmodule Backpex.HTML.Form do
id={"#{@id}-#{value}"}
name={@name <> "[]"}
value={value}
- checked={value in List.wrap(@value)}
+ checked={to_string(value) in Enum.map(List.wrap(@value), &to_string/1)}
class={["checkbox checkbox-sm checkbox-primary", @errors != [] && "checkbox-error"]}
{@rest}
/>
From 7ed8c658c32c3f6c59843f875b31a98256632dcb Mon Sep 17 00:00:00 2001
From: Florian Arens <60519307+Flo0807@users.noreply.github.com>
Date: Wed, 22 Apr 2026 15:19:22 +0200
Subject: [PATCH 04/10] Use semantic fieldset/legend for Checkgroup and fix
group ARIA
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Wrap the checkgroup inputs in a
diff --git a/lib/backpex/html/form.ex b/lib/backpex/html/form.ex
index 062f0c5c1..2a40948c9 100644
--- a/lib/backpex/html/form.ex
+++ b/lib/backpex/html/form.ex
@@ -146,11 +146,11 @@ defmodule Backpex.HTML.Form do
def input(%{type: "checkgroup"} = assigns) do
~H"""
-
- {@label}
+
+
"""
end
From 245c588910b71c2fa59e594aec5fa0fe30eaafa5 Mon Sep 17 00:00:00 2001
From: Florian Arens <60519307+Flo0807@users.noreply.github.com>
Date: Wed, 22 Apr 2026 15:19:43 +0200
Subject: [PATCH 05/10] Wire aria-invalid and aria-describedby for Checkgroup
errors
Each checkbox now advertises its invalid state via aria-invalid when
the field has errors, and points at the help text via
aria-describedby when one is provided. Give the help_text component
an optional `id` attribute so we can target it stably as
`-help`.
---
lib/backpex/html/form.ex | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/lib/backpex/html/form.ex b/lib/backpex/html/form.ex
index 2a40948c9..805be5bec 100644
--- a/lib/backpex/html/form.ex
+++ b/lib/backpex/html/form.ex
@@ -158,12 +158,14 @@ defmodule Backpex.HTML.Form do
value={value}
checked={to_string(value) in Enum.map(List.wrap(@value), &to_string/1)}
class={["checkbox checkbox-sm checkbox-primary", @errors != [] && "checkbox-error"]}
+ aria-invalid={@errors != [] && "true"}
+ aria-describedby={@help_text && "#{@id}-help"}
/>
{label}
<.error :for={msg <- @errors} :if={not @hide_errors}>{msg}
- <.help_text :if={@help_text}>{@help_text}
+ <.help_text :if={@help_text} id={"#{@id}-help"}>{@help_text}
"""
end
@@ -294,13 +296,14 @@ defmodule Backpex.HTML.Form do
"""
@doc type: :component
+ attr :id, :string, default: nil
attr :class, :string, default: nil
slot :inner_block, required: true
def help_text(assigns) do
~H"""
-
+
{render_slot(@inner_block)}
"""
From 13404ee0d7b7367402d84c18a013ff6b5294e605 Mon Sep 17 00:00:00 2001
From: Florian Arens <60519307+Flo0807@users.noreply.github.com>
Date: Wed, 22 Apr 2026 15:20:57 +0200
Subject: [PATCH 06/10] Support readonly on Checkgroup field
Add a `readonly` option to Checkgroup's config schema, pass it
through Checkgroup.render_form/1 to BackpexForm.input, and honor it
on each checkbox via `disabled={@readonly}`. Matches the convention
already used by the Text and Select fields.
Because `{@rest}` is no longer spread onto individual checkboxes,
the checkgroup clause pulls `readonly` out of `@rest` explicitly.
---
lib/backpex/fields/checkgroup.ex | 5 +++++
lib/backpex/html/form.ex | 3 +++
2 files changed, 8 insertions(+)
diff --git a/lib/backpex/fields/checkgroup.ex b/lib/backpex/fields/checkgroup.ex
index 89180ea89..b5f7da19a 100644
--- a/lib/backpex/fields/checkgroup.ex
+++ b/lib/backpex/fields/checkgroup.ex
@@ -4,6 +4,10 @@ defmodule Backpex.Fields.Checkgroup do
doc: "List of options or function that receives the assigns.",
type: {:or, [{:list, :any}, {:fun, 1}]},
required: true
+ ],
+ readonly: [
+ doc: "Sets the field to readonly. Also see the [panels](/guides/fields/readonly.md) guide.",
+ type: {:or, [:boolean, {:fun, 1}]}
]
]
@@ -65,6 +69,7 @@ defmodule Backpex.Fields.Checkgroup do
options={@options}
translate_error_fun={Backpex.Field.translate_error_fun(@field_options, assigns)}
help_text={Backpex.Field.help_text(@field_options, assigns)}
+ readonly={@readonly}
/>
diff --git a/lib/backpex/html/form.ex b/lib/backpex/html/form.ex
index 805be5bec..28a0b662f 100644
--- a/lib/backpex/html/form.ex
+++ b/lib/backpex/html/form.ex
@@ -145,6 +145,8 @@ defmodule Backpex.HTML.Form do
end
def input(%{type: "checkgroup"} = assigns) do
+ assigns = assign_new(assigns, :readonly, fn -> Map.get(assigns.rest, :readonly, false) end)
+
~H"""