Skip to main content

max / balanced_breakfast

12.1 KB · 397 lines History Blame Raw
1 /* === 20. Sources list: sidebar entries ================================
2 Charter target (F5): .source-item collapses onto .card --row.
3 ========================================================================== */
4 .sources-list { padding: var(--gap-bound) 0; }
5
6 .source-item {
7 display: flex;
8 justify-content: space-between;
9 align-items: center;
10 padding: var(--gap-peer) var(--gap-group);
11 cursor: pointer;
12 min-height: 2.5rem;
13 /* 3px, wider than a frame on purpose: this edge is the state marker. */
14 border-left: 3px solid transparent;
15 }
16 .source-item:hover { background-color: var(--surface-sunken); }
17 .source-item.active {
18 background-color: var(--surface-sunken);
19 border-left-color: var(--category-four);
20 }
21
22 .source-name {
23 flex: 1;
24 white-space: nowrap;
25 overflow: hidden;
26 text-overflow: ellipsis;
27 font-weight: 600;
28 font-size: var(--font-size-base);
29 }
30
31
32 /* Carries `.raised`; same as `.tag`, the fill and the bevel are the design
33 system's now. */
34 .source-count {
35 padding: var(--step-hair) var(--step-snug);
36 border-radius: var(--radius-xl);
37 font-size: var(--font-size-xs);
38 color: var(--content-muted);
39 border: var(--border-width) solid var(--border);
40 min-width: 1.8rem;
41 text-align: center;
42 }
43 .source-count.all-read {
44 color: var(--success);
45 border-color: var(--success);
46 }
47
48 .source-delete {
49 display: none;
50 background: none;
51 border: none;
52 color: var(--content-muted);
53 cursor: pointer;
54 font-size: var(--font-size-base);
55 padding: 0 var(--step-tight);
56 line-height: 1;
57 }
58 .source-delete:hover { color: var(--danger); }
59 .source-item:hover .source-delete { display: inline; }
60
61 /* === 20. Sources: tag filter bar ====================================== */
62 .tag-filter-bar {
63 display: flex;
64 flex-wrap: wrap;
65 gap: var(--gap-peer);
66 padding: var(--gap-peer) var(--gap-group);
67 border-bottom: var(--border-width) solid var(--border);
68 background-color: var(--surface-overlay);
69 }
70 /* Carries `.chip`, which is what a filter chip is: raised at rest, hover,
71 press, and a `.latched` state for the one that is on. The fill and both
72 bevels come from layout.css; the pill shape, the type and the accent are
73 this app's. */
74 .tag-chip {
75 border: var(--border-width) solid var(--border);
76 border-radius: var(--radius-xl);
77 padding: var(--step-hair) var(--step-base);
78 font-size: var(--font-size-xs);
79 color: var(--content-secondary);
80 cursor: pointer;
81 white-space: nowrap;
82 }
83 .tag-chip:hover { border-color: var(--category-four); color: var(--content); }
84 /* An active filter chip is latched down, same inversion as a pressed button.
85 The inset bevel comes from `.latched`, which the markup sets alongside
86 `.active`; the accent fill stays here, because which colour a latched chip
87 takes is this app's call and the depth is not. */
88 .tag-chip.active {
89 background-color: var(--category-four);
90 border-color: var(--category-four);
91 color: var(--content);
92 font-weight: 600;
93 }
94 .source-tags {
95 display: flex;
96 flex-wrap: wrap;
97 gap: var(--gap-peer);
98 margin-top: var(--step-hair);
99 }
100 /* Source-row tags use .tag.tag--xs[data-color="muted"] (see § Tag). */
101
102 /* === 20. Sources: edit button ========================================= */
103 .source-edit {
104 display: inline;
105 background: none;
106 border: none;
107 color: var(--content-muted);
108 cursor: pointer;
109 font-size: var(--font-size-md);
110 padding: 0 var(--step-tight);
111 line-height: 1;
112 opacity: 0.4;
113 }
114 .source-edit:hover { color: var(--category-four); opacity: 1; }
115
116 /* === 22. Reader-expanded mode =========================================
117 Hides items panel and expands detail to full width. */
118 .main.reader-expanded .items-panel {
119 display: none;
120 }
121
122 .main.reader-expanded .detail-panel {
123 flex: 1;
124 width: auto;
125 min-width: 0;
126 border-left: none;
127 }
128
129 .main.reader-expanded .item-detail {
130 max-width: 800px;
131 margin: 0 auto;
132 }
133
134 .reader-back-btn {
135 margin-right: auto;
136 }
137
138 /* === 23. Query feeds: sidebar divider ================================ */
139 .source-divider {
140 padding: var(--gap-peer) var(--gap-group) var(--gap-bound);
141 font-size: var(--font-size-xxs);
142 font-weight: 600;
143 color: var(--content-muted);
144 text-transform: uppercase;
145 letter-spacing: 0.5px;
146 border-top: var(--border-width) solid var(--border);
147 margin-top: var(--gap-bound);
148 cursor: default;
149 }
150
151 /* Query feed items: subtle visual distinction */
152 .source-item.query-feed .source-name {
153 font-style: italic;
154 }
155
156 /* Add query feed button */
157 .source-item.add-query-feed-btn {
158 padding: var(--gap-peer) var(--gap-group);
159 border-top: var(--border-width) solid var(--border);
160 }
161 .source-item.add-query-feed-btn:hover {
162 background-color: var(--surface-sunken);
163 }
164
165 /* === 23. Query feeds: builder =========================================
166 `.condition-row` is the canonical NAMED LAYOUT for mixed-intent rows
167 (see compose-first rule, named-layout exception).
168 ========================================================================== */
169 .qf-builder .form-group { margin-bottom: var(--gap-section); }
170
171 .condition-row {
172 display: flex;
173 align-items: center;
174 gap: var(--gap-bound);
175 margin-bottom: var(--gap-peer);
176 }
177 .condition-row .field {
178 flex: 1;
179 min-width: 0;
180 font-size: var(--font-size-md);
181 padding: var(--gap-bound) var(--gap-peer);
182 }
183 .condition-row .cond-field { flex: 0 0 auto; width: 90px; }
184 .condition-row .cond-op { flex: 0 0 auto; width: 120px; }
185 .condition-row .cond-val { flex: 1; }
186 .condition-row .cond-remove {
187 flex-shrink: 0;
188 padding: var(--step-tight) var(--step-snug);
189 font-size: var(--font-size-lg);
190 line-height: 1;
191 }
192
193 .add-condition-btn {
194 margin-top: var(--gap-peer);
195 }
196
197 .match-preview {
198 padding: var(--gap-peer) 0;
199 font-size: var(--font-size-md);
200 color: var(--content-muted);
201 }
202
203 .modal-actions {
204 display: flex;
205 justify-content: flex-end;
206 gap: var(--gap-peer);
207 margin-top: var(--gap-section);
208 padding-top: var(--gap-section);
209 border-top: var(--border-width) solid var(--border);
210 }
211
212 /* === 24. Sidebar saved articles ======================================== */
213 .sidebar-saved {
214 display: flex;
215 justify-content: space-between;
216 align-items: center;
217 padding: var(--gap-peer) var(--gap-group);
218 cursor: pointer;
219 border-top: var(--border-width) solid var(--border);
220 /* 3px, wider than a frame on purpose: this edge is the state marker. */
221 border-left: 3px solid transparent;
222 min-height: 2.5rem;
223 font-size: var(--font-size-base);
224 font-weight: 600;
225 }
226 .sidebar-saved:hover { background-color: var(--surface-sunken); }
227 .sidebar-saved.active {
228 background-color: var(--surface-sunken);
229 border-left-color: var(--category-four);
230 }
231
232 /* Sidebar footer */
233 .sidebar-footer {
234 margin-top: auto;
235 padding: var(--gap-peer) var(--gap-group);
236 border-top: var(--border-width) solid var(--border);
237 }
238
239 /* F2 additions (2026-06-02): utility primitives.
240 .hidden: visibility toggle utility (replaces ad-hoc style.display = 'none').
241 .confirm-message: retires the inline-style violation in
242 components.js confirmAction (was setting marginBottom, fontSize, color
243 via style.cssText). */
244 .hidden { display: none !important; }
245 .confirm-message {
246 margin-bottom: var(--gap-section);
247 font-size: var(--font-size-base);
248 color: var(--content);
249 }
250
251 /* === 26. Help shortcuts ================================================ */
252 .help-shortcuts {
253 display: grid;
254 grid-template-columns: 1fr 1fr;
255 gap: var(--gap-pane);
256 }
257 .help-shortcuts .help-section:last-child {
258 grid-column: 1 / -1;
259 }
260 .help-section h3 {
261 font-size: var(--font-size-sm);
262 font-weight: 600;
263 color: var(--content-muted);
264 text-transform: uppercase;
265 letter-spacing: 0.5px;
266 margin-bottom: var(--gap-peer);
267 }
268 .help-row {
269 display: flex;
270 align-items: center;
271 gap: var(--gap-bound);
272 margin-bottom: var(--gap-peer);
273 font-size: var(--font-size-base);
274 }
275 .help-row kbd {
276 background: var(--surface-raised);
277 border: var(--border-width) solid var(--border);
278 border-radius: var(--radius-sm);
279 box-shadow: var(--bevel-raised);
280 padding: var(--step-hair) var(--step-snug);
281 font-family: inherit;
282 font-size: var(--font-size-md);
283 min-width: 1.4rem;
284 text-align: center;
285 color: var(--content);
286 }
287 .help-row span { color: var(--content-secondary); margin-left: auto; }
288
289 /* === 27. Welcome modal ================================================= */
290 .welcome-content h3 {
291 font-size: var(--font-size-base);
292 font-weight: 600;
293 color: var(--content);
294 margin-top: var(--gap-section);
295 margin-bottom: var(--gap-bound);
296 }
297 .welcome-content h3:first-child { margin-top: 0; }
298 .welcome-content p {
299 font-size: var(--font-size-base);
300 color: var(--content-secondary);
301 line-height: 1.5;
302 }
303 .welcome-cta {
304 display: flex;
305 justify-content: center;
306 gap: var(--gap-peer);
307 margin-top: var(--gap-section);
308 padding-top: var(--gap-section);
309 border-top: var(--border-width) solid var(--border);
310 }
311
312 /* === 28. Settings modal ================================================ */
313 .settings-content .form-group { margin-bottom: var(--gap-section); }
314 .sync-manual-entry { margin-top: var(--gap-section); }
315 .sync-manual-form { margin-top: var(--gap-peer); }
316 .sync-action-spaced { margin-bottom: var(--gap-section); }
317 .sync-disconnect { margin-top: var(--gap-section); }
318
319 /* === 30. Reading list / Bookmarks ====================================== */
320
321 .bookmark-tag-bar {
322 display: flex;
323 gap: var(--gap-peer);
324 padding: var(--gap-peer) var(--gap-group);
325 border-bottom: var(--border-width) solid var(--border);
326 overflow-x: auto;
327 flex-shrink: 0;
328 }
329 /* Bookmark Open/more actions use renderRow's .row-actions (hover-revealed);
330 pinned state uses .row--article.is-pinned. */
331 .bookmark-tags {
332 display: flex;
333 gap: var(--gap-peer);
334 flex-wrap: wrap;
335 margin-top: var(--gap-bound);
336 }
337 .bookmark-tag {
338 cursor: pointer;
339 font-size: var(--font-size-xs);
340 }
341 .bookmark-tag:hover {
342 background: var(--action);
343 color: white;
344 }
345 .context-menu-item.danger,
346 .context-menu-item--danger {
347 /* F2 (2026-06-02): added --danger parity name; .danger kept as legacy
348 alias until F5 retires call sites. Inline #dc3545 fallback removed;
349 --accent-red is themed and always defined. */
350 color: var(--danger);
351 }
352
353 /* === 31. Screen-reader only (.sr-only) ================================ */
354 .sr-only {
355 position: absolute;
356 width: 1px;
357 height: 1px;
358 padding: 0;
359 margin: -1px;
360 overflow: hidden;
361 clip: rect(0, 0, 0, 0);
362 white-space: nowrap;
363 border: 0;
364 }
365
366 /* === 32. Focus indicators ==============================================
367 The design system's ring for BB's own clickable elements; the same colour
368 as a box-shadow ring for inputs, which avoids the layout shift an outline
369 causes on a bevelled field. Rule: every new interactive primitive must
370 appear in one of these selector lists.
371
372 makeover's own primitives are absent on purpose. `.button` and `.tab` take
373 an identical ring from layout.css, and restating it here unlayered would
374 override the design system with a copy of itself.
375 ========================================================================== */
376 .source-item:focus-visible,
377 .star-btn:focus-visible,
378 .source-delete:focus-visible,
379 .row--article:focus-visible,
380 .plugin-item:focus-visible,
381 .toast-action:focus-visible {
382 outline: 2px solid var(--focus-ring);
383 outline-offset: 2px;
384 }
385
386 /* A field's focus ring comes from layout.css now. What stood here was this
387 app's own third answer to the same question: `outline: none` plus a composed
388 box-shadow that had to restate the bevel beside it. `outline: none` also
389 killed the generated `.field:focus-visible`, so keeping this rule would have
390 left fields with no ring rather than with two. Same 2px, and --focus-ring
391 resolves to the same colour; the generated ring sits inside the well
392 (`outline-offset: calc(-1 * 2px)`), which is where a well's ring belongs.
393
394 `:focus-visible` rather than `:focus` is not a behaviour change for a text
395 field: a focused text input always matches `:focus-visible`. */
396
397