wilr / silverstripe-togglegroup-field
Toggle group form fields for the Silverstripe CMS, styled after the shadcn/ui toggle group component. Provides drop-in replacements for OptionsetField and CheckboxSetField.
Package info
github.com/wilr/silverstripe-togglegroup-field
Type:silverstripe-vendormodule
pkg:composer/wilr/silverstripe-togglegroup-field
Requires
- php: ^8.3
- silverstripe/framework: ^6
Requires (Dev)
Suggests
- dnadesign/silverstripe-elemental: Allows ToggleGroupField and ToggleGroupSetField to be used inside Elemental blocks.
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-17 00:00:53 UTC
README
Form field subclasses that render as a segmented "toggle group" control - styled after shadcn/ui's toggle group component
- instead of a list of radio buttons or checkboxes.
ToggleGroupFieldis a drop-in replacement forOptionsetField(single selection)ToggleGroupSetFieldis a drop-in replacement forCheckboxSetField(multiple selection)
Both fields work anywhere a standard getCMSFields() field can be used,
including inside Elemental
content blocks.
Above: ToggleGroupField (single-selection "Alignment") and
ToggleGroupSetField (multi-selection "Available days") added to a Page's
getCMSFields().
Above: the same two fields added to a DNADesign\Elemental\Models\BaseElement
subclass's getCMSFields(), rendering identically inside the Elemental block
editor.
Requirements
- PHP ^8.3
- silverstripe/framework ^6
- dnadesign/silverstripe-elemental ^6 (optional, only if you want to use the fields inside Elemental blocks)
For Silverstripe CMS 5, use the 1 branch (^1).
Installation
composer require wilr/silverstripe-togglegroup-field
Then run a dev/build and flush (?flush=1).
Usage
Both fields extend their core Silverstripe equivalent, so they accept the
same constructor arguments, support the same saving behaviour (including
has_one/many_many relations and DBMultiEnum/JSON-encoded values), the
same validation, and the same readonly/disabled transformations. Only the
rendering changes.
Single selection - ToggleGroupField
use Wilr\ToggleGroupField\Forms\ToggleGroupField; public function getCMSFields() { $fields = parent::getCMSFields(); $fields->addFieldToTab('Root.Main', ToggleGroupField::create( 'Alignment', 'Alignment', [ 'left' => 'Left', 'center' => 'Center', 'right' => 'Right', ] )); return $fields; }
Multiple selection - ToggleGroupSetField
use Wilr\ToggleGroupField\Forms\ToggleGroupSetField; public function getCMSFields() { $fields = parent::getCMSFields(); $fields->addFieldToTab('Root.Main', ToggleGroupSetField::create( 'Days', 'Available days', [ 'mon' => 'Mon', 'tue' => 'Tue', 'wed' => 'Wed', 'thu' => 'Thu', 'fri' => 'Fri', ] )); return $fields; }
Inside an Elemental block
Add the fields to a DNADesign\Elemental\Models\BaseElement subclass exactly
as you would on a Page:
use DNADesign\Elemental\Models\BaseElement; use Wilr\ToggleGroupField\Forms\ToggleGroupField; class MyElement extends BaseElement { private static $db = [ 'Alignment' => 'Varchar', ]; public function getCMSFields() { $fields = parent::getCMSFields(); $fields->addFieldToTab('Root.Main', ToggleGroupField::create( 'Alignment', 'Alignment', [ 'left' => 'Left', 'center' => 'Center', 'right' => 'Right', ] )); return $fields; } }
Elemental's block editor is a schema-driven, React-rendered form (unlike a
classic Page.getCMSFields(), which is rendered server-side as plain HTML).
OptionsetField and CheckboxSetField normally declare a dedicated React
component for that context, which would bypass this module's template
entirely. Both fields here explicitly fall back to the generic FormField
schema component instead, so the same server-rendered markup - and therefore
the same styling and behaviour - is used everywhere, including inline and in
the "add block" modal.
Style variants
ToggleGroupField::create(/* ... */) ->setButtonsBlock(true) // stretch options to fill the available width ->setButtonsSmall(true); // render a more compact button size
Icons on option labels
Both fields can show a Silverstripe CMS font icon and/or custom SVG beside (or instead of) each option's text label.
CMS font icons
Use the same icon identifiers as FormAction::setIcon() (with or without
the font-icon- prefix). Browse the available glyphs in
vendor/silverstripe/admin/client/src/font/icons-reference.html after
installing silverstripe/admin.
ToggleGroupField::create( 'Alignment', 'Alignment', [ 'left' => 'Left', 'center' => 'Center', 'right' => 'Right', ] )->setOptionIcons([ 'left' => 'angle-left', 'center' => 'dot-3', 'right' => 'angle-right', ]);
Custom SVG
Pass a full <svg>...</svg> element, or one or more shape elements such as
<path> (automatically wrapped in a 24×24 viewBox SVG that inherits the
button colour via currentColor). SVG is rendered as trusted HTML - only
pass markup you control.
ToggleGroupField::create( 'Layout', 'Layout', [ 'list' => 'List', 'grid' => 'Grid', ] )->setOptionIcons([ 'list' => '<path d="M4 6h16M4 12h16M4 18h16" stroke="currentColor" stroke-width="2" fill="none"/>', 'grid' => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><rect x="3" y="3" width="8" height="8"/><rect x="13" y="3" width="8" height="8"/><rect x="3" y="13" width="8" height="8"/><rect x="13" y="13" width="8" height="8"/></svg>', ]);
Icon-only buttons
Hide the text visually while keeping it available to screen readers through
the associated <label>:
ToggleGroupField::create(/* ... */) ->setOptionIcons([ 'left' => 'angle-left', 'center' => 'dot-3', 'right' => 'angle-right', ]) ->setIconsOnly(true);
Accessibility
Each option is rendered as a real <input type="radio"> or
<input type="checkbox"> paired with a <label> - the toggle-look buttons
are just CSS applied to native, always-keyboard-accessible form controls
(visually hidden with a clip-based technique rather than display: none,
so they remain focusable and readable by screen readers). This means:
- The field works correctly with no JavaScript at all.
- Tab moves focus in and out of the group as normal.
- For
ToggleGroupField, arrow keys move the selection between options - this is native browser behaviour for radio inputs sharing the samename. - For
ToggleGroupSetField, arrow keys move focus (without changing selection) between checkboxes, matching the WAI-ARIA toggle group keyboard pattern. This is implemented inclient/src/js/toggle-group.jssince checkboxes have no native roving-focus behaviour. - The wrapping element uses
role="radiogroup"(ToggleGroupField) orrole="group"(ToggleGroupSetField) to describe the relationship between the options to assistive technology.
Running the tests
composer install
vendor/bin/phpunit
License
See LICENSE

