Skip to main content

max / balanced_breakfast

Adopt GoingsOn's utility layer The two sheets now carry an identical set of layout and text primitives, so markup ports between the apps without translation. BB gains w-full, the bound gap tier, and the text size and colour utilities it lacked (text-xs, text-base, text-muted, text-secondary, text-danger, text-center, text-left). Size and colour compose rather than pairing into a class each. Five classes that were pure utility combinations retire onto them: article-time, form-row, source-actions, sync-toggle-label and the unused source-name--muted. These are applied through className rather than a class attribute here, so the JS assignments moved with the rules. .text-sm-secondary is gone from both apps; it was defined differently in each and the two definitions disagreed with each other. .row stays the list-row component, which is what it means here and in audiofiles. GoingsOn gave up its conflicting flex utility of that name. Classes 188 -> 192, and the shared vocabulary is 75 names.
Author: Max Johnson <me@maxj.phd> · 2026-07-28 20:59 UTC
Signed with PGP, not checked
Commit: a1416190082fd3ca23c1687bc8cda04299dac3ed
Parent: 577927c
6 files changed, +22 insertions, -22 deletions
@@ -241,13 +241,19 @@
241 241 BB uses `.row` as the renderRow COMPONENT base (see ยง Component Primitives).
242 242 For a raw centered flex row use `.row-flex`; for a column use `.stack`.
243 243 ========================================================================== */
244 - /* Layout primitives */
244 + /* Layout primitives. Gap tiers carry makeover-geometry's relational names:
245 + bound, peer, group, section. Identical to GoingsOn's set, so markup ports
246 + between the apps without translation. `.row` is NOT here: it is the list-row
247 + component below, which is what it means in GoingsOn and in audiofiles too. */
245 248 .flex-1 { flex: 1; }
249 + .w-full { width: 100%; }
246 250 .stack { display: flex; flex-direction: column; }
251 + .stack-bound { gap: var(--gap-bound); }
247 252 .stack-peer { gap: var(--gap-peer); }
248 253 .stack-group { gap: var(--gap-group); }
249 254 .stack-section { gap: var(--gap-section); }
250 255 .row-flex { display: flex; align-items: center; }
256 + .row-flex-bound { gap: var(--gap-bound); }
251 257 .row-flex-peer { gap: var(--gap-peer); }
252 258 .row-flex-group { gap: var(--gap-group); }
253 259 .row-flex-section { gap: var(--gap-section); }
@@ -256,9 +262,16 @@
256 262 .mb-section { margin-bottom: var(--gap-section); }
257 263 .mb-peer { margin-bottom: var(--gap-peer); }
258 264
259 - /* Text */
265 + /* Text: size and colour compose, e.g. `text-sm text-muted`, rather than a
266 + class per pairing. Same set as GoingsOn. */
267 + .text-xs { font-size: var(--font-size-xs); }
260 268 .text-sm { font-size: var(--font-size-sm); }
261 - .text-sm-secondary { font-size: var(--font-size-sm); color: var(--content-secondary); }
269 + .text-base { font-size: var(--font-size-base); }
270 + .text-muted { color: var(--content-muted); }
271 + .text-secondary { color: var(--content-secondary); }
272 + .text-danger { color: var(--danger); }
273 + .text-center { text-align: center; }
274 + .text-left { text-align: left; }
262 275
263 276 /* === 4. Layout shell: body & #app ==================================== */
264 277 body {
@@ -493,7 +506,6 @@
493 506 color: var(--danger);
494 507 font-size: var(--font-size-md);
495 508 }
496 - .article-time { color: var(--content-muted); font-size: var(--font-size-sm); }
497 509
498 510 /* Title: two-line clamp (overrides .row-primary's single flex line). */
499 511 .row--article .row-primary {
@@ -751,11 +763,6 @@
751 763 box-shadow: var(--bevel-inset);
752 764 }
753 765 /* .form-row: horizontal grouping of two related fields (e.g. label + button) */
754 - .form-row {
755 - display: flex;
756 - gap: var(--gap-peer);
757 - align-items: center;
758 - }
759 766
760 767 .form-actions {
761 768 display: flex;
@@ -886,7 +893,6 @@
886 893 .source-item-empty-link { color: var(--category-four); }
887 894
888 895 /* sources.js "+ Saved Filter" button label. Was inline color. */
889 - .source-name--muted { color: var(--content-muted); }
890 896
891 897 /* settings-sync.js renderConnect block. Was three inline styles. */
892 898 .sync-connect {
@@ -1328,11 +1334,6 @@
1328 1334 font-size: var(--font-size-base);
1329 1335 }
1330 1336
1331 - .source-actions {
1332 - display: flex;
1333 - align-items: center;
1334 - gap: var(--gap-bound);
1335 - }
1336 1337
1337 1338 .source-count {
1338 1339 background-color: var(--surface-page);
@@ -1616,7 +1617,6 @@
1616 1617 .sync-manual-entry { margin-top: var(--gap-section); }
1617 1618 .sync-manual-form { margin-top: var(--gap-peer); }
1618 1619 .sync-action-spaced { margin-bottom: var(--gap-section); }
1619 - .sync-toggle-label { display: flex; align-items: center; gap: var(--gap-bound); }
1620 1620 .sync-disconnect { margin-top: var(--gap-section); }
1621 1621
1622 1622 /* === 30. Reading list / Bookmarks ====================================== */
@@ -391,7 +391,7 @@
391 391 dataGroup.appendChild(dataLabel);
392 392 // F4 (2026-06-02): inline layout retired in favor of .form-row primitive.
393 393 const dataActions = document.createElement('div');
394 - dataActions.className = 'form-row';
394 + dataActions.className = 'row-flex row-flex-peer';
395 395
396 396 const importBtn = document.createElement('button');
397 397 importBtn.className = 'btn btn-sm';
@@ -76,7 +76,7 @@
76 76 meta.appendChild(author);
77 77 }
78 78 const time = document.createElement('span');
79 - time.className = 'article-time';
79 + time.className = 'text-sm text-muted';
80 80 time.textContent = bm.timeAgo;
81 81 meta.appendChild(time);
82 82 header.appendChild(meta);
@@ -142,7 +142,7 @@
142 142 author.className = 'article-author';
143 143 author.textContent = item.author;
144 144 const time = document.createElement('span');
145 - time.className = 'article-time';
145 + time.className = 'text-sm text-muted';
146 146 time.textContent = item.timeAgo;
147 147 meta.appendChild(author);
148 148 meta.appendChild(time);
@@ -360,7 +360,7 @@
360 360 const toggleGroup = document.createElement('div');
361 361 toggleGroup.className = 'form-group';
362 362 const toggleLabel = document.createElement('label');
363 - toggleLabel.className = 'sync-toggle-label';
363 + toggleLabel.className = 'row-flex row-flex-bound';
364 364 const toggle = document.createElement('input');
365 365 toggle.type = 'checkbox';
366 366 toggle.checked = status.autoSyncEnabled;
@@ -53,7 +53,7 @@
53 53 /** Wrap trailing controls in a `.source-actions` cluster (meta slot). */
54 54 function actionsEl(children) {
55 55 const wrap = document.createElement('span');
56 - wrap.className = 'source-actions';
56 + wrap.className = 'row-flex row-flex-bound';
57 57 children.forEach(c => c && wrap.appendChild(c));
58 58 return wrap;
59 59 }
@@ -246,7 +246,7 @@
246 246 // "+ Saved Filter" button
247 247 const addQfBtn = document.createElement('li');
248 248 addQfBtn.className = 'source-item add-query-feed-btn';
249 - addQfBtn.innerHTML = '<span class="source-name source-name--muted" title="Create a dynamic feed from search filters">+ Saved Filter</span>';
249 + addQfBtn.innerHTML = '<span class="source-name text-muted" title="Create a dynamic feed from search filters">+ Saved Filter</span>';
250 250 addQfBtn.onclick = () => BB.queryFeeds.openBuilder(null);
251 251 frag.appendChild(addQfBtn);
252 252