Sail Hub Docs Sail Hub

Layout format

Everything a layout says about itself lives in plugin.json. This page is the full reference.

plugin.json#

{
  "id": "yourname.my-layout",
  "name": "My Layout",
  "version": "1.0.0",
  "author": "Your name",
  "description": "What it looks like, in a sentence.",
  "type": "layout",
  "requires": {
    "aseoriy.library-layouts": { "version": "^2.1.0", "hub": "b2ac4147-faca-4770-87d2-b429a8fa5abf" }
  },
  "settings": [
    { "id": "shelf", "type": "toggle", "label": "Recently played row", "default": true }
  ],
  "layout": {
    "contract": 2,
    "styles": ["layout.css"],
    "preview": "preview.png",
    "tokens": { "--sl-accent": "#ff7a59" },
    "labels": { "recentGames": "Jump back in" },
    "blocks": {
      "home": { "covers": "portrait", "shelf": { "setting": "shelf", "values": { "true": "recent", "false": "none" } } },
      "gamePage": { "layout": "hero", "stats": ["playtime", "lastPlayed"] },
      "manageGame": "properties",
      "profilePage": true
    }
  }
}
  • id is required: lowercase, yourname.layout-name, and never reused for something else. It's how Sail tells layouts apart and finds updates.
  • type: "layout" marks it as a layout. A layout with a main script is refused.
  • requires tells Sail to offer Library Layouts to anyone who installs your layout without it. Keep it as it is.
  • contract is the version of the format you wrote for. The current one is 2. Library Layouts still shows contract 1 layouts, and tells people when a layout needs a newer engine.
  • styles lists your stylesheets (up to 8). preview points at an optional picture of your layout; Library Layouts doesn't show it yet.

Building blocks#

Each block is a part the engine builds. Leave a block out and Sail's own interface stays in that spot, so you can start small. Unknown blocks and values are ignored (developer mode lists them), so a newer layout still works on an older engine.

BlockValuesWhat it builds
titleBarrows: 1 or 2
menu: "full" or "none"
sailButtons: "hidden" or "shown"
A menu bar (Sail, View, Friends, Games, Help) holding every Sail button. With 2 rows the page tabs get their own row; with 1 they share the bar. sailButtons: "hidden" moves Sail's buttons (Add Game, Settings and so on) into the menu; with menu: "none" they always stay, so nobody is left without them.
tabsposition: "titleBar"
names: { "downloads": "Store" }
order: tabs to show first, in that order (the rest follow)
hidden: tabs to hide
Moves Sail's page tabs up into the title bar. Tabs: library, downloads, mods, workshop, downloadsmgr, achievements, maintenance.
gameListposition: "left"
filter, search: true or false
Your games listed in the left panel, with a filter and a search box.
homecovers: "wide" or "portrait"
shelf: "none" or "recent"
header: "none" or "count"
Tall covers, a recently played row, and an "All Games (n)" heading on the home page.
gamePagelayout: "sail" or "hero"
stats: any of lastPlayed, playtime, achievements
A full-width hero image with the title on it, and stats next to the Play button.
manageGame"properties"Turns Manage Game into a properties window with its pages listed on the left.
profilePagetrueA profile page for the signed-in account, opened from the account menu on Sail's profile button.
bottomBaritems: any of addGame, downloads, friendsA bar along the bottom with those buttons.
friendsPanel"shown" or "hidden"Whether Sail's friends list starts shown on the right. People can still open it from the bottom bar's Friends button or the menu bar's Friends menu, so include one of those if you hide it.

Options people can change#

Options are your manifest's normal settings (toggle, select, number, text, heading, the same as plugin settings). Sail draws them in its usual settings window and keeps them on each PC. Your CSS and blocks can follow them:

  • A toggle that's on adds the class sl-opt-<id> to the page, so &.sl-opt-shelf in your CSS means "while that toggle is on".
  • A choice adds sl-opt-<id>-<value>, for example &.sl-opt-accent-mint.
  • A number becomes a CSS variable, var(--sl-opt-<id>).
  • A block value can follow an option: { "setting": "covers", "values": { "true": "portrait", "false": "wide" } }. Without values, the option's own value is used.
  • A whole block can be switched with "when": "<id>" (built only while that toggle is on) or "unless": "<id>".

Colours and fonts#

layout.tokens sets CSS variables while your layout is on. Names must start with --sl-, and values are plain text (no url(), no ;). The engine's parts use these, among others:

TokenUsed for
--sl-bg, --sl-surface, --sl-surface-strongPage background and panels
--sl-top, --sl-listThe title bar and the game list
--sl-text, --sl-text-bright, --sl-text-strong, --sl-muted, --sl-faintText, from normal to quiet
--sl-accent, --sl-accent-2, --sl-accent-darkHighlights, the selected game, buttons
--sl-menu-bg, --sl-menu-hover, --sl-menu-line, --sl-menu-borderMenus
--sl-art, --sl-letter, --sl-field, --sl-lineCover placeholders, search fields, divider lines
--sl-good, --sl-warn, --sl-runningInstalled, needs attention, running
--sl-fontThe font for everything the engine draws

To colour the rest of Sail too, set Sail's own variables (--bg-color, --card-bg, --text-color, --border-color, --accent) in your CSS, as the starter does.

Words#

layout.labels replaces words the engine draws. Text only, up to 60 characters each.

LabelDefault
home, searchHome, Search
recentGames, allGamesRecent Games, All Games ({count}). {count} is the number of games.
today, yesterday, thisWeek, thisMonth, earlierToday, Yesterday, This week, This month, Earlier
lastPlayed, playtime, achievementsLast played, Play time, Achievements
pageGeneral, pageCompat, pageSaves, pageFiles, pageLibraryThe properties window's pages: General, Compatibility, Saves, Installed Files, Library
addGame, downloads, friendsAdd a Game, Downloads, Friends & Chat

Last updated 21 October 2026