Dockable
Concepts

State attributes

Every state a primitive exposes, as data-* attributes and ARIA. The full table.

Primitives expose state only as data-* attributes and ARIA. They never add class names. Boolean attributes are present or absent, never "false": select them with [data-selected], not [data-selected="true"].

.tab[data-selected] { font-weight: 600; }
.tab[data-dragging] { opacity: 0.4; }
.tabset[data-active] .tab[data-selected] { box-shadow: inset 0 -2px Highlight; }
[data-drop-kind="edge"] { outline-style: dashed; }

With Tailwind v4 the same attributes are variants: data-selected:bg-palette-soft, data-[orientation=vertical]:w-px, in-data-active:… (an ancestor has data-active).

The full table

attributeonpresent when / value
data-layout-pathevery primitivethe element's place in the layout; see Layout paths
data-maximizedRoota tabset of the main layout is maximized
data-maximizedTabSetthis tabset is maximized
data-draggingRoota node of this layout is being dragged
data-draggingTabthis tab is being dragged
data-draggingSplitterthe splitter is being dragged with the pointer
data-draggingDropIndicatora drag is over the layout
data-orientationRow"horizontal" (children side by side) or "vertical" (stacked)
data-orientationTabList"horizontal" (default) or "vertical", from its orientation prop
data-orientationSplitter"vertical" for a splitter between side by side children (a vertical bar), "horizontal" between stacked children. Same as its aria-orientation, so it is the opposite of its row's data-orientation
data-rootRowthe layout's root row
data-activeTabSetthe model's active tabset (the last one the user pressed in)
data-emptyTabSet, TabSetContentthe tabset has no tabs
data-selectedTab, Panelthe tab is its tabset's selected tab
data-pinnedTabthe tab is pinned
data-popout-enabledTabthe tab can be popped out into a window (enablePopout and popouts are supported)
data-visiblePanelthe panel is shown (selected, and not hidden by another tabset being maximized)
data-visibleDropIndicatora drop target is under the pointer
data-drop-locationDropIndicatorwhile visible: "center", "top", "bottom", "left" or "right"
data-drop-kindDropIndicatorwhile visible: "edge" for a drop at the outer edge of a row, "rect" otherwise
data-drop-targetTabSet, TabListthe current drag would drop into or beside this tabset (into this strip, for TabList)
data-drop-locationTabSetwhile it is the drop target: where the drag would dock
data-drop-indexTabListwhile it is the drop target: the insertion index
data-drop-refusedRoot, DropIndicator, TabSetthe drag is over a target that a drop rule refused (on TabSet: this one)
data-drop-active, data-drop-overDropZonea drag the zone takes is in progress / over it
data-overflowingTabListsome tabs do not fit in the strip, so they are hidden (tab overflow)
data-overflow-hiddenTabthis tab does not fit in its strip, so it is hidden (it also gets display: none)
data-countTabOverflowTriggerthe number of hidden tabs
data-locationBorder, BorderContent"top", "bottom", "left" or "right"
data-openBorder, BorderContenta tab of the border is selected: its panel is open
data-overlay, data-dockedBorder, BorderContentan overlay border / a split (docked) border
data-tab-directionBorderon a left border: "up" or "down" (borderLeftTabDirection)
data-edge, data-drop-targetEdgeIndicatorits edge; the drop would dock to that edge

Two attributes mark elements the core creates, not the primitives:

attributeonmeaning
data-dockable-moveablethe element hosting a tab's content (inside its Panel)sized 100%/100%; its overflow follows the tab's enableScrollbars
data-dockable-popoutthe content root in a popout window's documentthe element Dockable.Popout portals into

ARIA

primitiveARIA
TabListrole="tablist", aria-orientation, aria-keyshortcuts
Tabrole="tab", aria-selected, aria-controls (its panel's id), aria-keyshortcuts, tabIndex 0 on the tab stop and -1 on the others
Panelrole="tabpanel", aria-labelledby (its tab's id), aria-keyshortcuts, tabIndex={-1}
Splitterrole="separator", aria-orientation, aria-valuenow, aria-valuemin, aria-valuemax, aria-valuetext (e.g. "40%"), aria-label (from getLabel), tabIndex={0}
DropIndicatoraria-hidden="true"

aria-selected is the one exception to "present or absent": it is an ARIA attribute, so it is "true" or "false". Style with [data-selected].

What is not exposed (yet)

Some states you might want are not on the element. Each has a workaround:

  • the selected tab of the active tabset: Tab knows selected but not its tabset's active. Use an ancestor selector: .tabset[data-active] .tab[data-selected], or Tailwind's in-data-active: (gap 1);
  • a panel's tabset state: panels are not inside their tabset, so no selector reaches them from it (gap 2).

See Limitations.

On this page