Skip to main content

max / balanced_breakfast

8.0 KB · 276 lines History Blame Raw
1 /* === 4. Layout shell: body & #app ==================================== */
2 body {
3 font-family: var(--font-sans);
4 background-color: var(--surface-page);
5 color: var(--content);
6 line-height: 1.5;
7 overflow: hidden;
8 }
9
10 #app {
11 display: flex;
12 flex-direction: column;
13 height: 100vh;
14 }
15
16 /* === 4. Layout shell: main =========================================== */
17 .main {
18 display: flex;
19 flex: 1;
20 overflow: hidden;
21 }
22
23 /* === 5. Header ========================================================== */
24 .header {
25 display: flex;
26 justify-content: space-between;
27 align-items: center;
28 padding: var(--gap-peer) var(--gap-section);
29 background-color: var(--surface-overlay);
30 border-bottom: var(--border-width) solid var(--border);
31 -webkit-user-select: none;
32 user-select: none;
33 position: relative;
34 }
35
36 .header-actions {
37 display: flex;
38 gap: var(--gap-peer);
39 align-items: center;
40 }
41
42 /* Carries `.field` in the markup: shape is below, fill and inset edge come
43 from the generated layout.css. Width is the only thing that made this a
44 different rule from a form field, and it is all that is left here. */
45 .search-input {
46 width: 180px;
47 }
48
49 /* `.search-spinner` and `@keyframes spin` are gone (2026-08-26). They were the
50 only rotation left in this file, and a spinner turns at a rate it invented:
51 it reads as progress on a wait that has none to report. Search here is a
52 local query behind a 300ms debounce, so under rule 4 of wiki
53 `loading-and-progress-standard` there was nothing for it to say. */
54
55 .search-input::placeholder { color: var(--content-muted); }
56
57 /* `.sort-select` is gone: every property it set is `.field` plus the
58 `select.field` cursor, and its `:focus { outline: none }` was killing the
59 generated `.field:focus-visible` ring on the one control that had no ring of
60 its own to replace it with. The call sites carry `.field`. */
61
62 /* === 6. Sidebar ======================================================== */
63 .sidebar {
64 width: 220px;
65 min-width: 220px;
66 background-color: var(--surface-overlay);
67 border-right: var(--border-width) solid var(--border);
68 display: flex;
69 flex-direction: column;
70 overflow-y: auto;
71 scrollbar-gutter: stable;
72 }
73
74 .sidebar h2 {
75 padding: var(--gap-peer) var(--gap-group);
76 font-size: var(--font-size-sm);
77 font-weight: 700;
78 color: var(--content-muted);
79 text-transform: uppercase;
80 letter-spacing: 0.5px;
81 border-bottom: var(--border-width) solid var(--border);
82 }
83
84 /* === 7. Items panel ==================================================== */
85 .items-panel {
86 flex: 1;
87 display: flex;
88 flex-direction: column;
89 overflow-y: auto;
90 background-color: var(--surface-page);
91 }
92
93 /* `.items-list`, `.plugin-list` and `.sources-list` each wrote
94 `list-style: none` by hand. All three carry `.list` now, which is the
95 generated reset; `.sources-list` keeps its padding and the other two had
96 nothing else to keep. */
97
98 /* Article rows: feed items + reading-list bookmarks. Uses the renderRow .row
99 layout (icon / content / actions, flex + gap) plus article appearance +
100 read/unread/selected/pinned state. Replaces the legacy .item-* layout. */
101 .row--article {
102 padding: var(--gap-peer) var(--gap-section);
103 border-bottom: var(--border-width) solid var(--border);
104 /* 3px, wider than a frame on purpose: this edge is the state marker. */
105 border-left: 3px solid transparent;
106 cursor: pointer;
107 }
108 .row--article:hover { background-color: var(--surface-overlay); }
109 .row--article.is-unread {
110 border-left-color: var(--danger);
111 background-color: color-mix(in srgb, var(--danger) 6%, var(--surface-page));
112 }
113 .row--article.is-read { color: var(--content-secondary); }
114 .row--article.is-selected {
115 background-color: var(--surface-sunken);
116 border-left-color: var(--category-four);
117 }
118 .row--article.is-unread.is-selected {
119 border-left-color: var(--danger);
120 background-color: color-mix(in srgb, var(--danger) 12%, var(--surface-page));
121 }
122 .row--article.is-pinned { border-left-color: var(--action); }
123 .empty-icon {
124 display: flex;
125 justify-content: center;
126 margin-bottom: var(--gap-section);
127 color: var(--content-muted);
128 }
129 .empty-icon svg { width: 2.5rem; height: 2.5rem; }
130
131 /* Standalone empty-state block. The items/bookmarks empty rows use
132 `<li class="empty-state">`; new surfaces use BB.ui.renderEmptyState().
133 <div class="empty-state">
134 <div class="empty-state-icon">...</div>
135 <p class="empty-state-text">No items</p>
136 <button class="button button--primary">Add one</button>
137 </div>
138 */
139 .empty-state {
140 display: flex;
141 flex-direction: column;
142 align-items: center;
143 justify-content: center;
144 text-align: center;
145 padding: var(--gap-page) var(--gap-pane);
146 color: var(--content-muted);
147 line-height: 1.6;
148 gap: var(--gap-section);
149 }
150 .empty-state-icon {
151 font-size: 2.5rem;
152 opacity: 0.6;
153 }
154 .empty-state-text {
155 font-size: var(--font-size-base);
156 color: var(--content-secondary);
157 margin: 0;
158 }
159 .empty-state--compact {
160 padding: var(--gap-pane) var(--gap-group);
161 gap: var(--gap-peer);
162 }
163 .empty-state--compact .empty-state-icon { font-size: var(--font-size-3xl); }
164
165 /* Control stack (star / pin + read dot) passed into renderRow's icon slot.
166 The .row layout provides the gap to the content; no margin needed here. */
167 .article-controls {
168 display: flex;
169 flex-direction: column;
170 align-items: center;
171 min-width: 24px;
172 }
173
174 .star-btn {
175 background: none;
176 border: none;
177 cursor: pointer;
178 font-size: var(--font-size-xl);
179 color: var(--content-muted);
180 padding: var(--step-tight) var(--step-snug);
181 margin: calc(-1 * var(--step-tight)) calc(-1 * var(--step-snug));
182 line-height: 1;
183 }
184 .star-btn:hover { color: var(--category-four); transform: scale(1.1); }
185 .star-btn.starred { color: var(--category-four); }
186
187 .read-indicator {
188 width: 7px;
189 height: 7px;
190 border-radius: var(--radius-full);
191 margin-top: var(--gap-bound);
192 }
193 .read-indicator.unread { background-color: var(--danger); }
194
195 /* Content column is renderRow's .row-content (flex:1; min-width:0). */
196
197 /* Header slot holds the author/time meta line (+ mobile source badge). */
198 .article-meta {
199 display: flex;
200 justify-content: space-between;
201 align-items: flex-start;
202 margin-bottom: var(--step-hair);
203 }
204 .article-author {
205 font-weight: 700;
206 color: var(--danger);
207 font-size: var(--font-size-md);
208 }
209
210 /* Title: two-line clamp (overrides .row-primary's single flex line). */
211 .row--article .row-primary {
212 display: -webkit-box;
213 -webkit-line-clamp: 2;
214 -webkit-box-orient: vertical;
215 overflow: hidden;
216 font-weight: 400;
217 line-height: 1.4;
218 font-size: var(--font-size-base);
219 }
220 .row--article .row-secondary {
221 font-size: var(--font-size-md);
222 margin-top: var(--step-hair);
223 }
224 /* .article-source-badge (mobile-only) lives in section 38. */
225
226 .load-more { padding: var(--gap-group); text-align: center; }
227
228 /* === 8. Detail panel =================================================== */
229 .detail-panel {
230 width: 400px;
231 min-width: 400px;
232 background-color: var(--surface-overlay);
233 border-left: var(--border-width) solid var(--border);
234 display: none;
235 flex-direction: column;
236 overflow-y: auto;
237 }
238
239 .detail-header {
240 display: flex;
241 justify-content: flex-end;
242 gap: var(--gap-bound);
243 padding: var(--gap-peer);
244 border-bottom: var(--border-width) solid var(--border);
245 background-color: var(--surface-sunken);
246 }
247
248 .item-detail { padding: var(--gap-group); }
249
250 .detail-title {
251 font-size: var(--font-size-xl);
252 margin-bottom: var(--gap-section);
253 color: var(--content);
254 font-weight: 700;
255 line-height: 1.4;
256 }
257
258 .detail-meta {
259 display: flex;
260 flex-wrap: wrap;
261 gap: var(--gap-peer);
262 margin-bottom: var(--gap-peer);
263 color: var(--content-secondary);
264 font-size: var(--font-size-md);
265 padding-bottom: var(--gap-section);
266 border-bottom: var(--border-width) solid var(--border);
267 }
268
269 .detail-tags {
270 display: flex;
271 flex-wrap: wrap;
272 gap: var(--gap-peer);
273 margin-bottom: var(--gap-section);
274 }
275
276