Sweets
Configuration

Layout

Tiling layout, special workspace, gaps, and window borders.

These blocks shape how tiled windows are arranged and drawn: the master/stack split, special workspace overlay, spacing between windows, and borders around them.

sweets.layout{ ... }

Controls tiling behavior and the layout new workspaces start in.

sweets.layout {
  master = 50,
  new_window = "master",
  reorder_ratio = 0.1,
  default = "tile",
  master_position = "left",
  cycle = { "tile", "monocle", "dwindle" },
}
KeyTypeDefaultDescription
masterint50Master size, 1–99 percent (starting value; adjustable at runtime)
new_windowstringmasterPlacement of a newly opened window: master or stack
reorder_rationumber0.1Drag-to-reorder threshold, 0.1–0.9
defaultstringtileLayout new workspaces start in: tile, monocle, dwindle, centered, grid, columns, deck, or tabbed
master_positionstringleftSide the master area sits on: left, right, top, or bottom
cyclearrayall layoutsOrdered set of layout names the cycle_layout action steps through

Layouts

Each workspace has its own layout, so different workspaces (and the special workspace) can use different arrangements at the same time. New workspaces start in default; change a workspace's layout at runtime with the layout and cycle_layout actions (see Key bindings).

  • tile — master/stack: one master area beside the stack. This is the layout the master, new_window, reorder_ratio, master_position, drag-reorder, and keyboard-resize controls below apply to.
  • monocle — every window fills the whole usable area and only the focused one is shown, like a stack of full-screen cards. Switching focus (focus_next/focus_prev) brings the next window to the front. The master/stack split and resize controls do nothing in monocle; gaps and borders still apply.
  • dwindle — fibonacci split: each new window halves the remaining space, alternating between a vertical and a horizontal cut, so windows spiral inward. The first window takes the left column, the next the top of what's left, and so on. Every window stays visible. The first split honors the master percentage, so the horizontal resize controls (resize_grow/resize_shrink) widen and narrow the master column just like in tile. The vertical resize controls resize the divider the focused window borders, at any depth — horizontal actions (resize_grow/resize_shrink) move the vertical divider beside it, vertical actions (resize_grow_vertical/resize_shrink_vertical) the horizontal one. Resizing a deep window adjusts its own split, not the master. The pointer (MOD+right-drag) works the same way and additionally lets a corner drag move both dividers at once. Each split remembers its own ratio. Gaps and borders apply throughout.
  • centered — centered master: the master window sits in a centered column taking the master percentage of the width, with stack windows split into a left and a right column on either side. New stack windows alternate right, left, right, left… so the side columns stay balanced. Within each column windows stack top to bottom, evenly by default. With one window the master fills the area; with two it degrades to a plain two-column split (master beside a single stack). Best on wide and 4K displays. Every window stays visible. The horizontal resize controls (resize_grow/resize_shrink) widen and narrow the master column like in tile, and a MOD+right-drag on the edge a window shares with the master resizes it with the pointer (the dragged edge tracks the cursor as the master grows symmetrically). The vertical resize controls (resize_grow_vertical/resize_shrink_vertical) and a MOD+right-drag on the top or bottom edge of a side-column window resize it against its neighbor in that column, so each column's heights are adjustable independently. master_position is ignored (always horizontal-centered). Gaps and borders apply throughout.
  • grid — even grid: windows fill a grid of roughly ceil(sqrt(n)) columns, row by row, every window the same size. When the last row is short its windows stretch to fill the full width, so the grid always reaches every edge with no leftover gap. Best when you have several equally important windows. A MOD+right-drag resizes cells against their neighbors: a left or right edge moves the column divider between the window and its row neighbor, a top or bottom edge moves the shared divider between that row and the adjacent one, and a corner drag moves both. The keyboard resize actions work too: resize_grow/resize_shrink move the focused cell's column divider (its right neighbor, or its left when it is last in the row) and resize_grow_vertical/resize_shrink_vertical move the divider between its row and the one below (or above, for the bottom row), always enlarging or shrinking the focused cell. The grid_balance action resets every row and column back to even, undoing all resizing in one keystroke. There is no master, so the master/stack controls do nothing and master_position is ignored; gaps and borders apply throughout.
  • columns — equal-width columns: every window becomes a full-height column side by side, left to right in open order, each the same width by default (the last column takes any rounding remainder so the columns always reach both edges). Unlike grid it never wraps and unlike tile there is no master, so it stays a single band however many windows you open. Best on wide and 4K displays for a few side-by-side windows. Every window stays visible. master_position reorients the band: left (default) and right give vertical columns side by side, top and bottom give full-width rows stacked instead, and the right and bottom variants reverse the open order so the first window sits at the far edge. Because columns only have two orientations, cycle_master_position toggles straight between columns and rows (one press per flip); use an explicit master_position "right" or "bottom" bind if you want the reversed order. Bands are resizable against their neighbor along the run axis: the resize controls (resize_grow/resize_shrink for columns, resize_grow_vertical/resize_shrink_vertical for rows) grow and shrink the focused band against the next one (or the previous one when it is last), and a MOD+right-drag on the leading or trailing edge does the same with the pointer. Resizing across the other axis does nothing. There is no master, so the master/stack controls do nothing; gaps and borders apply throughout.
  • deck — master plus a tabbed stack: the master takes its slot exactly like tile, but the whole stack shares one slot and only the focused stack window is shown, like a deck of cards behind the master. Opening more windows never shrinks the stack, so it stays readable however many you pile up. Switching focus (focus_next/focus_prev, or focusing a stack window directly) brings that window to the front of the stack; the master stays put. The master percentage and master_position place the split just like tile, and the split resizes the same way: the resize controls (resize_grow/resize_shrink along the split axis) widen and narrow the master, and a MOD+right-drag on the master/stack divider does the same with the pointer. Because the stack is a single slot there is nothing to resize within it, so the cross-axis controls do nothing. Gaps and borders apply throughout.
  • tabbed — sway/i3-style tabs: every window fills one slot below a tab strip along the top, and only the focused window is shown, like monocle with a title bar. The strip draws one tab per window with its title, the focused tab highlighted, so you can see everything open at a glance. Switching focus (focus_next/focus_prev, which walk the tabs in open order) brings the next window to the front and moves the highlight. You can also drive the strip with the pointer: left-click a tab to focus its window, middle-click a tab to close it, or scroll the wheel over the strip to cycle tabs (scroll down for the next tab). move_left/move_right reorder the focused window within the strip. A tab whose window asks for attention is drawn in the urgent color until you focus it. Opening more windows never shrinks the visible window; tabs just get narrower. There is no master, so the master/stack and resize controls do nothing and master_position is ignored. Style the strip with sweets.tabbar{ ... } below; gaps and borders apply throughout.

