Dock

@sushindustries/ui

A launcher, what is open, and a corner. Search opens in the middle of the screen.

4 min read
View as Markdown

Search opens a window, not a panel

The control is one glyph in a round well. It was a pill with the word Search in it, which put a labelled button beside a row of buttons labelled with folder names, all competing for the same reading. A magnifier is the one icon that needs no label; the word lives in the tooltip and the aria-label.

Pressing it does not open anything here. It calls onSearch, and on a desktop that means opening a window - dragged, resized, raised, closed and remembered by exactly the same code as a folder, because it is the same thing.

That is the third version. The first was a panel above the button, which put the results in the corner you were already looking away from and opened off the edge on a narrow screen. The second was a palette centred on the screen, which was better placed and still a second kind of surface on a desktop that already had one - and which arrived with its own bugs about stacking contexts and its own missing height bound.

A search window has none of those problems, because none of them are its problems. It inherits the answers a window already has.

A stacking context is not a containing block

The dock needs to be above the desktop so a dragged window cannot cover it. Doing that with position: relative; z-index also made it the containing block for what it contained - which is how the centred palette ended up centring itself on a forty-pixel strip. A flex item takes a z-index while staying statically positioned, so z-index alone lifts the dock without capturing anything inside it.

Tasks toggle

A tab bar implies one of them is showing and the others are not. Here they are all on screen at once, stacked.

Pressing one is a toggle, which is the behaviour every taskbar has and almost nobody writes down:

The window isPressing its task
minimisedbrings it back, and to the front
behind anotherbrings it to the front
already in frontminimises it

The third case is the one people find by accident and then use constantly. It is also why this is toggleToggleA button that stays down, and the single-select group of them - aria-pressed is the whole contract.@sushindustries/ui · content in useDeskStateuseDeskStateWhich windows are open, where they sit and what has been put away, remembered without breaking a server render.@sushindustries/ui · layout rather than raise - the dock does not decide, it presses.

Each task has its own close button, so a window can be dismissed without being raised first. Minimised tasks are dimmed rather than hidden: a window that vanishes from the dock when minimised is a window somebody has lost.

The row scrolls sideways rather than wrapping. A dock that grows a second row moves the desktop above it, and a desktop that resizes because you opened a window is a desktop that loses track of your icons.

Embedded, not applied

An inset shadow at the top edge and a fill darker than the desktop, so the light falls into the dock rather than off the front of it. That is the whole difference between a strip laid on top and a channel cut in.

.dock {
	background: color-mix(in srgb, var(--bg-3) 86%, transparent);
	box-shadow:
		inset 0 1px 0 color-mix(in srgb, var(--bg-3) 90%, transparent),
		inset 0 2px 6px color-mix(in srgb, var(--bg-3) 60%, transparent);
}
css

No blur: it sits on the desktop, which is a solid surface, so there is nothing behind it worth a GPU readback. Controls inside it have no borders either - the channel is already a boundary, and a border inside it is a second one saying the same thing.

Where this is used

WhereDoing
The home page laptopvia Laptop's dockDockA launcher, what is open, and a corner. Search opens upward and results are the consumer's to compute.@sushindustries/ui · layout slot
site-shelf.tsxopens SEARCH_PATH on the desk, and decides what a chosen result does

It is a child of the screen rather than of the scrolling desktop, so it stays put while the desktop scrolls under it.