Sail Hub Docs Sail Hub

Styling

Your stylesheets are applied inside your layout's own scope, so they only style Sail while your layout is on, and are gone the moment someone switches away.

Writing the CSS#

  • A plain selector, like #gameGrid .game-card, matches inside the page.
  • & is the page itself: &.sl-opt-shelf (an option is on), &.sl-hero (the hero game page is in use), &.sl-props-on (the properties window is in use).
  • Use relative paths for your own files: url("images/bg.webp").
  • Sail's reduced-motion setting already stops transitions. If you move things on hover, keep them still with &.reduce-motion.
/* Rounded covers with an accent ring */
#gameGrid .game-card { border-radius: 12px !important; overflow: hidden; }
#gameGrid .game-card:hover { box-shadow: 0 0 0 2px var(--sl-accent) !important; }

/* Hide Sail's "Continue playing" card while the recently played row is on */
&.sl-opt-shelf [data-sl-region="continue"] { display: none !important; }

/* Keep covers still for people who turned motion down */
&.reduce-motion #gameGrid .game-card:hover { transform: none !important; }

Parts of Sail you can style#

While a layout is on, the engine names Sail's main parts with data-sl-region and its page tabs with data-sl-tab. These names stay the same when Sail changes inside.

RegionSail's elementRegionSail's element
titlebar.custom-titlebargamepage#gamePageView
sidebar#mainSidebarhero.gp-header
nav#sidebarNavHeaderheroTitle.gp-header-content
main#mainScrolleractionbar.gp-action-bar
friends#socialSidebarplay#gpPlayBtn
home#libraryViewstop#gpStopBtn
grid#gameGridmanage#gameSettingsModal
continue#continuePlayingCard

Tabs: [data-sl-tab="library"], downloads, mods, workshop, downloadsmgr, achievements, maintenance.

When a rule doesn't win

Sail styles some of these by id with !important. A region attribute can't outweigh that, so for those use the id itself and !important, for example #mainSidebar.sidebar { width: 76px !important; }. The ids in the table are kept stable on purpose, and Sail checks them in its tests.

The engine's own parts#

The parts the engine builds have their own classes:

BlockClasses
titleBar.sl-menubar, .sl-menubar-btn, .sl-menu, .sl-menu-item
tabs.sl-top-nav, .sl-more
gameList.sl-list, .sl-row, .sl-group, .sl-search
home.sl-shelf, .sl-shelf-card, .sl-shelf-art, .sl-shelf-when, .sl-all-title
gamePage.sl-stats, .sl-stat
manageGame.sl-props, .sl-props-nav, .sl-props-tab
profilePage.sl-profile and its .sl-pf-* sections
bottomBar.sl-bottom, .sl-bottom-btn
Always.sl-foot (the Layouts gear at the bottom of the left panel)

What a layout can't do#

These keep layouts safe to install from anyone. A layout that breaks one is refused, and Sail stays as it was.

  • No scripts or code of any kind, and no @import.
  • Nothing from the internet: url() may only point at files inside your layout (or inline data: images and fonts), so a layout can't see who uses it or change what it loads later.
  • No reaching outside its scope: braces that don't match, which would style all of Sail, are refused.
  • No links (symlinks), and nothing outside the allowed file types.
  • Text from the manifest (tab names, labels) is always shown as text, never as HTML.

If you need code, make a normal plugin instead.

Last updated 20 October 2026