Headless UI for SolidJS
Warning This release is a breaking change
solid-headless
has been changed to "type": "module"
.data-sh-disabled
is assigned to the element.solid-headless
can now be used without building the package.solid-headless
is no longer needed to be added for ssr.noExternal
.data-sh-*={ownerID}
. Components are now accompanied with data-sh={tag}
, this allows querying components properly as well as writing better selectors. Components that previously do not have those tags also now have their own tags.
Example:[data-sh="tab-group"] {
width: 100%;
display: flex;
align-items: stretch;
justify-content: center;
}
multiple
is now static for HeadlessSelect
-based components (RadioGroup
, Listbox
, Select
, etc.)Modal
will now complain if either defaultOpen
or isOpen
isn't used)Modal
) now also resume focus to last focus start point if the last focused element is no longer available.<ColorSchemeProvider>
, usePreferredColorScheme
, useNativeColorScheme
and useColorScheme
. This set of utilities allows automatic management of color scheme preferences by both the user and the device. This also synchronizes across pages and persists in the local storage. <ColorSchemeProvider>
can also be either controlled and uncontrolled through the use of initialValue
, value
and onChange
.See docs
for example usage.
Uncontrolled components are components whose state is managed by its own instance. These states are usually received through event listeners/properties (e.g. onChange
) that allows you to perform side-effects derived from these states.
Controlled components are components whose state is managed by another component. This is akin to 2-way binding which allows you to read and write the state of that component (e.g. value
and onChange
).
solid-headless
has a handful of stateful components and most of them allows controlled state. However, there was no distinction on how to manage these states and so internally it's sort of turned into "spaghetti code". This release involves a rework on how states are managed in the components by introducing uncontrolled and controlled components.
When a component detects that you use the controlling property (e.g. Accordion
with value
), it opts in to controlled behavior where it doesn't create its own state and relies on the user implementation of the state that will control the component. This also means that the user is required to provide a listener (e.g. onChange
) to receive the state update. If the controlling property is not detected, the component becomes "uncontrolled" and thus creates its own state.
Here's an example with Dialog
:
// Controlled
const [isOpen, setIsOpen] = createSignal(true);
// The controlling property for `Dialog` is `isOpen`.
<Dialog isOpen={isOpen()} onChange={setIsOpen}>
{children}
</Dialog>
// Uncontrolled
// `defaultOpen` provides the initial state.
<Dialog defaultOpen>
{children}
</Dialog>
When managing initial states, uncontrolled components uses the initializing property (Dialog
's defaultOpen
) for initializing it's state. Controlled components must manage its initial state on its own since the initializing property will be ignored.
Check each component for further details.
HeadlessDisclosure
isOpen
to become the controlling property. defaultOpen
now defines the initial state when uncontrolled.HeadlessSelect
defaultValue
.multiple
is set to true, value
, defaultValue
and onChange
will use an array state instead of a value state.HeadlessToggle
checked
to become the controlling property.defaultChecked
now defines the initial state when uncontrolled.Accordion
HeadlessSelect
AlertDialog
onOpen
event.HeadlessDisclosure
Checkbox
HeadlessToggle
ContextMenu
onOpen
and onClose
events. (Just to make it consistent with other Dialog-based components).HeadlessDisclosure
.Dialog
onOpen
event.HeadlessDisclosure
.Disclosure
HeadlessDisclosure
.Listbox
onSelectChange
mimics the onChange
event from HeadlessSelect
(Uses array state when multiple
is set to true
.).HeadlessDisclosure
and HeadlessSelect
.Popover
onOpen
and onClose
events. (Just to make it consistent with other Dialog-based components).HeadlessDisclosure
.RadioGroup
HeadlessSelect
.Select
HeadlessSelect
.CommandBar
** Demo only. CommandBar
is unstyled.
This release introduces the CommandBar
aka kbar, a Dialog
-like component for displaying dialogs through Cmd + K
or Ctrl + K
keyboard shortcuts. The feature introduces the same structure as a Dialog
component (a root component, Panel
, Title
, Overlay
and Description
). Search component and command bar layout is up to the user implementation.
You can try the demo here.
I've now started working on the docs site for solid-headless
although I don't see it getting launched up until the end of this year, but surely it would be already around once v1.0.0 is released 😅
solid-headless
. Previously, solid-headless
is loaded in a single entrypoint for both ESM and CJS, however this proved problematic for runtimes that don't have process.env.NODE_ENV
. This release removes those entrypoints, ESM now mimics CJS (it now has prod and dev bundles) and export maps now supports prod/dev conditions.Tailwind
prefix has been dropped.Toaster
class has been renamed to ToasterStore
.Menu
, ContextMenu
(kind of Disclosure
) and Feed
.data-sh-disabled
, data-sh-expanded
, data-sh-active
, data-sh-checked
and data-sh-pressed
properties.Popover
toggle conflict when clicking PopoverButton
HeadlessToggle
.TailwindAlert
TailwindAlertDialog
TailwindToaster
, TailwindToast
and Toaster
TailwindCheckbox
Popover
, Dialog
and Listbox
.TailwindDialogOverlay
.TailwindPopoverOverlay
.enterDuration
and leaveDuration
from Transition
Transition
beforeLeave
, afterLeave
, beforeEnter
and afterEnter
for Transition
TailwindPopover
, a TailwindDisclosure
-like component with accessibility features similar to a TailwindDialog
.Toolbar
.Dialog
.Listbox
when clicking on the ListboxButton
.