docs(design): center search in a 3-zone top bar

The single-row top bar was a flat flex row, which made the search field
drift left or right depending on how wide the surrounding clusters were.
Rework it as a 3-column CSS grid (left zone, search, right zone) so the
search input lands at the geometric center of the window regardless of
the side-zone widths.

Mechanics:

- `.topbar-single` becomes `display: grid` with
  `grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr)` and a 16 px
  column gap. The middle (auto) column holds only the search, which is
  capped at `flex: 0 1 360px` so it cannot push into the side columns.
- The left and right zones are flex containers with
  `justify-content: space-between`, so brand pins far-left while filter
  pills hug the search, and sort hugs the search while the kebab pins
  far-right. The filter pills are now grouped semantically with the
  search (they scope it) instead of floating next to the brand.
- A `@container launcher (max-width: 1100px)` rule collapses the layout
  back to a single nowrap flex row at narrow widths — the geometric
  centering stops reading at narrow widths and would otherwise force
  awkward truncation, so we abandon it rather than fight it.
- The `.launcher` root opts into container queries via
  `container-type: inline-size; container-name: launcher`.

`launcher.jsx` now wraps the existing children in `.topbar-left`,
`.topbar-center`, `.topbar-right` (plus a `.topbar-left-trail` /
`.topbar-right-lead` for the inner space-between alignment), but each
control component is otherwise untouched.

The README's "Top bar" section is rewritten to spec the new layout, and
a new "Changes since v2" section calls it out at the top of the
handoff. The game-directory button line is left as-is in this commit
and addressed separately.

Test Plan

- Open `design_reference/SoftLAN Launcher.html` in a static server and
  inspect variant A at full width: the search input's horizontal center
  should match the window's horizontal center, regardless of accent /
  density choices in the Tweaks panel.
- Shrink the launcher artboard below 1100 px and confirm the row
  collapses to a single left-to-right strip with no overlap.
This commit is contained in:
2026-05-21 18:00:55 +02:00
parent 8d96d99160
commit 6e28f736e8
3 changed files with 108 additions and 28 deletions
+20 -10
View File
@@ -28,7 +28,7 @@ function Launcher({
initialOpenGame = null,
initialSettingsOpen = false,
}) {
const { density, aspect, accent, bg } = tweaks;
const { density, aspect, accent, bg, gameFolderSet } = tweaks;
const [filter, setFilter] = useState(initialFilter);
const [sort, setSort] = useState(initialSort);
const [query, setQuery] = useState(initialQuery);
@@ -51,15 +51,25 @@ function Launcher({
style={{ '--accent': accent }}>
{variant === 'single' ? (
<header className="topbar topbar-single">
<div className="brand">
<div className="brand-mark" style={{ background: accent }}>S</div>
<div className="brand-name">SoftLAN</div>
<div className="topbar-left">
<div className="brand">
<div className="brand-mark" style={{ background: accent }}>S</div>
<div className="brand-name">SoftLAN</div>
</div>
<div className="topbar-left-trail">
<SegmentedFilters value={filter} onChange={setFilter} counts={counts} accent={accent}/>
</div>
</div>
<div className="topbar-center">
<SearchField value={query} onChange={setQuery} accent={accent} wide/>
</div>
<div className="topbar-right">
<div className="topbar-right-lead">
<SortMenu value={sort} onChange={setSort} accent={accent}/>
</div>
<DirectoryButton path={gameFolderSet === false ? null : DIR_PATH}/>
<KebabMenu items={menuItems}/>
</div>
<SegmentedFilters value={filter} onChange={setFilter} counts={counts} accent={accent}/>
<SearchField value={query} onChange={setQuery} accent={accent} wide/>
<SortMenu value={sort} onChange={setSort} accent={accent}/>
<DirectoryButton path={DIR_PATH}/>
<KebabMenu items={menuItems}/>
</header>
) : (
<header className="topbar topbar-two">
@@ -68,7 +78,7 @@ function Launcher({
<div className="brand-mark" style={{ background: accent }}>S</div>
<div className="brand-name">SoftLAN <span className="brand-name-soft">Launcher</span></div>
</div>
<DirectoryButton path={DIR_PATH}/>
<DirectoryButton path={gameFolderSet === false ? null : DIR_PATH}/>
<div className="topbar-row1-right">
<StorageMeter accent={accent}/>
<KebabMenu items={menuItems}/>