These actions are not bound by default. Bind them yourself, for example:

sweets.bind("MOD+e", "cycle_layout")
sweets.bind("MOD+t", "layout", "tile")
sweets.bind("MOD+w", "layout", "monocle")
sweets.bind("MOD+d", "layout", "dwindle")
sweets.bind("MOD+c", "layout", "centered")
sweets.bind("MOD+g", "layout", "grid")
sweets.bind("MOD+n", "layout", "columns")
sweets.bind("MOD+k", "layout", "deck")
sweets.bind("MOD+b", "layout", "tabbed")

Cycling layouts

cycle_layout advances the active workspace to the next layout. By default it walks every layout in the order listed above (tilemonocledwindle → … → tabbed → back to tile). Set cycle in sweets.layout{ ... } to restrict and reorder that walk to just the layouts you use:

sweets.layout {
  cycle = { "tile", "dwindle", "monocle" },
}

Only the listed layouts take part, and the array order is the cycle order — so this example steps tiledwindlemonocle → back to tile. The list must name at least one valid layout with no duplicates, or it is a config error that keeps the last valid config active. The default layout and the explicit layout action are independent of cycle; if the current workspace is on a layout that is not in the list, cycling jumps to the first entry.

cycle_layout also takes an optional direction. The default is forward; pass "prev" to step backward through the same set, which pairs well with a Shift variant of the forward bind:

sweets.bind("MOD+w", "cycle_layout")
sweets.bind("MOD+Shift+w", "cycle_layout", "prev")

Master position

master_position chooses which side of the screen the master area occupies. left (default) and right keep a vertical stack beside the master column; top and bottom rotate the layout so the master is a full-width row with the stack tiled in a row across the remaining space. The master percentage applies along the split axis either way (column width for left/right, row height for top/bottom). The columns layout also honors it as an orientation control — see its entry above — while the other layouts ignore it.

Master position is per-workspace, seeded from master_position. Change it at runtime with the master_position and cycle_master_position actions (see Key bindings). They are not bound by default:

sweets.bind("MOD+m", "cycle_master_position")
sweets.bind("MOD+Left", "master_position", "left")
sweets.bind("MOD+Right", "master_position", "right")
sweets.bind("MOD+Up", "master_position", "top")
sweets.bind("MOD+Down", "master_position", "bottom")

new_window

Controls where a freshly opened window lands. master (dwm-style, default) makes it the new master and pushes the old master into the stack, so the layout reshuffles on every open. stack keeps the existing master untouched and appends the window to the bottom of the stack (the first window on an empty workspace is the master either way). An invalid value is a config error.

reorder_ratio

