Composition recipes
Ready-to-use examples of layout composition. Each recipe shows working code you can adapt.
Centered tall at custom ratio
Scale the tall layout to 70% of screen size, centered:
local Modifiers = require("layouts.modifiers")local Centered = require("layouts.modifiers.centered")local Tall = require("layouts.tall")
local centeredTall = Modifiers.wrap(Tall, Centered.ratio(0.7), { name = "tall-centered", displayName = "Tall (Centered)",})
spoon.Shoji:configure({ layouts = { centeredTall }, enabled_layouts = { "tall", "tall-centered" },})Adjust the ratio parameter (0.0–1.0) to control how much screen space the layout uses. Useful on ultrawide monitors where full-width windows feel too stretched.
Mirrored wide (main on bottom)
Flip the wide layout vertically so the main window is at the bottom:
local Modifiers = require("layouts.modifiers")local Mirror = require("layouts.modifiers.mirror")local Wide = require("layouts.wide")
local wideBottom = Modifiers.wrap(Wide, Mirror.vertical(), { name = "wide-bottom", displayName = "Wide (Bottom)",})
spoon.Shoji:configure({ layouts = { wideBottom }, enabled_layouts = { "wide", "wide-bottom" },})The main area moves to the bottom, stack windows appear along the top. Good for setups where you want your primary window at eye level.
Gaps with per-edge values
Add custom spacing around and between windows:
local Modifiers = require("layouts.modifiers")local Gaps = require("layouts.modifiers.gaps")local Tall = require("layouts.tall")
-- Uniform gapslocal uniformGaps = Modifiers.wrap(Tall, Gaps.create({ outer = 16, inner = 8,}), { name = "tall-gaps", displayName = "Tall (Gaps)",})
-- Per-edge outer gaps (more space on sides than top/bottom)local sideGaps = Modifiers.wrap(Tall, Gaps.create({ outer = { left = 40, right = 40, top = 10, bottom = 10 }, inner = 8,}), { name = "tall-side-gaps", displayName = "Tall (Side Gaps)",})
spoon.Shoji:configure({ layouts = { uniformGaps, sideGaps },})Per-edge gaps let you compensate for menu bars, docks, or asymmetric monitor bezels. Unspecified edges default to 0.
Partition for IDE layout
Split the screen with dedicated regions for different purposes:
local Partition = require("layouts.combinators.partition")local Tall = require("layouts.tall")local Column = require("layouts.column")
local ideLayout = Partition.horizontal({ { ratio = 0.65, layout = Tall, count = 1 }, { ratio = 0.35, layout = Column },}, { name = "ide", displayName = "IDE",})
spoon.Shoji:configure({ layouts = { ideLayout }, enabled_layouts = { "tall", "ide" },})Window 1 gets the left 65% (editor). Remaining windows stack in columns on the
right (terminal, browser, docs). Adjust count to assign more windows to the
main region.
Presentation layout
Vertical partition with a large main area and a row of supporting windows:
local Partition = require("layouts.combinators.partition")local Monocle = require("layouts.monocle")local Column = require("layouts.column")
local presentLayout = Partition.vertical({ { ratio = 0.85, layout = Monocle, count = 1 }, { ratio = 0.15, layout = Column },}, { name = "presentation", displayName = "Presentation",})
spoon.Shoji:configure({ layouts = { presentLayout },})Top 85% shows one window full-width. Bottom 15% shows supporting windows in a row.
IfMax for adaptive layouts
Switch layouts automatically based on window count:
local IfMax = require("layouts.combinators.if_max")local Monocle = require("layouts.monocle")local Tall = require("layouts.tall")
-- Use monocle for 1-2 windows, tall for 3+local adaptive = IfMax.create(2, Monocle, Tall, { name = "adaptive", displayName = "Adaptive",})
spoon.Shoji:configure({ layouts = { adaptive }, enabled_layouts = { "adaptive", "tall" },})With 1–2 windows, you get focused full-screen viewing. Open a third window and tall layout takes over automatically.
Focused coding layout
Start monocle for deep work, switch to BSP when context-switching:
local IfMax = require("layouts.combinators.if_max")local Monocle = require("layouts.monocle")local BSP = require("layouts.bsp")
local focusedCoding = IfMax.create(1, Monocle, BSP, { name = "focused", displayName = "Focused",})
spoon.Shoji:configure({ layouts = { focusedCoding },})Single window? Full focus. Multiple windows? BSP distributes them evenly.
Combining modifiers and combinators
Wrap combined layouts with modifiers for full customization:
local Modifiers = require("layouts.modifiers")local Gaps = require("layouts.modifiers.gaps")local IfMax = require("layouts.combinators.if_max")local Monocle = require("layouts.monocle")local Tall = require("layouts.tall")
-- Create adaptive layout firstlocal adaptive = IfMax.create(2, Monocle, Tall, { name = "adaptive-base",})
-- Wrap with gaps modifierlocal gappedAdaptive = Modifiers.wrap(adaptive, Gaps.create({ outer = 12, inner = 8,}), { name = "adaptive-gapped", displayName = "Adaptive (Gaps)",})
spoon.Shoji:configure({ layouts = { gappedAdaptive },})Modifiers apply after the combinator arranges windows, so gaps affect the final result regardless of which child layout is active.