Tunes how far you must drag a tiled window (with MOD + left drag) before it takes a neighbor's place on release. It is the fraction of the neighboring window you must cross, measured from the edge you approach it from, so it behaves the same in every direction regardless of the master split. Lower values reorder sooner (a short drag is enough); higher values require dragging deeper into the neighbor, which avoids accidental swaps. Out-of-range values are a config error.

Keyboard resizing

The default MOD+= and MOD+- bindings grow or shrink the focused tiled window by changing the master/stack split in 5-percent steps. If the focused window is the master, grow makes the master area larger; if it is in the stack, grow makes the stack area larger.

MOD+Shift+= and MOD+Shift+- resize along the other axis instead, transferring space between the focused stack window and an adjacent stack neighbor. The keypad KP_Add and KP_Subtract bindings provide the same controls.

The two resize axes follow the master position: the resize key whose direction matches the master/stack split adjusts the master size, and the cross-axis key resizes the stack pair. With top/bottom master the vertical keys drive the master split and the horizontal keys resize the stack. Stack resize has no effect for the master or when there is only one stack window.

Special workspace

The special workspace shown by MOD+s tiles like ordinary workspaces and starts in the same default layout and master_position. It is private compositor state, not one of the numbered workspaces, and it appears as a dimmed overlay on the active output. Its windows use the global sweets.gaps{ ... } values.

While it is open and focused, the layout actions (layout, cycle_layout, master_position, cycle_master_position) and the resize controls act on the special workspace, so it can hold its own arrangement independent of the numbered workspace beneath it.

The special workspace stacks above regular windows and top-layer surfaces such as panels, but below overlay-layer surfaces. Launchers like rofi (which use the overlay layer by default) open in front of it and stay clickable while it is open; apps launched from them land on the special workspace.

sweets.special{ ... }

Controls the private special workspace overlay.

sweets.special {
  dim = "#000000A6",
}
KeyTypeDefaultDescription
dimstring#000000A6Dimmer color behind special workspace windows, #RRGGBB[AA]

The alpha channel controls the dim strength. For example, #00000080 is a medium black dim, while #181825CC gives a stronger tinted overlay.

sweets.gaps{ ... }

Controls the spacing around tiled windows.

sweets.gaps {
  inner = 8,
  outer = 8,
  smart = false,
}
KeyTypeDefaultDescription
innerint8Pixels between tiled windows, 0–4096
outerint8Pixels between tiled windows and usable-area edges, 0–4096
smartboolfalseRemove outer gaps when exactly one tiled window is visible

Smart gaps count tiled windows on the active workspace. Floating windows do not affect the count. With one tiled window, only the outer margin is removed; with two or more, the configured inner and outer gaps apply normally.

sweets.border{ ... }

Controls the border drawn around each window.

sweets.border {
  width = 2,
  focused = "#C27AFF",
  unfocused = "#333338",
  urgent = "#F7768E",
}
KeyTypeDefaultDescription
widthint2Window border thickness in pixels, 0–64 (0 disables)
focusedstring#C27AFFBorder color of the focused window
unfocusedstring#333338Border color of unfocused windows
urgentstring#F7768EBorder color of an unfocused window demanding attention

A window demanding attention (an X11 window setting the ICCCM urgency hint or _NET_WM_STATE_DEMANDS_ATTENTION) is drawn with the urgent color instead of stealing focus. Focusing the window clears the state.

Colors are #RRGGBB or #RRGGBBAA hex (the # is optional); a missing alpha is opaque.

sweets.tabbar{ ... }

Styles the tab strip drawn by the tabbed layout. It has no effect on other layouts.

sweets.tabbar {
  height = 24,
  font = "Sans 10",
  active_bg = "#C27AFF",
  active_fg = "#FFFFFF",
  inactive_bg = "#212127",
  inactive_fg = "#B7B7C2",
  urgent_bg = "#F7768E",
  urgent_fg = "#FFFFFF",
}
KeyTypeDefaultDescription
heightint24Tab strip height in pixels, 8–256
fontstringSans 10Pango font description for tab titles
active_bgstring#C27AFFBackground of the focused tab
active_fgstring#FFFFFFTitle color of the focused tab
inactive_bgstring#212127Background of unfocused tabs
inactive_fgstring#B7B7C2Title color of unfocused tabs
urgent_bgstring#F7768EBackground of a tab whose window wants attention
urgent_fgstring#FFFFFFTitle color of an urgent tab

Titles are ellipsized to fit their tab and rendered at the output scale, so they stay crisp on HiDPI displays. If the usable area is too short to fit the strip plus a window, the strip is dropped and the layout falls back to a plain full-area stack. The urgent color applies to a background tab only; focusing it clears the urgency, so the focused tab always uses active_*.

font is a Pango font description such as "JetBrains Mono 11" or "Sans Bold 10". Colors are #RRGGBB or #RRGGBBAA hex (the # is optional); a missing alpha is opaque.

On this page