@aparte/core 0.16.6 → 0.16.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -4
- package/dist/client/aparte-client.d.ts +14 -0
- package/dist/client/aparte-client.d.ts.map +1 -1
- package/dist/components/bubble/aparte-chat-bubble.d.ts +33 -18
- package/dist/components/bubble/aparte-chat-bubble.d.ts.map +1 -1
- package/dist/components/chat/aparte-chat.d.ts +32 -25
- package/dist/components/chat/aparte-chat.d.ts.map +1 -1
- package/dist/components/composer/aparte-composer-action.d.ts +1 -1
- package/dist/components/composer/aparte-composer-add-attachment.d.ts +1 -1
- package/dist/components/composer/aparte-composer-attachments.d.ts +1 -1
- package/dist/components/composer/aparte-composer-cancel.d.ts +2 -2
- package/dist/components/composer/aparte-composer-cancel.d.ts.map +1 -1
- package/dist/components/composer/aparte-composer-input.d.ts +6 -6
- package/dist/components/composer/aparte-composer-send.d.ts +3 -3
- package/dist/components/composer/aparte-composer-send.d.ts.map +1 -1
- package/dist/components/composer/aparte-composer.d.ts +9 -2
- package/dist/components/composer/aparte-composer.d.ts.map +1 -1
- package/dist/components/context/aparte-context.d.ts +0 -1
- package/dist/components/context/aparte-context.d.ts.map +1 -1
- package/dist/components/conversation-list/aparte-conversation-list.d.ts +6 -6
- package/dist/components/elicitation/aparte-elicitation.d.ts +20 -18
- package/dist/components/elicitation/aparte-elicitation.d.ts.map +1 -1
- package/dist/components/scroll-rail/aparte-scroll-rail.d.ts +9 -2
- package/dist/components/scroll-rail/aparte-scroll-rail.d.ts.map +1 -1
- package/dist/components/sidebar/aparte-sidebar.d.ts +16 -2
- package/dist/components/sidebar/aparte-sidebar.d.ts.map +1 -1
- package/dist/components/split/aparte-split.d.ts +9 -4
- package/dist/components/split/aparte-split.d.ts.map +1 -1
- package/dist/components/status/aparte-chat-status.d.ts +3 -2
- package/dist/components/status/aparte-chat-status.d.ts.map +1 -1
- package/dist/components/viewport/aparte-chat-viewport.d.ts +27 -4
- package/dist/components/viewport/aparte-chat-viewport.d.ts.map +1 -1
- package/dist/config/icon-provider.d.ts +6 -0
- package/dist/config/icon-provider.d.ts.map +1 -1
- package/dist/config/locale.d.ts +3 -1
- package/dist/config/locale.d.ts.map +1 -1
- package/dist/conversations/conversation-controller.d.ts.map +1 -1
- package/dist/custom-elements.json +10458 -10325
- package/dist/{dialog-triggers-CVCxqKg3.js → dialog-triggers-D8wNICrT.js} +36 -19
- package/dist/dialog-triggers-D8wNICrT.js.map +1 -0
- package/dist/generated/element-attributes.d.ts +13 -3
- package/dist/generated/element-attributes.d.ts.map +1 -1
- package/dist/{glyphs-DQTeOj4w.js → glyphs-C8z2oJUg.js} +36 -27
- package/dist/{glyphs-DQTeOj4w.js.map → glyphs-C8z2oJUg.js.map} +1 -1
- package/dist/icons/extended.d.ts +0 -6
- package/dist/icons/extended.d.ts.map +1 -1
- package/dist/icons/glyphs.d.ts +13 -4
- package/dist/icons/glyphs.d.ts.map +1 -1
- package/dist/icons.js +28 -31
- package/dist/icons.js.map +1 -1
- package/dist/index.css +909 -357
- package/dist/index.js +176 -54
- package/dist/index.js.map +1 -1
- package/dist/index.node.js +1 -1
- package/dist/primitives/icon/aparte-icon.d.ts +1 -1
- package/dist/primitives/progress-spinner/aparte-progress-spinner.d.ts +2 -3
- package/dist/primitives/progress-spinner/aparte-progress-spinner.d.ts.map +1 -1
- package/dist/primitives/select/aparte-optgroup.d.ts +1 -1
- package/dist/primitives/select/aparte-optgroup.d.ts.map +1 -1
- package/dist/primitives/select/aparte-option.d.ts +4 -4
- package/dist/primitives/select/aparte-option.d.ts.map +1 -1
- package/dist/primitives/select/aparte-select.d.ts +21 -12
- package/dist/primitives/select/aparte-select.d.ts.map +1 -1
- package/dist/renderers/segments/code.d.ts.map +1 -1
- package/dist/renderers/segments/error.d.ts.map +1 -1
- package/dist/renderers/segments/thinking.d.ts.map +1 -1
- package/dist/renderers/segments/tool-call.d.ts.map +1 -1
- package/package.json +3 -2
- package/dist/dialog-triggers-CVCxqKg3.js.map +0 -1
package/dist/index.css
CHANGED
|
@@ -183,7 +183,6 @@
|
|
|
183
183
|
below (it was a frozen plum hex that outlived every rebrand); the assistant is
|
|
184
184
|
plain prose (AI-chat convention) — flip it to get a two-bubble chat-app look. */
|
|
185
185
|
--aparte-message-content-bg-assistant: transparent;
|
|
186
|
-
--aparte-message-content-radius: 14px;
|
|
187
186
|
|
|
188
187
|
/* Aliases used by some segment renderers (artifact card, tool pill).
|
|
189
188
|
Mapped to existing tokens so consumers that override --aparte-surface-1/2
|
|
@@ -206,8 +205,6 @@
|
|
|
206
205
|
--aparte-thumb-remove-bg-hover: rgba(0, 0, 0, 0.85);
|
|
207
206
|
--aparte-thumb-remove-color: #ffffff;
|
|
208
207
|
|
|
209
|
-
/* Action Bar */
|
|
210
|
-
--aparte-action-bar-btn-size: 28px;
|
|
211
208
|
/* The mark (display/mark.css): how much of its intent a marked row's ground takes,
|
|
212
209
|
and how wide the bar on its start edge is. One pair for the select's chosen option,
|
|
213
210
|
the checked field choice, the active conversation and an answered question. */
|
|
@@ -215,12 +212,16 @@
|
|
|
215
212
|
--aparte-mark-bar: 2px;
|
|
216
213
|
|
|
217
214
|
/* Branch Picker */
|
|
218
|
-
--aparte-branch-picker-btn-size: 20px;
|
|
219
|
-
--aparte-branch-picker-btn-icon-size: 16px;
|
|
220
215
|
--aparte-branch-picker-label-min-width: 32px;
|
|
221
216
|
|
|
222
217
|
/* Avatars */
|
|
223
218
|
--aparte-avatar-size: 32px;
|
|
219
|
+
/* Fractions of the size, computed on the element (display/avatar.css): the initials'
|
|
220
|
+
type, the corner and the group's overlap follow the ramp instead of sitting at one
|
|
221
|
+
absolute value for 20px and 56px alike. */
|
|
222
|
+
--aparte-avatar-initials-ratio: 0.34;
|
|
223
|
+
--aparte-avatar-radius-ratio: 0.28;
|
|
224
|
+
--aparte-avatar-overlap-ratio: 0.28;
|
|
224
225
|
--aparte-avatar-image-user: none;
|
|
225
226
|
--aparte-avatar-image-assistant: none;
|
|
226
227
|
--aparte-avatar-image-size: 90%;
|
|
@@ -230,21 +231,18 @@
|
|
|
230
231
|
/* Input */
|
|
231
232
|
--aparte-input-editor-max-height: 200px;
|
|
232
233
|
|
|
233
|
-
/* Icons
|
|
234
|
-
|
|
235
|
-
--aparte-icon-size-sm: 12px;
|
|
236
|
-
--aparte-icon-size-lg: 16px;
|
|
237
|
-
--aparte-icon-size-xl: 20px;
|
|
234
|
+
/* Icons: one knob, inherited — a container declares `--aparte-icon-size` and every
|
|
235
|
+
glyph below follows. The steps are on the type scale, in the derived layer. */
|
|
238
236
|
|
|
239
|
-
/* Send button + inline action buttons */
|
|
240
|
-
--aparte-send-btn-size: 36px;
|
|
241
|
-
--aparte-input-action-btn-size: 36px;
|
|
237
|
+
/* Send button + inline action buttons (their size is the lg step, in the derived layer) */
|
|
242
238
|
--aparte-input-action-btn-icon-size: 20px;
|
|
243
239
|
--aparte-model-select-min-width: 120px;
|
|
244
240
|
|
|
245
241
|
/* Status Indicator */
|
|
246
242
|
--aparte-status-bg: transparent;
|
|
247
243
|
--aparte-status-dot-size: 6px;
|
|
244
|
+
/* The floor of every pulsing dot's opacity: above the perception threshold. */
|
|
245
|
+
--aparte-pulse-floor: 0.55;
|
|
248
246
|
|
|
249
247
|
/* Waiting Indicator (the bubble's own dots, before the first token) */
|
|
250
248
|
--aparte-waiting-height: 1.5em;
|
|
@@ -252,7 +250,6 @@
|
|
|
252
250
|
/* Thinking Segment */
|
|
253
251
|
--aparte-thinking-content-max-height: 300px;
|
|
254
252
|
--aparte-thinking-content-line-height: 1.55;
|
|
255
|
-
--aparte-thinking-toggle-size: 9px;
|
|
256
253
|
--aparte-thinking-rail-width: 2px;
|
|
257
254
|
|
|
258
255
|
/* Elicitation — the panel a tool's question is asked in.
|
|
@@ -282,8 +279,7 @@
|
|
|
282
279
|
--aparte-code-copy-icon-size: 14px;
|
|
283
280
|
--aparte-code-inline-font-size: 0.875em;
|
|
284
281
|
|
|
285
|
-
/* Scroll-to-bottom button */
|
|
286
|
-
--aparte-scroll-btn-size: 36px;
|
|
282
|
+
/* Scroll-to-bottom button (its size is the lg step, in the derived layer) */
|
|
287
283
|
--aparte-scroll-btn-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
|
|
288
284
|
|
|
289
285
|
/* Error segment */
|
|
@@ -291,8 +287,6 @@
|
|
|
291
287
|
--aparte-error-border: #fecaca;
|
|
292
288
|
--aparte-error-solid: #dc2626;
|
|
293
289
|
--aparte-error-text: #991b1b;
|
|
294
|
-
--aparte-error-title: #7f1d1d;
|
|
295
|
-
--aparte-error-icon-size: 20px;
|
|
296
290
|
--aparte-error-rail-width: 4px;
|
|
297
291
|
|
|
298
292
|
/* Prose (aparte-segment-content) */
|
|
@@ -327,12 +321,9 @@
|
|
|
327
321
|
|
|
328
322
|
/* Input (composer) */
|
|
329
323
|
|
|
330
|
-
/* Conversation List */
|
|
331
|
-
--aparte-conv-action-btn-size: 20px;
|
|
332
324
|
|
|
333
325
|
/* Scroll rail — the ticks beside the transcript. Off-scale on purpose: a 2px line
|
|
334
326
|
and a 14px mark are a minimap's measures, not spacing steps. */
|
|
335
|
-
--aparte-scroll-rail-width: 20px;
|
|
336
327
|
--aparte-scroll-rail-tick-size: 14px;
|
|
337
328
|
--aparte-scroll-rail-tick-thickness: 2px;
|
|
338
329
|
|
|
@@ -341,7 +332,6 @@
|
|
|
341
332
|
/* Context gauge, ring variant — a diameter, and a stroke in the ring's own 36-unit
|
|
342
333
|
box. Literals like the rail's: a 22px ring is a toolbar's measure, not a spacing step. */
|
|
343
334
|
--aparte-context-ring-size: 22px;
|
|
344
|
-
--aparte-context-ring-stroke: 4;
|
|
345
335
|
|
|
346
336
|
/* Shell — the sidebar's column, the header's row and the split's seam. */
|
|
347
337
|
--aparte-sidebar-width: 260px;
|
|
@@ -353,9 +343,15 @@
|
|
|
353
343
|
is 12px because that is a target, and it grows to the touch target on a coarse
|
|
354
344
|
pointer (responsive.css). */
|
|
355
345
|
--aparte-split-position: 38%;
|
|
356
|
-
|
|
346
|
+
/* Never more than the container has: at 320px this floor was wider than a phone, so
|
|
347
|
+
the primary pane took everything and the end pane was annihilated to 0. */
|
|
348
|
+
--aparte-split-min: min(20rem, 100%);
|
|
357
349
|
--aparte-split-max: 60%;
|
|
358
|
-
|
|
350
|
+
/* The seam's TRACK — the grab zone, and what the grid reserves. The painted line
|
|
351
|
+
inside it is `--aparte-split-seam-width` (derived layer): one token used to size
|
|
352
|
+
both, so the line could not be thinned without moving the layout, and at 4px it
|
|
353
|
+
was four times the kit's rule with no grip — an interaction drawn as a decoration. */
|
|
354
|
+
--aparte-split-handle-size: 12px;
|
|
359
355
|
--aparte-split-hit-area: 12px;
|
|
360
356
|
/* Sizes that are not steps of a scale. Each is off-scale on purpose — a 40px
|
|
361
357
|
file tile is a tile, not fourteen spacing units — so each gets a name rather
|
|
@@ -406,6 +402,9 @@
|
|
|
406
402
|
--aparte-menu-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
407
403
|
--aparte-popover-shadow: 0 1px 3px rgba(0, 0, 0, 0.08), 0 10px 28px rgba(0, 0, 0, 0.16);
|
|
408
404
|
--aparte-tooltip-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
|
|
405
|
+
/* The composer laid over the transcript (overlay-composer): a thing that floats has to
|
|
406
|
+
be seen floating, and its ground is the transcript's own. */
|
|
407
|
+
--aparte-composer-overlay-shadow: 0 -4px 16px rgba(0, 0, 0, 0.06);
|
|
409
408
|
--aparte-switch-thumb-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
|
|
410
409
|
--aparte-range-thumb-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
|
|
411
410
|
|
|
@@ -444,20 +443,122 @@
|
|
|
444
443
|
--aparte-skeleton-text-last-width: 60%;
|
|
445
444
|
--aparte-progress-indeterminate-width: 40%;
|
|
446
445
|
|
|
446
|
+
/* The control height scale — four steps, and EVERY control reads one: a text
|
|
447
|
+
button used to have no height (23px of padding and line-height) beside 24, 29, 32,
|
|
448
|
+
36 and 44px neighbours, and the 36px family (send, the input's action button, the
|
|
449
|
+
scroll button) lived off the scale as literals. sm is the small control (action bar,
|
|
450
|
+
branch arrows, a row's ⋯), md the default button, lg the field and the composer's
|
|
451
|
+
controls at rest, xl the large button. */
|
|
447
452
|
--aparte-btn-size-sm: 24px;
|
|
448
453
|
--aparte-btn-size-md: 32px;
|
|
449
|
-
--aparte-btn-size-lg:
|
|
454
|
+
--aparte-btn-size-lg: 36px;
|
|
455
|
+
--aparte-btn-size-xl: 40px;
|
|
456
|
+
/* The field family's literal sizes, beside the button's: the derived knobs
|
|
457
|
+
(paddings, radius) sit in the anchored layer below, like the button's own. */
|
|
458
|
+
--aparte-field-textarea-min-height: 80px;
|
|
459
|
+
--aparte-checkbox-size: 18px;
|
|
460
|
+
--aparte-radio-size: 18px;
|
|
461
|
+
--aparte-radio-dot-size: 8px;
|
|
462
|
+
--aparte-switch-width: 40px;
|
|
463
|
+
--aparte-switch-height: 22px;
|
|
464
|
+
--aparte-switch-thumb-inset: 2px;
|
|
465
|
+
--aparte-range-thumb-size: 16px;
|
|
466
|
+
--aparte-range-track-height: 4px;
|
|
450
467
|
|
|
451
|
-
--aparte-focus-outline-offset: -2px;
|
|
452
468
|
--aparte-tool-spinner-size: 10px;
|
|
453
469
|
--aparte-model-select-chevron-room: 28px;
|
|
454
470
|
}
|
|
455
471
|
|
|
472
|
+
/* =========================================
|
|
473
|
+
The system decides, by default.
|
|
474
|
+
|
|
475
|
+
Dark used to exist only behind the attribute: on a dark OS an un-attributed chat
|
|
476
|
+
rendered LIGHT on the host's dark page, unreadable, with no error. This block is
|
|
477
|
+
the DARK block above, verbatim, applied when the OS asks and the host has not
|
|
478
|
+
said otherwise — `data-aparte-theme="light"` on the root is the veto.
|
|
479
|
+
|
|
480
|
+
A duplicate on purpose (CSS cannot share one block between a media query and an
|
|
481
|
+
attribute selector): `check:derived-vars` holds the two byte-identical, so
|
|
482
|
+
editing one without the other fails the build rather than forking the theme.
|
|
483
|
+
No `color-scheme` here — it is not namespaced, and a library setting it on the
|
|
484
|
+
host's :root would flip the whole page's native controls.
|
|
485
|
+
========================================= */
|
|
486
|
+
@media (prefers-color-scheme: dark) {
|
|
487
|
+
:root:not([data-aparte-theme="light"]) {
|
|
488
|
+
--aparte-bg: #17141c;
|
|
489
|
+
--aparte-surface-1: #211b28;
|
|
490
|
+
--aparte-surface-2: #2a2333;
|
|
491
|
+
--aparte-message-content-bg-assistant: transparent;
|
|
492
|
+
--aparte-text: #f2eee7;
|
|
493
|
+
--aparte-text-muted: #a89bb6;
|
|
494
|
+
--aparte-neutral: #a89bb6;
|
|
495
|
+
--aparte-border: #342a40;
|
|
496
|
+
--aparte-primary: #d9a24b;
|
|
497
|
+
--aparte-primary-hover: #e7c588;
|
|
498
|
+
--aparte-scroll-btn-shadow: 0 2px 12px rgba(0, 0, 0, 0.35);
|
|
499
|
+
--aparte-select-shadow: 0 1px 3px rgba(0, 0, 0, 0.4), 0 10px 28px rgba(0, 0, 0, 0.55);
|
|
500
|
+
--aparte-menu-shadow: 0 4px 12px rgba(0, 0, 0, 0.35);
|
|
501
|
+
--aparte-composer-overlay-shadow: 0 -4px 16px rgba(0, 0, 0, 0.4);
|
|
502
|
+
/* Declared once at :root it darkened a dark page by 8/255: the modal state vanished. */
|
|
503
|
+
--aparte-scrim: rgba(0, 0, 0, 0.6);
|
|
504
|
+
--aparte-popover-shadow: 0 1px 3px rgba(0, 0, 0, 0.4), 0 10px 28px rgba(0, 0, 0, 0.55);
|
|
505
|
+
--aparte-tooltip-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);
|
|
506
|
+
--aparte-switch-thumb-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
|
|
507
|
+
--aparte-range-thumb-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
|
|
508
|
+
--aparte-ink-l: 0.85;
|
|
509
|
+
--aparte-error-bg: #2d1b1b;
|
|
510
|
+
--aparte-error-border: #7f1d1d;
|
|
511
|
+
--aparte-error-solid: #f87171;
|
|
512
|
+
--aparte-error-text: #fca5a5;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/* =========================================
|
|
517
|
+
Forced light — the veto, and the subtree escape.
|
|
518
|
+
|
|
519
|
+
New with the system default: without it, a host that wants light-always on a
|
|
520
|
+
dark OS has no lever, and a light island inside a dark page cannot exist. The
|
|
521
|
+
values are the light literals from the `:root` block above, one per property
|
|
522
|
+
the dark block overrides — `check:derived-vars` holds that pairing, so a light
|
|
523
|
+
literal edited above cannot drift from its copy here.
|
|
524
|
+
========================================= */
|
|
525
|
+
[data-aparte-theme="light"] {
|
|
526
|
+
color-scheme: light;
|
|
527
|
+
--aparte-bg: #f6f2ea;
|
|
528
|
+
--aparte-surface-1: #fffdf8;
|
|
529
|
+
--aparte-surface-2: #f1ebdf;
|
|
530
|
+
--aparte-message-content-bg-assistant: transparent;
|
|
531
|
+
--aparte-text: #241f2b;
|
|
532
|
+
--aparte-text-muted: #6d6479;
|
|
533
|
+
--aparte-neutral: #6d6479;
|
|
534
|
+
--aparte-border: #e5ddce;
|
|
535
|
+
--aparte-primary: #b07d33;
|
|
536
|
+
--aparte-primary-hover: #a06f28;
|
|
537
|
+
--aparte-scroll-btn-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
|
|
538
|
+
--aparte-select-shadow: 0 1px 3px rgba(0, 0, 0, 0.08), 0 10px 28px rgba(0, 0, 0, 0.16);
|
|
539
|
+
--aparte-menu-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
540
|
+
--aparte-popover-shadow: 0 1px 3px rgba(0, 0, 0, 0.08), 0 10px 28px rgba(0, 0, 0, 0.16);
|
|
541
|
+
--aparte-tooltip-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
|
|
542
|
+
/* The composer laid over the transcript (overlay-composer): a thing that floats has to
|
|
543
|
+
be seen floating, and its ground is the transcript's own. */
|
|
544
|
+
--aparte-composer-overlay-shadow: 0 -4px 16px rgba(0, 0, 0, 0.06);
|
|
545
|
+
--aparte-scrim: rgba(0, 0, 0, 0.35);
|
|
546
|
+
--aparte-switch-thumb-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
|
|
547
|
+
--aparte-range-thumb-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
|
|
548
|
+
--aparte-ink-l: 0.40;
|
|
549
|
+
--aparte-error-bg: #fef2f2;
|
|
550
|
+
--aparte-error-border: #fecaca;
|
|
551
|
+
--aparte-error-solid: #dc2626;
|
|
552
|
+
--aparte-error-text: #991b1b;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
|
|
456
556
|
/* =========================================
|
|
457
557
|
Dark Theme (App-controlled via attribute)
|
|
458
558
|
Usage: <div data-aparte-theme="dark">...</div>
|
|
459
559
|
========================================= */
|
|
460
560
|
[data-aparte-theme="dark"] {
|
|
561
|
+
color-scheme: dark;
|
|
461
562
|
/* Backgrounds */
|
|
462
563
|
--aparte-bg: #17141c;
|
|
463
564
|
--aparte-surface-1: #211b28;
|
|
@@ -488,6 +589,8 @@
|
|
|
488
589
|
--aparte-scroll-btn-shadow: 0 2px 12px rgba(0, 0, 0, 0.35);
|
|
489
590
|
--aparte-select-shadow: 0 1px 3px rgba(0, 0, 0, 0.4), 0 10px 28px rgba(0, 0, 0, 0.55);
|
|
490
591
|
--aparte-menu-shadow: 0 4px 12px rgba(0, 0, 0, 0.35);
|
|
592
|
+
--aparte-composer-overlay-shadow: 0 -4px 16px rgba(0, 0, 0, 0.4);
|
|
593
|
+
--aparte-scrim: rgba(0, 0, 0, 0.6);
|
|
491
594
|
--aparte-popover-shadow: 0 1px 3px rgba(0, 0, 0, 0.4), 0 10px 28px rgba(0, 0, 0, 0.55);
|
|
492
595
|
--aparte-tooltip-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);
|
|
493
596
|
--aparte-switch-thumb-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
|
|
@@ -507,7 +610,6 @@
|
|
|
507
610
|
--aparte-error-border: #7f1d1d;
|
|
508
611
|
--aparte-error-solid: #f87171;
|
|
509
612
|
--aparte-error-text: #fca5a5;
|
|
510
|
-
--aparte-error-title: #fecaca;
|
|
511
613
|
}
|
|
512
614
|
|
|
513
615
|
/* =========================================
|
|
@@ -573,8 +675,14 @@ aparte-chat {
|
|
|
573
675
|
--aparte-font-size-xs: calc(0.6875rem * var(--aparte-font-scale));
|
|
574
676
|
--aparte-font-size-sm: calc(0.75rem * var(--aparte-font-scale));
|
|
575
677
|
--aparte-font-size-md: calc(0.8125rem * var(--aparte-font-scale));
|
|
576
|
-
--aparte-font-size-lg: calc(0.875rem * var(--aparte-font-scale));
|
|
577
678
|
--aparte-font-size-base: calc(0.9375rem * var(--aparte-font-scale));
|
|
679
|
+
/* Above the body, in the order the names say. `lg` used to be 0.875rem — SMALLER than
|
|
680
|
+
`base` — with nothing above it, so a title and a placeholder shared a size and a
|
|
681
|
+
card's title was smaller than its body. A scale whose name lies is worse than a
|
|
682
|
+
short one. */
|
|
683
|
+
--aparte-font-size-lg: calc(1.0625rem * var(--aparte-font-scale));
|
|
684
|
+
--aparte-font-size-xl: calc(1.25rem * var(--aparte-font-scale));
|
|
685
|
+
--aparte-font-size-2xl: calc(1.5rem * var(--aparte-font-scale));
|
|
578
686
|
|
|
579
687
|
/* Elicitation and the conversation item, on the scale.
|
|
580
688
|
They were literals off every step — 0.76 / 0.78 / 0.8 / 0.82rem and a `7px 10px`
|
|
@@ -600,7 +708,6 @@ aparte-chat {
|
|
|
600
708
|
vertical half (#56) — a shorthand cannot be read by parts. */
|
|
601
709
|
--aparte-message-padding-block: var(--aparte-space-8);
|
|
602
710
|
--aparte-message-padding-inline: var(--aparte-space-6);
|
|
603
|
-
--aparte-message-padding: var(--aparte-message-padding-block) var(--aparte-message-padding-inline);
|
|
604
711
|
--aparte-message-content-padding: var(--aparte-space-5) var(--aparte-space-7);
|
|
605
712
|
--aparte-action-bar-gap: var(--aparte-space-2);
|
|
606
713
|
--aparte-branch-picker-gap: var(--aparte-space-2);
|
|
@@ -617,14 +724,15 @@ aparte-chat {
|
|
|
617
724
|
--aparte-input-padding-y: var(--aparte-space-5);
|
|
618
725
|
--aparte-conv-list-gap: var(--aparte-space-1);
|
|
619
726
|
--aparte-conv-item-gap: var(--aparte-space-3);
|
|
620
|
-
--aparte-elic-option-radius: var(--aparte-radius-
|
|
727
|
+
--aparte-elic-option-radius: var(--aparte-radius-md);
|
|
621
728
|
--aparte-elic-input-radius: var(--aparte-radius-md);
|
|
622
729
|
--aparte-tool-row-radius: var(--aparte-radius-md);
|
|
623
|
-
|
|
624
|
-
--aparte-
|
|
730
|
+
/* The split's painted seam: the kit's rule, inside a 12px track. */
|
|
731
|
+
--aparte-split-seam-width: var(--aparte-border-width);
|
|
732
|
+
--aparte-name-font-size: var(--aparte-font-size-md);
|
|
625
733
|
--aparte-timestamp-font-size: var(--aparte-font-size-sm);
|
|
626
734
|
--aparte-branch-picker-label-size: var(--aparte-font-size-sm);
|
|
627
|
-
--aparte-input-font-size: var(--aparte-font-size-
|
|
735
|
+
--aparte-input-font-size: var(--aparte-font-size-base);
|
|
628
736
|
--aparte-content-font-size: var(--aparte-font-size-base);
|
|
629
737
|
--aparte-input-editor-font-size: var(--aparte-font-size-base);
|
|
630
738
|
--aparte-status-font-size: var(--aparte-font-size-md);
|
|
@@ -637,6 +745,9 @@ aparte-chat {
|
|
|
637
745
|
moves --aparte-space-unit or --aparte-font-scale carries them along. */
|
|
638
746
|
/* A tooltip is an INVERTED surface: dark on a light theme, light on a dark one.
|
|
639
747
|
Reading the text pair gives exactly that, and for free — no third palette. */
|
|
748
|
+
/* The distance between a placed tooltip (`data-side`) and its anchor: the arrow's
|
|
749
|
+
half-diagonal, so the tip lands on the trigger rather than 2px short of it. */
|
|
750
|
+
--aparte-tooltip-gap: var(--aparte-space-3);
|
|
640
751
|
--aparte-tooltip-bg: var(--aparte-text);
|
|
641
752
|
--aparte-tooltip-color: var(--aparte-text-inverse);
|
|
642
753
|
|
|
@@ -672,6 +783,14 @@ aparte-chat {
|
|
|
672
783
|
--aparte-alert-padding: var(--aparte-space-4) var(--aparte-space-5);
|
|
673
784
|
--aparte-alert-radius: var(--aparte-radius-md);
|
|
674
785
|
--aparte-alert-rail-width: var(--aparte-space-1);
|
|
786
|
+
/* The icon scale, in rem times the type factor — the one scale in the theme that
|
|
787
|
+
was pinned in px while every type size followed `--aparte-font-scale`, so a glyph
|
|
788
|
+
beside text shrank optically the moment a reader enlarged the text. The loose
|
|
789
|
+
default is 16 (it was 14, and its documentation already said 16). */
|
|
790
|
+
--aparte-icon-size: calc(1rem * var(--aparte-font-scale));
|
|
791
|
+
--aparte-icon-size-sm: calc(0.75rem * var(--aparte-font-scale));
|
|
792
|
+
--aparte-icon-size-lg: calc(1.125rem * var(--aparte-font-scale));
|
|
793
|
+
--aparte-icon-size-xl: calc(1.25rem * var(--aparte-font-scale));
|
|
675
794
|
--aparte-alert-icon-size: var(--aparte-space-8);
|
|
676
795
|
--aparte-alert-dismiss-size: var(--aparte-btn-size-sm);
|
|
677
796
|
--aparte-alert-title-font-size: var(--aparte-font-size-md);
|
|
@@ -679,8 +798,7 @@ aparte-chat {
|
|
|
679
798
|
|
|
680
799
|
--aparte-avatar-fallback-bg: var(--aparte-surface-3);
|
|
681
800
|
--aparte-avatar-fallback-color: var(--aparte-text);
|
|
682
|
-
--aparte-avatar-group-
|
|
683
|
-
--aparte-avatar-group-ring-width: var(--aparte-border-width);
|
|
801
|
+
--aparte-avatar-group-ring-width: calc(var(--aparte-border-width) * 2);
|
|
684
802
|
--aparte-avatar-group-ring-color: var(--aparte-surface-1);
|
|
685
803
|
|
|
686
804
|
--aparte-badge-font-size: var(--aparte-font-size-2xs);
|
|
@@ -701,7 +819,7 @@ aparte-chat {
|
|
|
701
819
|
--aparte-card-header-padding: var(--aparte-space-4) var(--aparte-space-5);
|
|
702
820
|
--aparte-card-body-padding: var(--aparte-space-5);
|
|
703
821
|
--aparte-card-footer-padding: var(--aparte-space-4) var(--aparte-space-5);
|
|
704
|
-
--aparte-card-header-font-size: var(--aparte-font-size-
|
|
822
|
+
--aparte-card-header-font-size: var(--aparte-font-size-base);
|
|
705
823
|
|
|
706
824
|
--aparte-divider-margin: var(--aparte-space-5);
|
|
707
825
|
--aparte-divider-label-gap: var(--aparte-space-4);
|
|
@@ -717,22 +835,56 @@ aparte-chat {
|
|
|
717
835
|
--aparte-progress-height: var(--aparte-space-2);
|
|
718
836
|
--aparte-progress-fill: var(--aparte-primary);
|
|
719
837
|
|
|
720
|
-
|
|
838
|
+
/* The ground of a gauge or a placeholder — the thing a value is read against, so it
|
|
839
|
+
has to be seen. Derived RELATIVE to the page, not a surface level: a level sat at
|
|
840
|
+
two different distances from the ground in light and dark. */
|
|
841
|
+
--aparte-track: color-mix(in srgb, var(--aparte-text) 8%, var(--aparte-bg));
|
|
842
|
+
--aparte-skeleton-base: var(--aparte-track);
|
|
843
|
+
/* The rail is as wide as its widest tick (the current one is 1.6× the resting size);
|
|
844
|
+
it used to be a 20px literal clipping a 22.4px tick. */
|
|
845
|
+
--aparte-scroll-rail-width: calc(var(--aparte-scroll-rail-tick-size) * 1.6);
|
|
721
846
|
--aparte-skeleton-shine: var(--aparte-surface-3);
|
|
722
|
-
|
|
723
|
-
|
|
847
|
+
/* The line it stands for: as tall as the content type, and a gap that is the rest of
|
|
848
|
+
the content line, so the text that replaces it lands where it was (T21). It was 12px
|
|
849
|
+
on an 18px step for a 16px line on a 27px step — a third of a line of jump per line. */
|
|
850
|
+
--aparte-skeleton-text-height: var(--aparte-content-font-size);
|
|
851
|
+
--aparte-skeleton-text-gap: calc(var(--aparte-content-font-size) * (var(--aparte-content-line-height) - 1));
|
|
724
852
|
--aparte-skeleton-circle-size: var(--aparte-avatar-size);
|
|
725
853
|
|
|
726
854
|
--aparte-spinner-thickness: var(--aparte-space-1);
|
|
727
855
|
|
|
728
|
-
|
|
856
|
+
|
|
857
|
+
/* The field family's knobs, beside the button's — they used to be declared on
|
|
858
|
+
`.aparte-field` itself, which is why `.aparte-field-group` (their PARENT) and
|
|
859
|
+
`.aparte-color` (a sibling recipe) read a property that did not exist there and
|
|
860
|
+
rendered SQUARE: a custom property is substituted where it is declared and only
|
|
861
|
+
inherits downwards. Same reason they belong here rather than there: the theming
|
|
862
|
+
guide sends a consumer to `:root`, and every other family's knobs are here.
|
|
863
|
+
The *channels* stay local on purpose (`--aparte-checkbox-intent` and friends are
|
|
864
|
+
filled by a modifier, not set by a theme). */
|
|
865
|
+
--aparte-field-padding: var(--aparte-space-2) var(--aparte-space-3);
|
|
866
|
+
--aparte-field-padding-sm: var(--aparte-space-1) var(--aparte-space-2);
|
|
867
|
+
--aparte-field-padding-lg: var(--aparte-space-3) var(--aparte-space-4);
|
|
868
|
+
--aparte-field-radius: var(--aparte-radius-md);
|
|
869
|
+
/* The field's resting height is the lg step of the control scale, one step above the
|
|
870
|
+
default button and level with the composer's controls; `--sm` and `--lg` sit one
|
|
871
|
+
step either side. It used to be padding plus line-height, ~29px, the second
|
|
872
|
+
tallest thing in a row of five different heights. */
|
|
873
|
+
--aparte-field-size: var(--aparte-btn-size-lg);
|
|
874
|
+
--aparte-field-size-sm: var(--aparte-btn-size-md);
|
|
875
|
+
--aparte-field-size-lg: var(--aparte-btn-size-xl);
|
|
876
|
+
/* The 36px family, on the scale: named, not literal. */
|
|
877
|
+
--aparte-send-btn-size: var(--aparte-btn-size-lg);
|
|
878
|
+
--aparte-input-action-btn-size: var(--aparte-btn-size-lg);
|
|
879
|
+
--aparte-scroll-btn-size: var(--aparte-btn-size-lg);
|
|
729
880
|
|
|
730
881
|
--aparte-btn-gap: var(--aparte-space-2);
|
|
731
882
|
--aparte-btn-padding: var(--aparte-space-2) var(--aparte-space-3);
|
|
732
883
|
--aparte-btn-radius: var(--aparte-radius-md);
|
|
733
|
-
|
|
884
|
+
/* Control text is the md step (14px). One step lower was the origin of the "poor"
|
|
885
|
+
look: 13px labels in 32px boxes, beside 15px prose. */
|
|
886
|
+
--aparte-btn-font-size: var(--aparte-font-size-md);
|
|
734
887
|
--aparte-btn-bg-hover: var(--aparte-surface-2);
|
|
735
|
-
--aparte-btn-focus-offset: var(--aparte-space-1);
|
|
736
888
|
--aparte-btn-bg-active: var(--aparte-surface-3);
|
|
737
889
|
--aparte-btn-bg-toggled: var(--aparte-surface-2);
|
|
738
890
|
--aparte-btn-color-toggled: var(--aparte-text);
|
|
@@ -742,11 +894,17 @@ aparte-chat {
|
|
|
742
894
|
--aparte-btn-padding-sm: var(--aparte-space-1) var(--aparte-space-2);
|
|
743
895
|
--aparte-btn-padding-lg: var(--aparte-space-3) var(--aparte-space-5);
|
|
744
896
|
|
|
897
|
+
/* One knob for the composer's control set — the send button, the input's action
|
|
898
|
+
buttons, the editor's single-line height: the lg step at rest, the touch target
|
|
899
|
+
under a coarse pointer (responsive.css). It used to be undeclared, with `44px`
|
|
900
|
+
written as a fallback at every reader, so a desktop composer was a 62px bar around
|
|
901
|
+
15px of text and the coarse-pointer block had nothing left to enlarge. */
|
|
902
|
+
--aparte-composer-control-size: var(--aparte-btn-size-lg);
|
|
745
903
|
--aparte-input-container-min-height: var(--aparte-touch-target-size);
|
|
746
|
-
--aparte-elic-message-size: var(--aparte-font-size-
|
|
747
|
-
--aparte-elic-title-size: var(--aparte-font-size-
|
|
904
|
+
--aparte-elic-message-size: var(--aparte-font-size-base);
|
|
905
|
+
--aparte-elic-title-size: var(--aparte-font-size-base);
|
|
748
906
|
--aparte-elic-desc-size: var(--aparte-font-size-sm);
|
|
749
|
-
--aparte-elic-option-title-size: var(--aparte-font-size-
|
|
907
|
+
--aparte-elic-option-title-size: var(--aparte-font-size-md);
|
|
750
908
|
--aparte-elic-option-desc-size: var(--aparte-font-size-sm);
|
|
751
909
|
--aparte-elic-skip-size: var(--aparte-font-size-md);
|
|
752
910
|
--aparte-elic-step-size: var(--aparte-font-size-sm);
|
|
@@ -756,6 +914,17 @@ aparte-chat {
|
|
|
756
914
|
|
|
757
915
|
/* Borders */
|
|
758
916
|
--aparte-border-focus: var(--aparte-primary);
|
|
917
|
+
/* THE focus ring: a 2px outline in this colour, one spacing step OUTSIDE the box, on
|
|
918
|
+
every control. It used to be drawn two ways — this outline on seventeen recipes and a
|
|
919
|
+
soft box-shadow wash (30 % of the accent, 1.39:1 against the page) on five — and the
|
|
920
|
+
offset was −2px, inside the box, so on a bordered row the ring sat as a second
|
|
921
|
+
concentric line and the next row painted over its edge. */
|
|
922
|
+
--aparte-focus-outline-offset: var(--aparte-space-1);
|
|
923
|
+
/* The edge of a CONTROL — a field, a choice box, the select's trigger. `--aparte-border`
|
|
924
|
+
separates two regions and is right to be faint; a thing someone can touch has to be
|
|
925
|
+
seen, and six previews showed a control with no visible edge. Derived from the ink
|
|
926
|
+
and the ground so both schemes follow; the mix is a first setting. */
|
|
927
|
+
--aparte-border-control: color-mix(in srgb, var(--aparte-text) 30%, var(--aparte-surface-1));
|
|
759
928
|
|
|
760
929
|
/* Component-specific radius (overridable per app) */
|
|
761
930
|
--aparte-radius-input: var(--aparte-radius-lg);
|
|
@@ -763,19 +932,25 @@ aparte-chat {
|
|
|
763
932
|
--aparte-radius-send-btn: var(--aparte-radius-md);
|
|
764
933
|
--aparte-radius-action-btn: var(--aparte-radius-sm);
|
|
765
934
|
--aparte-radius-select: var(--aparte-radius-md);
|
|
766
|
-
--aparte-radius-avatar: var(--aparte-radius-md);
|
|
767
935
|
--aparte-radius-bubble: var(--aparte-radius-lg);
|
|
936
|
+
/* The bubble's corner IS the bubble radius above: it was a 14px literal off the scale,
|
|
937
|
+
the most visible corner of the transcript, and the knob named for it drove nothing. */
|
|
938
|
+
--aparte-message-content-radius: var(--aparte-radius-bubble);
|
|
768
939
|
--aparte-radius-code: var(--aparte-radius-lg);
|
|
769
940
|
--aparte-radius-thinking: var(--aparte-radius-lg);
|
|
770
941
|
--aparte-radius-error: var(--aparte-radius-md);
|
|
942
|
+
/* The floating surfaces. They read a step of the scale directly, so a theme that
|
|
943
|
+
wanted square menus and round bubbles had to move the step — and every other
|
|
944
|
+
reader of it. */
|
|
945
|
+
--aparte-radius-menu: var(--aparte-radius-lg);
|
|
946
|
+
--aparte-radius-popover: var(--aparte-radius-lg);
|
|
947
|
+
--aparte-radius-dialog: var(--aparte-radius-xl);
|
|
948
|
+
--aparte-radius-tooltip: var(--aparte-radius-sm);
|
|
771
949
|
|
|
772
950
|
/* The error ring on an avatar. A full shadow value, like
|
|
773
951
|
--aparte-scroll-btn-shadow: the whole ring is the knob, not its width. */
|
|
774
952
|
--aparte-avatar-error-ring: 0 0 0 2px var(--aparte-error-solid);
|
|
775
953
|
|
|
776
|
-
/* Focus ring — derived from the accent so it themes automatically. */
|
|
777
|
-
--aparte-focus-ring: 0 0 0 3px color-mix(in srgb, var(--aparte-primary) 30%, transparent);
|
|
778
|
-
|
|
779
954
|
/* Messages */
|
|
780
955
|
--aparte-message-content-text-user: var(--aparte-text);
|
|
781
956
|
--aparte-message-content-text-assistant: var(--aparte-text);
|
|
@@ -785,7 +960,9 @@ aparte-chat {
|
|
|
785
960
|
in sRGB rather than derived with `oklch(from …)`, because a wash has to keep the
|
|
786
961
|
SURFACE's lightness, not the accent's; 12 % is where the default palette reads as a
|
|
787
962
|
tint, not a fill, on both grounds. */
|
|
788
|
-
|
|
963
|
+
/* The mark's own tint: at 12 % the user bubble — the one mark that says "this turn is
|
|
964
|
+
yours" — sat at 1.03:1 on the light page. */
|
|
965
|
+
--aparte-message-content-bg-user: color-mix(in srgb, var(--aparte-primary) var(--aparte-mark-tint), var(--aparte-surface-1));
|
|
789
966
|
/* The third surface: the second, pulled 6 % toward the text. Close to what the old
|
|
790
967
|
literals encoded (`#e7dfce` from `#f1ebdf` light, `#342a40` from `#2a2333` dark —
|
|
791
968
|
5 to 9 % per channel, not one figure), now one rule that follows the masters. */
|
|
@@ -813,6 +990,12 @@ aparte-chat {
|
|
|
813
990
|
--aparte-thumb-radius: var(--aparte-radius-lg);
|
|
814
991
|
|
|
815
992
|
/* Action Bar */
|
|
993
|
+
/* The small controls of a turn and of a conversation row are the recipe's small step
|
|
994
|
+
(24px, the WCAG 2.5.8 floor). The action bar used to feed 28 with a 24 exception for
|
|
995
|
+
the last user turn, the branch arrows redrew 20 over their own `--sm`, the row's
|
|
996
|
+
`⋯` redrew 20: three control heights in one 14px row. */
|
|
997
|
+
--aparte-action-bar-btn-size: var(--aparte-btn-size-sm);
|
|
998
|
+
--aparte-conv-action-btn-size: var(--aparte-btn-size-sm);
|
|
816
999
|
--aparte-action-bar-btn-color: var(--aparte-text-muted);
|
|
817
1000
|
--aparte-action-bar-btn-hover-bg: var(--aparte-surface-2);
|
|
818
1001
|
--aparte-action-bar-btn-hover-color: var(--aparte-text);
|
|
@@ -823,11 +1006,10 @@ aparte-chat {
|
|
|
823
1006
|
--aparte-branch-picker-label-color: var(--aparte-text-muted);
|
|
824
1007
|
|
|
825
1008
|
/* Avatars */
|
|
826
|
-
--aparte-avatar-radius: var(--aparte-radius-avatar);
|
|
827
1009
|
--aparte-avatar-bg-user: var(--aparte-primary);
|
|
828
1010
|
--aparte-avatar-text-user: var(--aparte-text-inverse);
|
|
829
1011
|
--aparte-avatar-bg-assistant: var(--aparte-surface-3);
|
|
830
|
-
--aparte-avatar-text-assistant: var(--aparte-text
|
|
1012
|
+
--aparte-avatar-text-assistant: var(--aparte-text);
|
|
831
1013
|
|
|
832
1014
|
/* Text Sizes */
|
|
833
1015
|
--aparte-name-color: var(--aparte-text);
|
|
@@ -856,6 +1038,10 @@ aparte-chat {
|
|
|
856
1038
|
--aparte-code-header-bg: var(--aparte-surface-2);
|
|
857
1039
|
--aparte-code-border: var(--aparte-border);
|
|
858
1040
|
--aparte-code-header-text: var(--aparte-text-muted);
|
|
1041
|
+
/* The block's own type. It used to inherit the prose's size and leading (16px on 1.7)
|
|
1042
|
+
under the browser's generic monospace — the largest, airiest text in the message. */
|
|
1043
|
+
--aparte-code-block-font-size: var(--aparte-font-size-md);
|
|
1044
|
+
--aparte-code-block-line-height: var(--aparte-line-height-normal);
|
|
859
1045
|
|
|
860
1046
|
/* Scroll-to-bottom button */
|
|
861
1047
|
|
|
@@ -875,6 +1061,9 @@ aparte-chat {
|
|
|
875
1061
|
|
|
876
1062
|
/* Shell */
|
|
877
1063
|
--aparte-sidebar-bg: var(--aparte-surface-2);
|
|
1064
|
+
/* The one inline inset of the column's four regions — header, search, body, footer.
|
|
1065
|
+
They used to carry three (16 / 12 / 12 / 16) and the content sat on two axes. */
|
|
1066
|
+
--aparte-sidebar-inset: var(--aparte-space-3);
|
|
878
1067
|
|
|
879
1068
|
/* Conversation List */
|
|
880
1069
|
--aparte-conv-item-radius: var(--aparte-radius-md);
|
|
@@ -954,6 +1143,28 @@ aparte-chat {
|
|
|
954
1143
|
animation: aparte-pulse var(--aparte-duration-pulse) infinite ease-in-out;
|
|
955
1144
|
}
|
|
956
1145
|
|
|
1146
|
+
/* Under reduced motion the loop stops outright. The duration tokens are reset to
|
|
1147
|
+
0.01ms in responsive.css, but that leaves `infinite` in place: a 0.01ms cycle
|
|
1148
|
+
is not stillness, it is a flicker. The descendant sweep there does not reach a
|
|
1149
|
+
recipe used outside aparté's own elements, so each looping recipe stops itself,
|
|
1150
|
+
the way display/icon.css already does. */
|
|
1151
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1152
|
+
.aparte-dot { animation: none; }
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
/* `hidden` means hidden. 71 recipes in these sheets set `display` on their base class,
|
|
1156
|
+
and an author declaration beats the browser's own `[hidden] { display: none }` — so a
|
|
1157
|
+
hidden `.aparte-btn` was out of the accessibility tree and still painted: the copy
|
|
1158
|
+
button of a tool-only turn stood there with `hidden` on it. Scoped to our own classes
|
|
1159
|
+
(a prefix match, not a bare `[hidden]`, so a host's markup is not touched), and
|
|
1160
|
+
`!important` because the whole point is to outrank the recipe's `display`. The
|
|
1161
|
+
handful of per-element `[hidden]` rules that predate this stay; they are now
|
|
1162
|
+
redundant, not wrong. */
|
|
1163
|
+
[class^="aparte-"][hidden],
|
|
1164
|
+
[class*=" aparte-"][hidden] {
|
|
1165
|
+
display: none !important;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
957
1168
|
/* =========================================
|
|
958
1169
|
aparte-conversation-list
|
|
959
1170
|
========================================= */
|
|
@@ -985,7 +1196,7 @@ aparte-conversation-list {
|
|
|
985
1196
|
.aparte-link:hover { color: var(--aparte-link-color-hover); }
|
|
986
1197
|
.aparte-link:focus-visible {
|
|
987
1198
|
outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);
|
|
988
|
-
outline-offset: var(--aparte-
|
|
1199
|
+
outline-offset: var(--aparte-focus-outline-offset);
|
|
989
1200
|
}
|
|
990
1201
|
/* Quiet — for a link inside a dense row, where a coloured one would shout. It keeps
|
|
991
1202
|
the underline, which is the part that carries the meaning. */
|
|
@@ -1008,8 +1219,11 @@ aparte-conversation-list {
|
|
|
1008
1219
|
same thing at slightly different rhythms; a travelling wave, where a row wants one,
|
|
1009
1220
|
comes from an animation-delay on its later dots, not from a skewed curve. */
|
|
1010
1221
|
@keyframes aparte-pulse {
|
|
1011
|
-
|
|
1012
|
-
|
|
1222
|
+
/* Opacity alone: the scale that used to ride along made a row of waiting dots change
|
|
1223
|
+
width in a loop. And a floor above the perception threshold — at 0.3 the status
|
|
1224
|
+
dot spent half of every cycle at 1.55:1, and a capture froze it there. */
|
|
1225
|
+
0%, 100% { opacity: var(--aparte-pulse-floor); }
|
|
1226
|
+
50% { opacity: 1; }
|
|
1013
1227
|
}
|
|
1014
1228
|
|
|
1015
1229
|
/* One rotation, for every spinner and every spinning glyph. */
|
|
@@ -1030,9 +1244,9 @@ aparte-conversation-list {
|
|
|
1030
1244
|
* <aparte-icon name="copy"></aparte-icon>
|
|
1031
1245
|
* </button>
|
|
1032
1246
|
* <div class="aparte-btn-group" role="group" aria-label="Versions">
|
|
1033
|
-
* <button class="aparte-btn aparte-btn--surface"
|
|
1247
|
+
* <button class="aparte-btn aparte-btn--surface" aria-label="Previous"><aparte-icon name="prevBranch"></aparte-icon></button>
|
|
1034
1248
|
* <button class="aparte-btn aparte-btn--surface">1 / 2</button>
|
|
1035
|
-
* <button class="aparte-btn aparte-btn--surface"
|
|
1249
|
+
* <button class="aparte-btn aparte-btn--surface" aria-label="Next"><aparte-icon name="nextBranch"></aparte-icon></button>
|
|
1036
1250
|
* </div>
|
|
1037
1251
|
* <div class="aparte-btn-group" role="group" aria-label="View">
|
|
1038
1252
|
* <button class="aparte-btn aparte-btn--primary aparte-btn--outline" aria-pressed="true">Chat</button>
|
|
@@ -1059,6 +1273,13 @@ aparte-conversation-list {
|
|
|
1059
1273
|
*/
|
|
1060
1274
|
|
|
1061
1275
|
.aparte-btn {
|
|
1276
|
+
/* The glyph reads this: an `<aparte-icon>` (or a raw `svg.aparte-icon`) inside a button
|
|
1277
|
+
takes the button's icon size, per size modifier. */
|
|
1278
|
+
--aparte-icon-size: var(--aparte-btn-icon-size);
|
|
1279
|
+
/* The box, for EVERY button: `--icon` and `--circle` used to be the only readers of
|
|
1280
|
+
the size, so a text button was padding plus line-height — 23px beside a 32px icon
|
|
1281
|
+
button in the same row. The size is declared here and read below as a minimum. */
|
|
1282
|
+
--aparte-btn-size: var(--aparte-btn-size-md);
|
|
1062
1283
|
/* The intent, as a value. Every fill below reads THIS and nothing else, which is
|
|
1063
1284
|
what keeps the two axes independent. */
|
|
1064
1285
|
--aparte-btn-intent: var(--aparte-neutral);
|
|
@@ -1077,6 +1298,8 @@ aparte-conversation-list {
|
|
|
1077
1298
|
--aparte-btn-intent-ink: var(--aparte-btn-intent);
|
|
1078
1299
|
|
|
1079
1300
|
display: inline-flex;
|
|
1301
|
+
|
|
1302
|
+
vertical-align: middle;
|
|
1080
1303
|
align-items: center;
|
|
1081
1304
|
justify-content: center;
|
|
1082
1305
|
gap: var(--aparte-btn-gap);
|
|
@@ -1087,6 +1310,7 @@ aparte-conversation-list {
|
|
|
1087
1310
|
be set by the composer's own row rule, which is exactly the kind of thing a recipe
|
|
1088
1311
|
should not need a caller for. */
|
|
1089
1312
|
box-sizing: border-box;
|
|
1313
|
+
min-block-size: var(--aparte-btn-size);
|
|
1090
1314
|
padding: var(--aparte-btn-padding);
|
|
1091
1315
|
border: var(--aparte-border-width) solid transparent;
|
|
1092
1316
|
border-radius: var(--aparte-btn-radius);
|
|
@@ -1241,15 +1465,21 @@ aparte-conversation-list {
|
|
|
1241
1465
|
.aparte-btn--soft:active:not(:disabled) { background: color-mix(in srgb, var(--aparte-btn-intent) 30%, transparent); }
|
|
1242
1466
|
.aparte-btn--solid:active:not(:disabled) { background: var(--aparte-btn-intent); filter: brightness(0.93); }
|
|
1243
1467
|
|
|
1468
|
+
/* Drawn, not faded. Opacity on the whole button faded the glyph with its ground and
|
|
1469
|
+
read at 2:1 on the send button; "inactive" and "disappearing" are not the same
|
|
1470
|
+
message. A neutral ground, the muted ink, no intent — whatever the fill was. */
|
|
1244
1471
|
.aparte-btn:disabled,
|
|
1245
1472
|
.aparte-btn[aria-disabled='true'] {
|
|
1246
1473
|
cursor: not-allowed;
|
|
1247
|
-
|
|
1474
|
+
background: var(--aparte-surface-2);
|
|
1475
|
+
color: var(--aparte-text-muted);
|
|
1476
|
+
border-color: transparent;
|
|
1477
|
+
filter: none;
|
|
1248
1478
|
}
|
|
1249
1479
|
|
|
1250
1480
|
.aparte-btn:focus-visible {
|
|
1251
|
-
outline: var(--aparte-focus-outline-width) solid var(--aparte-
|
|
1252
|
-
outline-offset: var(--aparte-
|
|
1481
|
+
outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);
|
|
1482
|
+
outline-offset: var(--aparte-focus-outline-offset);
|
|
1253
1483
|
}
|
|
1254
1484
|
|
|
1255
1485
|
/* Toggled — a disclosure that is open, a toggle that is on, a tab that is current. */
|
|
@@ -1282,25 +1512,39 @@ aparte-conversation-list {
|
|
|
1282
1512
|
.aparte-btn--block { display: flex; width: 100%; }
|
|
1283
1513
|
|
|
1284
1514
|
.aparte-btn--sm { --aparte-btn-size: var(--aparte-btn-size-sm); --aparte-btn-icon-size: var(--aparte-icon-size-sm); font-size: var(--aparte-font-size-sm); padding: var(--aparte-btn-padding-sm); }
|
|
1285
|
-
.aparte-btn--lg { --aparte-btn-size: var(--aparte-btn-size-lg); --aparte-btn-icon-size: var(--aparte-icon-size-xl); font-size: var(--aparte-font-size-
|
|
1515
|
+
.aparte-btn--lg { --aparte-btn-size: var(--aparte-btn-size-lg); --aparte-btn-icon-size: var(--aparte-icon-size-xl); font-size: var(--aparte-font-size-base); padding: var(--aparte-btn-padding-lg); }
|
|
1516
|
+
.aparte-btn--xl { --aparte-btn-size: var(--aparte-btn-size-xl); --aparte-btn-icon-size: var(--aparte-icon-size-xl); font-size: var(--aparte-font-size-base); padding: var(--aparte-btn-padding-lg); }
|
|
1286
1517
|
.aparte-btn--sm.aparte-btn--icon,
|
|
1287
1518
|
.aparte-btn--sm.aparte-btn--circle,
|
|
1288
1519
|
.aparte-btn--lg.aparte-btn--icon,
|
|
1289
|
-
.aparte-btn--lg.aparte-btn--circle
|
|
1520
|
+
.aparte-btn--lg.aparte-btn--circle,
|
|
1521
|
+
.aparte-btn--xl.aparte-btn--icon,
|
|
1522
|
+
.aparte-btn--xl.aparte-btn--circle { padding: 0; }
|
|
1290
1523
|
|
|
1291
1524
|
/* An icon inside a button never takes the pointer: the click belongs to the button,
|
|
1292
|
-
and an SVG target makes `event.target` lie to a delegated listener.
|
|
1293
|
-
the button so the two axes stay in step. */
|
|
1525
|
+
and an SVG target makes `event.target` lie to a delegated listener. */
|
|
1294
1526
|
.aparte-btn > svg,
|
|
1527
|
+
.aparte-btn > aparte-icon,
|
|
1295
1528
|
.aparte-btn > .aparte-btn__icon {
|
|
1296
1529
|
pointer-events: none;
|
|
1297
1530
|
flex-shrink: 0;
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
/* A raw SVG or a consumer's own icon box is sized with the button so the two axes stay
|
|
1534
|
+
in step. An `<aparte-icon>` is NOT sized here: it renders its `<svg>` inside itself, a
|
|
1535
|
+
grandchild this child combinator never reached, so the documented markup kept the
|
|
1536
|
+
glyph at its loose 14px default in a button that asked for 16. The recipe feeds the
|
|
1537
|
+
icon's own knob instead — `--aparte-icon-size`, declared on `.aparte-btn` above — and
|
|
1538
|
+
`display/icon.css` draws it, at `--sm`'s 12 and `--lg`'s 20 like everything else. */
|
|
1539
|
+
.aparte-btn > svg:not(.aparte-icon),
|
|
1540
|
+
.aparte-btn > .aparte-btn__icon {
|
|
1298
1541
|
width: var(--aparte-btn-icon-size);
|
|
1299
1542
|
height: var(--aparte-btn-icon-size);
|
|
1300
1543
|
}
|
|
1301
1544
|
|
|
1302
1545
|
/* ── Group — buttons that read as one control. ───────────────────────────────── */
|
|
1303
|
-
.aparte-btn-group { display: inline-flex;
|
|
1546
|
+
.aparte-btn-group { display: inline-flex;
|
|
1547
|
+
vertical-align: middle; }
|
|
1304
1548
|
.aparte-btn-group > .aparte-btn { border-radius: 0; }
|
|
1305
1549
|
/* Logical properties, so the joined edge follows the writing direction instead of
|
|
1306
1550
|
assuming left-to-right. */
|
|
@@ -1384,24 +1628,24 @@ aparte-conversation-list {
|
|
|
1384
1628
|
/* ── The shared text-entry recipe ────────────────────────────────────────────── */
|
|
1385
1629
|
|
|
1386
1630
|
.aparte-field {
|
|
1387
|
-
/*
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
own element the way every sibling can. */
|
|
1394
|
-
--aparte-field-radius: var(--aparte-radius-md);
|
|
1395
|
-
|
|
1631
|
+
/* Every knob this family reads lives in `theme.css` on `:root` — including the
|
|
1632
|
+
radius and the paddings, which used to be declared right here. That was the one
|
|
1633
|
+
construction bug of the kit: `.aparte-field-group` is this element's PARENT and
|
|
1634
|
+
`.aparte-color` a sibling recipe, both read `--aparte-field-radius`, and a custom
|
|
1635
|
+
property declared here is invisible to both, so they computed `border-radius: 0`
|
|
1636
|
+
and every field group in the library rendered square. */
|
|
1396
1637
|
box-sizing: border-box;
|
|
1397
1638
|
display: block;
|
|
1398
1639
|
width: 100%;
|
|
1640
|
+
/* A resting height from the control scale, so a field and the button beside it stop
|
|
1641
|
+
disagreeing by 6px; the size modifiers move it a step either way. */
|
|
1642
|
+
min-block-size: var(--aparte-field-size);
|
|
1399
1643
|
font: inherit;
|
|
1400
|
-
font-size: var(--aparte-font-size-
|
|
1644
|
+
font-size: var(--aparte-font-size-md);
|
|
1401
1645
|
line-height: var(--aparte-line-height-normal);
|
|
1402
1646
|
color: var(--aparte-text);
|
|
1403
1647
|
background: var(--aparte-surface-1);
|
|
1404
|
-
border: var(--aparte-border-width) solid var(--aparte-border);
|
|
1648
|
+
border: var(--aparte-border-width) solid var(--aparte-border-control);
|
|
1405
1649
|
border-radius: var(--aparte-field-radius);
|
|
1406
1650
|
padding: var(--aparte-field-padding);
|
|
1407
1651
|
transition:
|
|
@@ -1436,10 +1680,13 @@ aparte-conversation-list {
|
|
|
1436
1680
|
border-color: var(--aparte-border-focus);
|
|
1437
1681
|
}
|
|
1438
1682
|
|
|
1683
|
+
/* Drawn, not faded: the muted ink on the quieter ground, and the edge back to the region
|
|
1684
|
+
colour. Opacity faded the text with its box and read under 3:1. */
|
|
1439
1685
|
.aparte-field:disabled {
|
|
1440
1686
|
cursor: not-allowed;
|
|
1441
|
-
opacity: var(--aparte-disabled-opacity);
|
|
1442
1687
|
background: var(--aparte-surface-2);
|
|
1688
|
+
color: var(--aparte-text-muted);
|
|
1689
|
+
border-color: var(--aparte-border);
|
|
1443
1690
|
}
|
|
1444
1691
|
|
|
1445
1692
|
.aparte-field[readonly] {
|
|
@@ -1453,37 +1700,35 @@ aparte-conversation-list {
|
|
|
1453
1700
|
border-color: var(--aparte-error);
|
|
1454
1701
|
}
|
|
1455
1702
|
|
|
1703
|
+
/* The one ring (see --aparte-focus-outline-offset in theme.css): an outline outside the
|
|
1704
|
+
box, and the border left alone — a highlighted border under an offset ring read as two
|
|
1705
|
+
concentric lines. An invalid field's ring takes the error colour. */
|
|
1456
1706
|
.aparte-field:focus-visible {
|
|
1457
|
-
outline:
|
|
1458
|
-
|
|
1459
|
-
box-shadow: var(--aparte-focus-ring);
|
|
1707
|
+
outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);
|
|
1708
|
+
outline-offset: var(--aparte-focus-outline-offset);
|
|
1460
1709
|
}
|
|
1461
1710
|
|
|
1462
1711
|
.aparte-field[aria-invalid='true']:focus-visible {
|
|
1463
|
-
|
|
1464
|
-
box-shadow: var(--aparte-field-error-ring);
|
|
1712
|
+
outline-color: var(--aparte-error-solid);
|
|
1465
1713
|
}
|
|
1466
1714
|
|
|
1467
1715
|
/* ── Sizes ───────────────────────────────────────────────────────────────────── */
|
|
1468
1716
|
|
|
1469
1717
|
.aparte-field--sm {
|
|
1470
|
-
--aparte-field-
|
|
1471
|
-
|
|
1718
|
+
--aparte-field-size: var(--aparte-field-size-sm);
|
|
1472
1719
|
padding: var(--aparte-field-padding-sm);
|
|
1473
|
-
font-size: var(--aparte-font-size-
|
|
1720
|
+
font-size: var(--aparte-font-size-sm);
|
|
1474
1721
|
}
|
|
1475
1722
|
|
|
1476
1723
|
.aparte-field--lg {
|
|
1477
|
-
--aparte-field-
|
|
1478
|
-
|
|
1724
|
+
--aparte-field-size: var(--aparte-field-size-lg);
|
|
1479
1725
|
padding: var(--aparte-field-padding-lg);
|
|
1480
|
-
font-size: var(--aparte-font-size-
|
|
1726
|
+
font-size: var(--aparte-font-size-base);
|
|
1481
1727
|
}
|
|
1482
1728
|
|
|
1483
1729
|
/* ── Textarea ────────────────────────────────────────────────────────────────── */
|
|
1484
1730
|
|
|
1485
1731
|
.aparte-field--textarea {
|
|
1486
|
-
--aparte-field-textarea-min-height: 80px;
|
|
1487
1732
|
|
|
1488
1733
|
display: block;
|
|
1489
1734
|
min-block-size: var(--aparte-field-textarea-min-height);
|
|
@@ -1498,7 +1743,7 @@ aparte-conversation-list {
|
|
|
1498
1743
|
display: flex;
|
|
1499
1744
|
align-items: stretch;
|
|
1500
1745
|
background: var(--aparte-surface-1);
|
|
1501
|
-
border: var(--aparte-border-width) solid var(--aparte-border);
|
|
1746
|
+
border: var(--aparte-border-width) solid var(--aparte-border-control);
|
|
1502
1747
|
border-radius: var(--aparte-field-radius);
|
|
1503
1748
|
/* The addons' ground follows the group's corners; the focus ring is a shadow on the
|
|
1504
1749
|
group itself, outside this box, so it is not clipped. */
|
|
@@ -1520,8 +1765,15 @@ aparte-conversation-list {
|
|
|
1520
1765
|
border-radius: 0;
|
|
1521
1766
|
}
|
|
1522
1767
|
|
|
1768
|
+
/* The GROUP draws the ring (below): the field inside it draws none. */
|
|
1523
1769
|
.aparte-field-group > .aparte-field:focus-visible {
|
|
1524
|
-
|
|
1770
|
+
outline: none;
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
/* A button in the group is as tall as the field it sits beside — the one row where the
|
|
1774
|
+
two recipes meet, and the one where a 4px difference shows. */
|
|
1775
|
+
.aparte-field-group > .aparte-btn {
|
|
1776
|
+
--aparte-btn-size: var(--aparte-field-size);
|
|
1525
1777
|
}
|
|
1526
1778
|
|
|
1527
1779
|
/* An addon has a ground of its own and a rule against the field: on the field's own
|
|
@@ -1548,8 +1800,8 @@ aparte-conversation-list {
|
|
|
1548
1800
|
}
|
|
1549
1801
|
|
|
1550
1802
|
.aparte-field-group:focus-within {
|
|
1551
|
-
|
|
1552
|
-
|
|
1803
|
+
outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);
|
|
1804
|
+
outline-offset: var(--aparte-focus-outline-offset);
|
|
1553
1805
|
}
|
|
1554
1806
|
|
|
1555
1807
|
/* `aria-invalid` lives on the field itself, not the wrapper the app didn't write a
|
|
@@ -1560,7 +1812,7 @@ aparte-conversation-list {
|
|
|
1560
1812
|
}
|
|
1561
1813
|
|
|
1562
1814
|
.aparte-field-group:has(.aparte-field[aria-invalid='true']):focus-within {
|
|
1563
|
-
|
|
1815
|
+
outline-color: var(--aparte-error-solid);
|
|
1564
1816
|
}
|
|
1565
1817
|
|
|
1566
1818
|
/* ── Checkbox ────────────────────────────────────────────────────────────────── */
|
|
@@ -1584,7 +1836,6 @@ aparte-conversation-list {
|
|
|
1584
1836
|
--aparte-derived-ink: var(--aparte-on-intent);
|
|
1585
1837
|
/* No shared control-size token covers a checkbox yet (the button's own sizes are
|
|
1586
1838
|
button-specific), so its box is declared here. */
|
|
1587
|
-
--aparte-checkbox-size: 18px;
|
|
1588
1839
|
|
|
1589
1840
|
box-sizing: border-box;
|
|
1590
1841
|
appearance: none;
|
|
@@ -1595,7 +1846,7 @@ aparte-conversation-list {
|
|
|
1595
1846
|
place-content: center;
|
|
1596
1847
|
width: var(--aparte-checkbox-size);
|
|
1597
1848
|
height: var(--aparte-checkbox-size);
|
|
1598
|
-
border: var(--aparte-border-width) solid var(--aparte-border);
|
|
1849
|
+
border: var(--aparte-border-width) solid var(--aparte-border-control);
|
|
1599
1850
|
border-radius: var(--aparte-radius-sm);
|
|
1600
1851
|
background: var(--aparte-surface-1);
|
|
1601
1852
|
cursor: pointer;
|
|
@@ -1680,7 +1931,7 @@ aparte-conversation-list {
|
|
|
1680
1931
|
}
|
|
1681
1932
|
|
|
1682
1933
|
.aparte-checkbox:focus-visible {
|
|
1683
|
-
outline: var(--aparte-focus-outline-width) solid var(--aparte-
|
|
1934
|
+
outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);
|
|
1684
1935
|
outline-offset: var(--aparte-focus-outline-offset);
|
|
1685
1936
|
}
|
|
1686
1937
|
|
|
@@ -1710,8 +1961,6 @@ aparte-conversation-list {
|
|
|
1710
1961
|
used. `middle` and not `text-bottom`: the label may wrap. */
|
|
1711
1962
|
vertical-align: middle;
|
|
1712
1963
|
--aparte-radio-intent: var(--aparte-primary);
|
|
1713
|
-
--aparte-radio-size: 18px;
|
|
1714
|
-
--aparte-radio-dot-size: 8px;
|
|
1715
1964
|
|
|
1716
1965
|
box-sizing: border-box;
|
|
1717
1966
|
appearance: none;
|
|
@@ -1722,7 +1971,7 @@ aparte-conversation-list {
|
|
|
1722
1971
|
place-content: center;
|
|
1723
1972
|
width: var(--aparte-radio-size);
|
|
1724
1973
|
height: var(--aparte-radio-size);
|
|
1725
|
-
border: var(--aparte-border-width) solid var(--aparte-border);
|
|
1974
|
+
border: var(--aparte-border-width) solid var(--aparte-border-control);
|
|
1726
1975
|
border-radius: var(--aparte-radius-full);
|
|
1727
1976
|
background: var(--aparte-surface-1);
|
|
1728
1977
|
cursor: pointer;
|
|
@@ -1764,7 +2013,7 @@ aparte-conversation-list {
|
|
|
1764
2013
|
}
|
|
1765
2014
|
|
|
1766
2015
|
.aparte-radio:focus-visible {
|
|
1767
|
-
outline: var(--aparte-focus-outline-width) solid var(--aparte-
|
|
2016
|
+
outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);
|
|
1768
2017
|
outline-offset: var(--aparte-focus-outline-offset);
|
|
1769
2018
|
}
|
|
1770
2019
|
|
|
@@ -1795,9 +2044,6 @@ aparte-conversation-list {
|
|
|
1795
2044
|
has to live here where `:checked` can override it — a central default could never
|
|
1796
2045
|
react to the class + state combo. */
|
|
1797
2046
|
--aparte-switch-track: var(--aparte-surface-3);
|
|
1798
|
-
--aparte-switch-width: 40px;
|
|
1799
|
-
--aparte-switch-height: 22px;
|
|
1800
|
-
--aparte-switch-thumb-inset: 2px;
|
|
1801
2047
|
/* DERIVED from the track, not a fourth literal to keep in step with the other three:
|
|
1802
2048
|
the thumb used to be 16px in a 20px track with a 1px border and a 2px top inset —
|
|
1803
2049
|
2px of air above it and none below, visibly low ("le cercle pas centré"). The
|
|
@@ -1816,7 +2062,7 @@ aparte-conversation-list {
|
|
|
1816
2062
|
flex-shrink: 0;
|
|
1817
2063
|
inline-size: var(--aparte-switch-width);
|
|
1818
2064
|
block-size: var(--aparte-switch-height);
|
|
1819
|
-
border: var(--aparte-border-width) solid var(--aparte-border);
|
|
2065
|
+
border: var(--aparte-border-width) solid var(--aparte-border-control);
|
|
1820
2066
|
border-radius: var(--aparte-radius-full);
|
|
1821
2067
|
background: var(--aparte-switch-track);
|
|
1822
2068
|
cursor: pointer;
|
|
@@ -1864,7 +2110,7 @@ aparte-conversation-list {
|
|
|
1864
2110
|
}
|
|
1865
2111
|
|
|
1866
2112
|
.aparte-switch:focus-visible {
|
|
1867
|
-
outline: var(--aparte-focus-outline-width) solid var(--aparte-
|
|
2113
|
+
outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);
|
|
1868
2114
|
outline-offset: var(--aparte-focus-outline-offset);
|
|
1869
2115
|
}
|
|
1870
2116
|
|
|
@@ -1888,8 +2134,6 @@ aparte-conversation-list {
|
|
|
1888
2134
|
|
|
1889
2135
|
.aparte-range {
|
|
1890
2136
|
--aparte-range-intent: var(--aparte-primary);
|
|
1891
|
-
--aparte-range-thumb-size: 16px;
|
|
1892
|
-
--aparte-range-track-height: 4px;
|
|
1893
2137
|
|
|
1894
2138
|
appearance: none;
|
|
1895
2139
|
-webkit-appearance: none;
|
|
@@ -1959,11 +2203,11 @@ aparte-conversation-list {
|
|
|
1959
2203
|
.aparte-range:active::-moz-range-thumb { transform: scale(1.15); }
|
|
1960
2204
|
|
|
1961
2205
|
.aparte-range:focus-visible::-webkit-slider-thumb {
|
|
1962
|
-
outline: var(--aparte-focus-outline-width) solid var(--aparte-
|
|
2206
|
+
outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);
|
|
1963
2207
|
outline-offset: var(--aparte-focus-outline-offset);
|
|
1964
2208
|
}
|
|
1965
2209
|
.aparte-range:focus-visible::-moz-range-thumb {
|
|
1966
|
-
outline: var(--aparte-focus-outline-width) solid var(--aparte-
|
|
2210
|
+
outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);
|
|
1967
2211
|
outline-offset: var(--aparte-focus-outline-offset);
|
|
1968
2212
|
}
|
|
1969
2213
|
|
|
@@ -1984,6 +2228,7 @@ aparte-conversation-list {
|
|
|
1984
2228
|
|
|
1985
2229
|
.aparte-field-label {
|
|
1986
2230
|
display: inline-block;
|
|
2231
|
+
vertical-align: middle;
|
|
1987
2232
|
margin-block-end: var(--aparte-space-1);
|
|
1988
2233
|
font-size: var(--aparte-font-size-sm);
|
|
1989
2234
|
font-weight: var(--aparte-font-weight-medium);
|
|
@@ -2071,7 +2316,7 @@ aparte-conversation-list {
|
|
|
2071
2316
|
inline-size: var(--aparte-color-size);
|
|
2072
2317
|
block-size: var(--aparte-color-size);
|
|
2073
2318
|
padding: 0;
|
|
2074
|
-
border: var(--aparte-border-width) solid var(--aparte-border);
|
|
2319
|
+
border: var(--aparte-border-width) solid var(--aparte-border-control);
|
|
2075
2320
|
border-radius: var(--aparte-field-radius);
|
|
2076
2321
|
background: none;
|
|
2077
2322
|
cursor: pointer;
|
|
@@ -2081,7 +2326,7 @@ aparte-conversation-list {
|
|
|
2081
2326
|
.aparte-color::-moz-color-swatch { border: none; border-radius: var(--aparte-radius-sm); }
|
|
2082
2327
|
.aparte-color:focus-visible {
|
|
2083
2328
|
outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);
|
|
2084
|
-
outline-offset: var(--aparte-
|
|
2329
|
+
outline-offset: var(--aparte-focus-outline-offset);
|
|
2085
2330
|
}
|
|
2086
2331
|
.aparte-color:disabled { cursor: not-allowed; opacity: var(--aparte-disabled-opacity); }
|
|
2087
2332
|
|
|
@@ -2137,6 +2382,7 @@ aparte-conversation-list {
|
|
|
2137
2382
|
would invite a click. */
|
|
2138
2383
|
.aparte-output {
|
|
2139
2384
|
display: inline-block;
|
|
2385
|
+
vertical-align: middle;
|
|
2140
2386
|
padding: var(--aparte-output-padding);
|
|
2141
2387
|
border-radius: var(--aparte-radius-sm);
|
|
2142
2388
|
background: var(--aparte-output-bg);
|
|
@@ -2219,19 +2465,22 @@ aparte-conversation-list {
|
|
|
2219
2465
|
}
|
|
2220
2466
|
.aparte-field-choice:has(:focus-visible) {
|
|
2221
2467
|
outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);
|
|
2222
|
-
outline-offset: var(--aparte-
|
|
2468
|
+
outline-offset: var(--aparte-focus-outline-offset);
|
|
2223
2469
|
}
|
|
2224
2470
|
|
|
2225
2471
|
.aparte-field-choice__body { flex: 1 1 auto; min-width: 0; }
|
|
2226
2472
|
/* =========================================
|
|
2227
2473
|
aparte-avatar — sizes, shape, image + initials fallback
|
|
2228
2474
|
=========================================
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2475
|
+
`--aparte-avatar-size` is NOT invented here: it already exists centrally for the
|
|
2476
|
+
message avatar (bubble.css), and this component reads the same one — a generic
|
|
2477
|
+
avatar and a chat-message avatar are the same object, and a size modifier applied
|
|
2478
|
+
here resizes a role-coloured message avatar too. The corner, the initials and the
|
|
2479
|
+
group's overlap are FRACTIONS of that size (`--aparte-avatar-radius-ratio`,
|
|
2480
|
+
`--aparte-avatar-initials-ratio`, `--aparte-avatar-overlap-ratio`), computed on the
|
|
2481
|
+
element so a size modifier moves them: as absolute tokens the 40px avatar drew the
|
|
2482
|
+
same 11px initials as the 32px one, the corner drifted from squircle to square up
|
|
2483
|
+
the ramp, and a 6px overlap was a fifth of a small avatar and a tenth of a large one.
|
|
2235
2484
|
|
|
2236
2485
|
<span class="aparte-avatar aparte-avatar--sm">AP</span>
|
|
2237
2486
|
<span class="aparte-avatar">JD</span>
|
|
@@ -2246,16 +2495,17 @@ aparte-conversation-list {
|
|
|
2246
2495
|
.aparte-avatar {
|
|
2247
2496
|
position: relative;
|
|
2248
2497
|
display: inline-flex;
|
|
2498
|
+
vertical-align: middle;
|
|
2249
2499
|
flex-shrink: 0;
|
|
2250
2500
|
align-items: center;
|
|
2251
2501
|
justify-content: center;
|
|
2252
2502
|
overflow: hidden;
|
|
2253
2503
|
width: var(--aparte-avatar-size);
|
|
2254
2504
|
height: var(--aparte-avatar-size);
|
|
2255
|
-
border-radius: var(--aparte-avatar-radius);
|
|
2505
|
+
border-radius: calc(var(--aparte-avatar-size) * var(--aparte-avatar-radius-ratio));
|
|
2256
2506
|
background: var(--aparte-avatar-fallback-bg);
|
|
2257
2507
|
color: var(--aparte-avatar-fallback-color);
|
|
2258
|
-
font-size: var(--aparte-avatar-
|
|
2508
|
+
font-size: calc(var(--aparte-avatar-size) * var(--aparte-avatar-initials-ratio));
|
|
2259
2509
|
font-weight: var(--aparte-font-weight-semibold);
|
|
2260
2510
|
line-height: var(--aparte-line-height-none);
|
|
2261
2511
|
text-transform: uppercase;
|
|
@@ -2279,13 +2529,13 @@ aparte-conversation-list {
|
|
|
2279
2529
|
|
|
2280
2530
|
/* Square is the default shape and needs no class (it is what the message avatar
|
|
2281
2531
|
already renders); named anyway so a square avatar can say so rather than omit. */
|
|
2282
|
-
.aparte-avatar--square { border-radius: var(--aparte-avatar-radius); }
|
|
2532
|
+
.aparte-avatar--square { border-radius: calc(var(--aparte-avatar-size) * var(--aparte-avatar-radius-ratio)); }
|
|
2283
2533
|
.aparte-avatar--circle { border-radius: var(--aparte-radius-full); }
|
|
2284
2534
|
|
|
2285
|
-
.aparte-avatar--xs { --aparte-avatar-size: var(--aparte-avatar-size-xs);
|
|
2286
|
-
.aparte-avatar--sm { --aparte-avatar-size: var(--aparte-avatar-size-sm);
|
|
2287
|
-
.aparte-avatar--lg { --aparte-avatar-size: var(--aparte-avatar-size-lg);
|
|
2288
|
-
.aparte-avatar--xl { --aparte-avatar-size: var(--aparte-avatar-size-xl);
|
|
2535
|
+
.aparte-avatar--xs { --aparte-avatar-size: var(--aparte-avatar-size-xs); }
|
|
2536
|
+
.aparte-avatar--sm { --aparte-avatar-size: var(--aparte-avatar-size-sm); }
|
|
2537
|
+
.aparte-avatar--lg { --aparte-avatar-size: var(--aparte-avatar-size-lg); }
|
|
2538
|
+
.aparte-avatar--xl { --aparte-avatar-size: var(--aparte-avatar-size-xl); }
|
|
2289
2539
|
|
|
2290
2540
|
/* ── Group — a fan of avatars that read as one party. ─────────────────────────
|
|
2291
2541
|
No z-index is set: later siblings simply paint over earlier ones in normal
|
|
@@ -2293,13 +2543,14 @@ aparte-conversation-list {
|
|
|
2293
2543
|
the one thing z-index would otherwise be needed for. */
|
|
2294
2544
|
.aparte-avatar-group {
|
|
2295
2545
|
display: inline-flex;
|
|
2546
|
+
vertical-align: middle;
|
|
2296
2547
|
align-items: center;
|
|
2297
2548
|
}
|
|
2298
2549
|
.aparte-avatar-group > .aparte-avatar {
|
|
2299
2550
|
box-shadow: 0 0 0 var(--aparte-avatar-group-ring-width) var(--aparte-avatar-group-ring-color);
|
|
2300
2551
|
}
|
|
2301
2552
|
.aparte-avatar-group > .aparte-avatar:not(:first-child) {
|
|
2302
|
-
margin-inline-start: calc(var(--aparte-avatar-
|
|
2553
|
+
margin-inline-start: calc(var(--aparte-avatar-size) * var(--aparte-avatar-overlap-ratio) * -1);
|
|
2303
2554
|
}
|
|
2304
2555
|
.aparte-avatar-group > .aparte-avatar:hover {
|
|
2305
2556
|
z-index: var(--aparte-z-raised);
|
|
@@ -2418,6 +2669,8 @@ aparte-icon {
|
|
|
2418
2669
|
--aparte-derived-ink: var(--aparte-on-intent);
|
|
2419
2670
|
|
|
2420
2671
|
display: inline-flex;
|
|
2672
|
+
|
|
2673
|
+
vertical-align: middle;
|
|
2421
2674
|
align-items: center;
|
|
2422
2675
|
justify-content: center;
|
|
2423
2676
|
gap: var(--aparte-badge-gap);
|
|
@@ -2515,6 +2768,7 @@ aparte-icon {
|
|
|
2515
2768
|
.aparte-tag {
|
|
2516
2769
|
box-sizing: border-box;
|
|
2517
2770
|
display: inline-flex;
|
|
2771
|
+
vertical-align: middle;
|
|
2518
2772
|
align-items: center;
|
|
2519
2773
|
gap: var(--aparte-tag-gap);
|
|
2520
2774
|
max-width: 100%;
|
|
@@ -2577,8 +2831,8 @@ aparte-icon {
|
|
|
2577
2831
|
* <span class="aparte-thumbnail aparte-thumbnail--lg"><img class="aparte-thumbnail__image"
|
|
2578
2832
|
* src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 2 3'%3E%3Crect width='2' height='3' fill='%23b07d33'/%3E%3C/svg%3E"
|
|
2579
2833
|
* alt="A screenshot"></span>
|
|
2580
|
-
* <span class="aparte-thumbnail"><span class="aparte-
|
|
2581
|
-
* <span class="aparte-thumbnail aparte-thumbnail--sm"><span class="aparte-
|
|
2834
|
+
* <span class="aparte-thumbnail"><span class="aparte-thumbnail__label">PDF</span></span>
|
|
2835
|
+
* <span class="aparte-thumbnail aparte-thumbnail--sm"><span class="aparte-thumbnail__label">MD</span></span>
|
|
2582
2836
|
*
|
|
2583
2837
|
* Distinct from `.aparte-avatar`, which is the same shape for a different reason: an
|
|
2584
2838
|
* avatar stands for a PERSON and falls back to initials, so it carries type styling and
|
|
@@ -2685,14 +2939,8 @@ aparte-icon {
|
|
|
2685
2939
|
}
|
|
2686
2940
|
|
|
2687
2941
|
.aparte-thumb--image[role='button']:focus-visible {
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
.aparte-thumb__img {
|
|
2692
|
-
width: 100%;
|
|
2693
|
-
height: 100%;
|
|
2694
|
-
object-fit: cover;
|
|
2695
|
-
display: block;
|
|
2942
|
+
outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);
|
|
2943
|
+
outline-offset: var(--aparte-focus-outline-offset);
|
|
2696
2944
|
}
|
|
2697
2945
|
|
|
2698
2946
|
.aparte-thumb--file {
|
|
@@ -2701,13 +2949,6 @@ aparte-icon {
|
|
|
2701
2949
|
justify-content: center;
|
|
2702
2950
|
}
|
|
2703
2951
|
|
|
2704
|
-
.aparte-thumb__ext {
|
|
2705
|
-
font-size: var(--aparte-font-size-xs);
|
|
2706
|
-
font-weight: var(--aparte-font-weight-bold);
|
|
2707
|
-
letter-spacing: 0.03em;
|
|
2708
|
-
color: var(--aparte-text-muted);
|
|
2709
|
-
}
|
|
2710
|
-
|
|
2711
2952
|
.aparte-thumb__name {
|
|
2712
2953
|
position: absolute;
|
|
2713
2954
|
left: 0;
|
|
@@ -2738,9 +2979,13 @@ aparte-icon {
|
|
|
2738
2979
|
*/
|
|
2739
2980
|
|
|
2740
2981
|
.aparte-spinner {
|
|
2741
|
-
|
|
2742
2982
|
display: inline-block;
|
|
2983
|
+
vertical-align: middle;
|
|
2743
2984
|
flex-shrink: 0;
|
|
2985
|
+
/* The token is the ring's outer size, border included: without this the ring painted
|
|
2986
|
+
16/20/28 for tokens of 12/16/24 (the 2px stroke added on each side), and painted
|
|
2987
|
+
the token's value only on a host page with its own border-box reset. */
|
|
2988
|
+
box-sizing: border-box;
|
|
2744
2989
|
width: var(--aparte-spinner-size);
|
|
2745
2990
|
height: var(--aparte-spinner-size);
|
|
2746
2991
|
border: var(--aparte-spinner-thickness) solid currentColor;
|
|
@@ -2755,6 +3000,15 @@ aparte-icon {
|
|
|
2755
3000
|
|
|
2756
3001
|
.aparte-spinner--sm { --aparte-spinner-size: var(--aparte-spinner-size-sm); }
|
|
2757
3002
|
.aparte-spinner--lg { --aparte-spinner-size: var(--aparte-spinner-size-lg); }
|
|
3003
|
+
|
|
3004
|
+
/* Under reduced motion the loop stops outright. The duration tokens are reset to
|
|
3005
|
+
0.01ms in responsive.css, but that leaves `infinite` in place: a 0.01ms cycle
|
|
3006
|
+
is not stillness, it is a flicker. The descendant sweep there does not reach a
|
|
3007
|
+
recipe used outside aparté's own elements, so each looping recipe stops itself,
|
|
3008
|
+
the way display/icon.css already does. */
|
|
3009
|
+
@media (prefers-reduced-motion: reduce) {
|
|
3010
|
+
.aparte-spinner { animation: none; }
|
|
3011
|
+
}
|
|
2758
3012
|
/* =========================================
|
|
2759
3013
|
aparte-progress — determinate bar + indeterminate variant
|
|
2760
3014
|
=========================================
|
|
@@ -2779,7 +3033,9 @@ aparte-icon {
|
|
|
2779
3033
|
width: 100%;
|
|
2780
3034
|
height: var(--aparte-progress-height);
|
|
2781
3035
|
border-radius: var(--aparte-radius-full);
|
|
2782
|
-
|
|
3036
|
+
/* The track: a reference the value is read against, so it has to be seen. A surface
|
|
3037
|
+
level put it at 1.06:1 in light and 1.20:1 in dark — one token, two distances. */
|
|
3038
|
+
background: var(--aparte-track);
|
|
2783
3039
|
}
|
|
2784
3040
|
|
|
2785
3041
|
.aparte-progress__bar {
|
|
@@ -2797,6 +3053,21 @@ aparte-icon {
|
|
|
2797
3053
|
animation: aparte-progress-slide var(--aparte-duration-pulse) var(--aparte-ease) infinite;
|
|
2798
3054
|
}
|
|
2799
3055
|
|
|
3056
|
+
/* Under reduced motion the loop stops outright. The duration tokens are reset to
|
|
3057
|
+
0.01ms in responsive.css, but that leaves `infinite` in place: a 0.01ms cycle
|
|
3058
|
+
is not stillness, it is a flicker. The descendant sweep there does not reach a
|
|
3059
|
+
recipe used outside aparté's own elements, so each looping recipe stops itself,
|
|
3060
|
+
the way display/icon.css already does.
|
|
3061
|
+
Stopped, the sliding segment would sit at one spot and read as a value; the whole
|
|
3062
|
+
track filled says "busy" without naming an amount. */
|
|
3063
|
+
@media (prefers-reduced-motion: reduce) {
|
|
3064
|
+
.aparte-progress--indeterminate .aparte-progress__bar {
|
|
3065
|
+
animation: none;
|
|
3066
|
+
width: 100%;
|
|
3067
|
+
inset-inline-start: 0;
|
|
3068
|
+
}
|
|
3069
|
+
}
|
|
3070
|
+
|
|
2800
3071
|
/* inset-inline-start, not transform: translateX — a transform has no logical
|
|
2801
3072
|
axis, so an RTL page would slide the indeterminate segment backwards. Animating
|
|
2802
3073
|
a logical inset instead makes the sweep follow reading direction automatically. */
|
|
@@ -2829,10 +3100,15 @@ aparte-icon {
|
|
|
2829
3100
|
<div>
|
|
2830
3101
|
<span class="aparte-skeleton aparte-skeleton--text" aria-hidden="true"></span>
|
|
2831
3102
|
<span class="aparte-skeleton aparte-skeleton--text" aria-hidden="true"></span>
|
|
2832
|
-
<span class="aparte-skeleton aparte-skeleton--text" aria-hidden="true"></span>
|
|
3103
|
+
<span class="aparte-skeleton aparte-skeleton--text" style="max-inline-size: 60%" aria-hidden="true"></span>
|
|
2833
3104
|
</div>
|
|
2834
|
-
<span class="aparte-skeleton aparte-skeleton--rect" style="grid-column:
|
|
3105
|
+
<span class="aparte-skeleton aparte-skeleton--rect" style="grid-column: 2" aria-hidden="true"></span>
|
|
2835
3106
|
</div>
|
|
3107
|
+
|
|
3108
|
+
The last line is shorter because the last line of a paragraph is, and the block sits
|
|
3109
|
+
in the text column, under the lines it stands for — not across the avatar's. The
|
|
3110
|
+
rect's height is the family's token (`--aparte-skeleton-rect-height`), not an inline
|
|
3111
|
+
value that contradicts it.
|
|
2836
3112
|
*/
|
|
2837
3113
|
|
|
2838
3114
|
.aparte-skeleton {
|
|
@@ -2849,6 +3125,19 @@ aparte-icon {
|
|
|
2849
3125
|
animation: aparte-skeleton-shimmer var(--aparte-duration-pulse) linear infinite;
|
|
2850
3126
|
}
|
|
2851
3127
|
|
|
3128
|
+
/* Under reduced motion the loop stops outright. The duration tokens are reset to
|
|
3129
|
+
0.01ms in responsive.css, but that leaves `infinite` in place: a 0.01ms cycle
|
|
3130
|
+
is not stillness, it is a flicker. The descendant sweep there does not reach a
|
|
3131
|
+
recipe used outside aparté's own elements, so each looping recipe stops itself,
|
|
3132
|
+
the way display/icon.css already does.
|
|
3133
|
+
The shine goes with the shimmer: a stopped gradient would sit as a pale band. */
|
|
3134
|
+
@media (prefers-reduced-motion: reduce) {
|
|
3135
|
+
.aparte-skeleton {
|
|
3136
|
+
animation: none;
|
|
3137
|
+
background-image: none;
|
|
3138
|
+
}
|
|
3139
|
+
}
|
|
3140
|
+
|
|
2852
3141
|
.aparte-skeleton--text {
|
|
2853
3142
|
width: 100%;
|
|
2854
3143
|
height: var(--aparte-skeleton-text-height);
|
|
@@ -2856,7 +3145,7 @@ aparte-icon {
|
|
|
2856
3145
|
}
|
|
2857
3146
|
.aparte-skeleton--text + .aparte-skeleton--text { margin-block-start: var(--aparte-skeleton-text-gap); }
|
|
2858
3147
|
/* The ragged last line reads as "text", where a full-width block reads as a bar. */
|
|
2859
|
-
.aparte-skeleton--text:last-child { width: var(--aparte-skeleton-text-last-width); }
|
|
3148
|
+
.aparte-skeleton--text:last-child:not(:first-child) { width: var(--aparte-skeleton-text-last-width); }
|
|
2860
3149
|
|
|
2861
3150
|
.aparte-skeleton--circle {
|
|
2862
3151
|
width: var(--aparte-skeleton-circle-size);
|
|
@@ -2944,6 +3233,7 @@ aparte-icon {
|
|
|
2944
3233
|
<button class="aparte-alert__dismiss" aria-label="Dismiss"><aparte-icon name="close"></aparte-icon></button>
|
|
2945
3234
|
</div>
|
|
2946
3235
|
<div class="aparte-alert aparte-alert--danger" role="alert">
|
|
3236
|
+
<span class="aparte-alert__icon"><aparte-icon name="alertTriangle"></aparte-icon></span>
|
|
2947
3237
|
<div class="aparte-alert__body">
|
|
2948
3238
|
<p class="aparte-alert__message">The request was refused: no API key is set.</p>
|
|
2949
3239
|
</div>
|
|
@@ -3034,7 +3324,7 @@ aparte-icon {
|
|
|
3034
3324
|
}
|
|
3035
3325
|
.aparte-alert__dismiss:active:not(:disabled) { filter: brightness(0.93); }
|
|
3036
3326
|
.aparte-alert__dismiss:focus-visible {
|
|
3037
|
-
outline: var(--aparte-focus-outline-width) solid var(--aparte-
|
|
3327
|
+
outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);
|
|
3038
3328
|
outline-offset: var(--aparte-focus-outline-offset);
|
|
3039
3329
|
}
|
|
3040
3330
|
.aparte-alert__dismiss:disabled { opacity: var(--aparte-disabled-opacity); cursor: not-allowed; pointer-events: none; }
|
|
@@ -3073,6 +3363,12 @@ aparte-icon {
|
|
|
3073
3363
|
padding: var(--aparte-card-body-padding);
|
|
3074
3364
|
}
|
|
3075
3365
|
|
|
3366
|
+
/* The body owns its edges: a paragraph's block margins fold into the padding instead of
|
|
3367
|
+
stacking on it. The sheet's own example puts a <p> here, and the body band measured
|
|
3368
|
+
twice the height of the header and footer beside it. */
|
|
3369
|
+
.aparte-card__body > :first-child { margin-block-start: 0; }
|
|
3370
|
+
.aparte-card__body > :last-child { margin-block-end: 0; }
|
|
3371
|
+
|
|
3076
3372
|
.aparte-card__footer {
|
|
3077
3373
|
display: flex;
|
|
3078
3374
|
align-items: center;
|
|
@@ -3347,7 +3643,7 @@ aparte-icon {
|
|
|
3347
3643
|
}
|
|
3348
3644
|
|
|
3349
3645
|
.aparte-tabs__tab:focus-visible {
|
|
3350
|
-
outline: var(--aparte-focus-outline-width) solid var(--aparte-
|
|
3646
|
+
outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);
|
|
3351
3647
|
outline-offset: var(--aparte-focus-outline-offset);
|
|
3352
3648
|
}
|
|
3353
3649
|
|
|
@@ -3364,10 +3660,13 @@ aparte-icon {
|
|
|
3364
3660
|
|
|
3365
3661
|
.aparte-tabs__panel {
|
|
3366
3662
|
padding-block: var(--aparte-space-6);
|
|
3663
|
+
/* The tab's own inline inset, so the panel's text starts under the label of the tab
|
|
3664
|
+
that opened it rather than 11px to its left. */
|
|
3665
|
+
padding-inline: var(--aparte-space-5);
|
|
3367
3666
|
}
|
|
3368
3667
|
|
|
3369
3668
|
.aparte-tabs__panel:focus-visible {
|
|
3370
|
-
outline: var(--aparte-focus-outline-width) solid var(--aparte-
|
|
3669
|
+
outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);
|
|
3371
3670
|
outline-offset: var(--aparte-focus-outline-offset);
|
|
3372
3671
|
}
|
|
3373
3672
|
|
|
@@ -3391,6 +3690,9 @@ aparte-icon {
|
|
|
3391
3690
|
its own raised chip, rather than an underline beneath a plain row. Same markup,
|
|
3392
3691
|
same states; only the container and the active tab's fill change. */
|
|
3393
3692
|
.aparte-tabs--segmented {
|
|
3693
|
+
/* A control, not a bar: the track hugs its segments. As a block-level flex row it
|
|
3694
|
+
inherited its container's width — 1207px of track for 160px of chips at 1280. */
|
|
3695
|
+
display: inline-flex;
|
|
3394
3696
|
gap: 0;
|
|
3395
3697
|
padding: var(--aparte-space-1);
|
|
3396
3698
|
background: var(--aparte-surface-2);
|
|
@@ -3410,6 +3712,11 @@ aparte-icon {
|
|
|
3410
3712
|
.aparte-tabs--segmented .aparte-tabs__tab[aria-selected='true'] {
|
|
3411
3713
|
background: var(--aparte-surface-1);
|
|
3412
3714
|
color: var(--aparte-text);
|
|
3715
|
+
/* Raised RELATIVE to the track, not an absolute surface level: surface-1 over
|
|
3716
|
+
surface-2 read as raised in light and as sunken in dark, the same rule inverting
|
|
3717
|
+
its elevation with the theme. A 1px ring in the border colour says "on top" on
|
|
3718
|
+
either ground. */
|
|
3719
|
+
box-shadow: 0 0 0 var(--aparte-border-width) var(--aparte-border);
|
|
3413
3720
|
}
|
|
3414
3721
|
/* =========================================
|
|
3415
3722
|
aparte-accordion — stacked disclosures
|
|
@@ -3426,7 +3733,21 @@ aparte-icon {
|
|
|
3426
3733
|
Shipping
|
|
3427
3734
|
<aparte-icon name="expand"></aparte-icon>
|
|
3428
3735
|
</summary>
|
|
3429
|
-
<div class="aparte-accordion__panel">Free above 50
|
|
3736
|
+
<div class="aparte-accordion__panel">Free above 50 €, otherwise 4,90 €. Two to four working days.</div>
|
|
3737
|
+
</details>
|
|
3738
|
+
<details class="aparte-accordion__item">
|
|
3739
|
+
<summary class="aparte-accordion__header">
|
|
3740
|
+
Returns
|
|
3741
|
+
<aparte-icon name="expand"></aparte-icon>
|
|
3742
|
+
</summary>
|
|
3743
|
+
<div class="aparte-accordion__panel">Thirty days, in the original packaging.</div>
|
|
3744
|
+
</details>
|
|
3745
|
+
<details class="aparte-accordion__item">
|
|
3746
|
+
<summary class="aparte-accordion__header">
|
|
3747
|
+
Payment
|
|
3748
|
+
<aparte-icon name="expand"></aparte-icon>
|
|
3749
|
+
</summary>
|
|
3750
|
+
<div class="aparte-accordion__panel">Card, PayPal, or an invoice for a company account.</div>
|
|
3430
3751
|
</details>
|
|
3431
3752
|
</div>
|
|
3432
3753
|
|
|
@@ -3494,7 +3815,7 @@ aparte-icon {
|
|
|
3494
3815
|
}
|
|
3495
3816
|
|
|
3496
3817
|
.aparte-accordion__header:focus-visible {
|
|
3497
|
-
outline: var(--aparte-focus-outline-width) solid var(--aparte-
|
|
3818
|
+
outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);
|
|
3498
3819
|
outline-offset: var(--aparte-focus-outline-offset);
|
|
3499
3820
|
}
|
|
3500
3821
|
|
|
@@ -3504,13 +3825,22 @@ aparte-icon {
|
|
|
3504
3825
|
opacity: var(--aparte-disabled-opacity);
|
|
3505
3826
|
}
|
|
3506
3827
|
|
|
3507
|
-
|
|
3828
|
+
/* Two spellings for the chevron, because the header's documented markup is a bare
|
|
3829
|
+
`<aparte-icon>` and a renderer that wraps a raw glyph needs a class to hang it on:
|
|
3830
|
+
the wrapper `.aparte-accordion__icon` (the thinking block), or an `<aparte-icon>` /
|
|
3831
|
+
`svg.aparte-icon` sitting directly in the header. Both are sized and both turn — the
|
|
3832
|
+
documented form used to match neither rule, so the kit's own example showed an open
|
|
3833
|
+
panel under a chevron still pointing down. */
|
|
3834
|
+
.aparte-accordion__icon,
|
|
3835
|
+
.aparte-accordion__header > aparte-icon,
|
|
3836
|
+
.aparte-accordion__header > .aparte-icon {
|
|
3837
|
+
display: inline-flex;
|
|
3508
3838
|
flex-shrink: 0;
|
|
3509
|
-
/*
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3839
|
+
/* Fed to the glyph through its own knob, not drawn as a width/height: the icon
|
|
3840
|
+
recipe reads `--aparte-icon-size`, and a box declared here around it would leave
|
|
3841
|
+
the glyph at its loose default. The step is the one the button recipe uses for an
|
|
3842
|
+
icon glyph, so a disclosure chevron reads as the same weight of icon everywhere. */
|
|
3843
|
+
--aparte-icon-size: var(--aparte-space-8);
|
|
3514
3844
|
color: var(--aparte-text-muted);
|
|
3515
3845
|
transition: transform var(--aparte-duration-slow) var(--aparte-ease);
|
|
3516
3846
|
}
|
|
@@ -3519,7 +3849,11 @@ aparte-icon {
|
|
|
3519
3849
|
puts aria-expanded on the header itself; native <details> puts `open` on the
|
|
3520
3850
|
ancestor and gives the browser no attribute to select on at all. */
|
|
3521
3851
|
.aparte-accordion__header[aria-expanded='true'] .aparte-accordion__icon,
|
|
3522
|
-
|
|
3852
|
+
.aparte-accordion__header[aria-expanded='true'] > aparte-icon,
|
|
3853
|
+
.aparte-accordion__header[aria-expanded='true'] > .aparte-icon,
|
|
3854
|
+
details[open] > .aparte-accordion__header .aparte-accordion__icon,
|
|
3855
|
+
details[open] > .aparte-accordion__header > aparte-icon,
|
|
3856
|
+
details[open] > .aparte-accordion__header > .aparte-icon {
|
|
3523
3857
|
transform: rotate(180deg);
|
|
3524
3858
|
}
|
|
3525
3859
|
|
|
@@ -3593,10 +3927,13 @@ details[open] > .aparte-accordion__header .aparte-accordion__icon {
|
|
|
3593
3927
|
width: max-content;
|
|
3594
3928
|
min-width: var(--aparte-menu-min-width);
|
|
3595
3929
|
max-width: var(--aparte-menu-max-width);
|
|
3930
|
+
/* The two caps are the box's outer width, padding and border included — same rule
|
|
3931
|
+
as the popover, for the same reason. */
|
|
3932
|
+
box-sizing: border-box;
|
|
3596
3933
|
padding: var(--aparte-space-2);
|
|
3597
3934
|
background: var(--aparte-surface-1);
|
|
3598
3935
|
border: var(--aparte-border-width) solid var(--aparte-border);
|
|
3599
|
-
border-radius: var(--aparte-radius-
|
|
3936
|
+
border-radius: var(--aparte-radius-menu);
|
|
3600
3937
|
box-shadow: var(--aparte-menu-shadow);
|
|
3601
3938
|
color: var(--aparte-text);
|
|
3602
3939
|
z-index: var(--aparte-z-dropdown);
|
|
@@ -3642,7 +3979,7 @@ details[open] > .aparte-accordion__header .aparte-accordion__icon {
|
|
|
3642
3979
|
}
|
|
3643
3980
|
|
|
3644
3981
|
.aparte-menu__item:focus-visible {
|
|
3645
|
-
outline: var(--aparte-focus-outline-width) solid var(--aparte-
|
|
3982
|
+
outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);
|
|
3646
3983
|
outline-offset: var(--aparte-focus-outline-offset);
|
|
3647
3984
|
}
|
|
3648
3985
|
|
|
@@ -3655,14 +3992,14 @@ details[open] > .aparte-accordion__header .aparte-accordion__icon {
|
|
|
3655
3992
|
pointer-events: none;
|
|
3656
3993
|
}
|
|
3657
3994
|
|
|
3658
|
-
/* The check mark for menuitemcheckbox / menuitemradio.
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
a plain role="menuitem"
|
|
3662
|
-
rather than a fill or a box-shadow is deliberate: a
|
|
3663
|
-
which forced-colors mode preserves, so the checked state
|
|
3664
|
-
with no separate override. */
|
|
3665
|
-
.aparte-menu__item[aria-checked]::before {
|
|
3995
|
+
/* The check mark for menuitemcheckbox / menuitemradio. The gutter is reserved on EVERY
|
|
3996
|
+
item of a panel that holds a checkable one — as an invisible glyph — so all the
|
|
3997
|
+
labels of that panel share one edge. It used to be reserved per checkable item, and
|
|
3998
|
+
a plain role="menuitem" beside a checkable one started 16px further left: two label
|
|
3999
|
+
edges in one menu. Text colour rather than a fill or a box-shadow is deliberate: a
|
|
4000
|
+
glyph inherits `color`, which forced-colors mode preserves, so the checked state
|
|
4001
|
+
survives that mode with no separate override. */
|
|
4002
|
+
.aparte-menu:has(.aparte-menu__item[aria-checked]) .aparte-menu__item::before {
|
|
3666
4003
|
content: '✓';
|
|
3667
4004
|
flex-shrink: 0;
|
|
3668
4005
|
visibility: hidden;
|
|
@@ -3737,11 +4074,14 @@ details[open] > .aparte-accordion__header .aparte-accordion__icon {
|
|
|
3737
4074
|
display: flex;
|
|
3738
4075
|
flex-direction: column;
|
|
3739
4076
|
gap: var(--aparte-space-3);
|
|
4077
|
+
/* The cap is the box's outer width: it measured 342px for a documented 320 (padding
|
|
4078
|
+
and border outside the cap) until the recipe said so itself. */
|
|
4079
|
+
box-sizing: border-box;
|
|
3740
4080
|
max-width: var(--aparte-popover-max-width);
|
|
3741
4081
|
padding: var(--aparte-space-5);
|
|
3742
4082
|
background: var(--aparte-surface-1);
|
|
3743
4083
|
border: var(--aparte-border-width) solid var(--aparte-border);
|
|
3744
|
-
border-radius: var(--aparte-radius-
|
|
4084
|
+
border-radius: var(--aparte-radius-popover);
|
|
3745
4085
|
box-shadow: var(--aparte-popover-shadow);
|
|
3746
4086
|
color: var(--aparte-text);
|
|
3747
4087
|
font-size: var(--aparte-font-size-md);
|
|
@@ -3804,7 +4144,7 @@ details[open] > .aparte-accordion__header .aparte-accordion__icon {
|
|
|
3804
4144
|
background: var(--aparte-surface-1);
|
|
3805
4145
|
color: var(--aparte-text);
|
|
3806
4146
|
border: var(--aparte-border-width) solid var(--aparte-border);
|
|
3807
|
-
border-radius: var(--aparte-radius-
|
|
4147
|
+
border-radius: var(--aparte-radius-dialog);
|
|
3808
4148
|
box-shadow: var(--aparte-menu-shadow);
|
|
3809
4149
|
}
|
|
3810
4150
|
|
|
@@ -3880,11 +4220,20 @@ body:has(.aparte-dialog[open]) {
|
|
|
3880
4220
|
.aparte-dialog:modal {
|
|
3881
4221
|
width: 100%;
|
|
3882
4222
|
max-width: 100%;
|
|
3883
|
-
|
|
4223
|
+
/* As tall as its content, capped at the screen, sitting on the bottom edge: a sheet
|
|
4224
|
+
stretched to 100dvh held a label, a field and two buttons over 586px of nothing.
|
|
4225
|
+
`fit-content`, not `auto` — on a positioned box with both block insets set, an
|
|
4226
|
+
auto height FILLS them (CSS 2 §10.6.4: the auto margins go to 0 and the height
|
|
4227
|
+
is solved for), so `auto` measured the same 720 the explicit 100dvh had. What
|
|
4228
|
+
the UA gives a dialog is `fit-content`, and it is what lets the auto top margin
|
|
4229
|
+
push the sheet onto the bottom edge. And the safe areas: the ✕ used to sit 6px
|
|
4230
|
+
from the physical top. */
|
|
4231
|
+
height: fit-content;
|
|
3884
4232
|
max-height: 100dvh;
|
|
3885
|
-
margin: 0;
|
|
4233
|
+
margin: auto 0 0;
|
|
4234
|
+
padding-block: env(safe-area-inset-top, 0px) env(safe-area-inset-bottom, 0px);
|
|
3886
4235
|
border: 0;
|
|
3887
|
-
border-radius: 0;
|
|
4236
|
+
border-radius: var(--aparte-radius-dialog) var(--aparte-radius-dialog) 0 0;
|
|
3888
4237
|
}
|
|
3889
4238
|
}
|
|
3890
4239
|
|
|
@@ -3921,23 +4270,22 @@ body:has(.aparte-dialog[open]) {
|
|
|
3921
4270
|
and skips the dark-theme override block for that reason; the artifact "paper" tokens
|
|
3922
4271
|
are the same decision made for the same reason.
|
|
3923
4272
|
|
|
3924
|
-
<span
|
|
3925
|
-
<div class="aparte-tooltip" role="tooltip"
|
|
3926
|
-
style="position: absolute; bottom: calc(100% + 8px); left: 50%; translate: -50% 0">
|
|
4273
|
+
<span class="aparte-tooltip-anchor" style="margin-block-start: 2.5rem; margin-inline-start: 5rem">
|
|
4274
|
+
<div class="aparte-tooltip" data-side="top" role="tooltip">
|
|
3927
4275
|
Copy to clipboard
|
|
3928
4276
|
<span class="aparte-tooltip__arrow"></span>
|
|
3929
4277
|
</div>
|
|
3930
4278
|
<button class="aparte-btn aparte-btn--icon" aria-label="Copy"><aparte-icon name="copy"></aparte-icon></button>
|
|
3931
4279
|
</span>
|
|
3932
4280
|
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
4281
|
+
`data-side` places it — `top`, `bottom`, `start` or `end` — against the
|
|
4282
|
+
`.aparte-tooltip-anchor` that wraps the trigger, `--aparte-tooltip-gap` away, and turns
|
|
4283
|
+
the arrow to match. The two inline styles that used to do this in the example were a
|
|
4284
|
+
knob missing, not a division of labour: a demo that needs inline styles to work is a
|
|
4285
|
+
recipe with a parameter it forgot. What stays yours is the viewport: nothing here flips
|
|
4286
|
+
a tooltip that would leave the screen — that needs script, a positioning library's job.
|
|
4287
|
+
Without `data-side` the box is not positioned at all, as before, and its placement is
|
|
4288
|
+
yours (the one inline style left in the example is the demo's own margin).
|
|
3941
4289
|
|
|
3942
4290
|
`pointer-events: none` is not a detail: a tooltip describes what the pointer is over,
|
|
3943
4291
|
so it must never itself become that thing, or hovering it re-triggers the hover that
|
|
@@ -3954,11 +4302,17 @@ body:has(.aparte-dialog[open]) {
|
|
|
3954
4302
|
|
|
3955
4303
|
position: relative;
|
|
3956
4304
|
display: inline-block;
|
|
4305
|
+
vertical-align: middle;
|
|
4306
|
+
/* As wide as its label, up to the cap. With a `max-width` and no intrinsic width an
|
|
4307
|
+
absolutely-positioned chip shrinks to `min-content`: "Copy to clipboard" broke into
|
|
4308
|
+
two lines at every width, 71px wide — the widest word plus padding. */
|
|
4309
|
+
width: max-content;
|
|
4310
|
+
box-sizing: border-box;
|
|
3957
4311
|
max-width: var(--aparte-tooltip-max-width);
|
|
3958
4312
|
padding: var(--aparte-space-2) var(--aparte-space-4);
|
|
3959
4313
|
background: var(--aparte-tooltip-bg);
|
|
3960
4314
|
color: var(--aparte-tooltip-color);
|
|
3961
|
-
border-radius: var(--aparte-radius-
|
|
4315
|
+
border-radius: var(--aparte-radius-tooltip);
|
|
3962
4316
|
box-shadow: var(--aparte-tooltip-shadow);
|
|
3963
4317
|
font-size: var(--aparte-font-size-sm);
|
|
3964
4318
|
line-height: var(--aparte-line-height-snug);
|
|
@@ -3970,12 +4324,49 @@ body:has(.aparte-dialog[open]) {
|
|
|
3970
4324
|
pointer-events: none;
|
|
3971
4325
|
}
|
|
3972
4326
|
|
|
3973
|
-
/*
|
|
3974
|
-
it
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
4327
|
+
/* Where the box goes: the anchor is the trigger's wrapper and the tooltip is placed
|
|
4328
|
+
against it. `translate` centres the box on the axis across the side: with a logical
|
|
4329
|
+
inset of 50% the box's start edge sits at the anchor's middle, and moving it back by
|
|
4330
|
+
half its own size centres it — in both writing directions, because the inset flips
|
|
4331
|
+
with the direction and the translate is half the box either way. */
|
|
4332
|
+
.aparte-tooltip-anchor {
|
|
4333
|
+
position: relative;
|
|
4334
|
+
display: inline-block;
|
|
4335
|
+
vertical-align: middle;
|
|
4336
|
+
}
|
|
4337
|
+
|
|
4338
|
+
.aparte-tooltip[data-side] {
|
|
4339
|
+
position: absolute;
|
|
4340
|
+
}
|
|
4341
|
+
|
|
4342
|
+
.aparte-tooltip[data-side='top'] {
|
|
4343
|
+
inset-block-end: calc(100% + var(--aparte-tooltip-gap));
|
|
4344
|
+
inset-inline-start: 50%;
|
|
4345
|
+
translate: -50% 0;
|
|
4346
|
+
}
|
|
4347
|
+
|
|
4348
|
+
.aparte-tooltip[data-side='bottom'] {
|
|
4349
|
+
inset-block-start: calc(100% + var(--aparte-tooltip-gap));
|
|
4350
|
+
inset-inline-start: 50%;
|
|
4351
|
+
translate: -50% 0;
|
|
4352
|
+
}
|
|
4353
|
+
|
|
4354
|
+
.aparte-tooltip[data-side='start'] {
|
|
4355
|
+
inset-inline-end: calc(100% + var(--aparte-tooltip-gap));
|
|
4356
|
+
inset-block-start: 50%;
|
|
4357
|
+
translate: 0 -50%;
|
|
4358
|
+
}
|
|
4359
|
+
|
|
4360
|
+
.aparte-tooltip[data-side='end'] {
|
|
4361
|
+
inset-inline-start: calc(100% + var(--aparte-tooltip-gap));
|
|
4362
|
+
inset-block-start: 50%;
|
|
4363
|
+
translate: 0 -50%;
|
|
4364
|
+
}
|
|
4365
|
+
|
|
4366
|
+
/* The arrow points DOWN by default — the common case, a tooltip above the thing it
|
|
4367
|
+
labels — and each `data-side` turns it toward the anchor. Centring on the box's edge
|
|
4368
|
+
uses a logical inset plus a negative margin rather than a transform, so it re-centres
|
|
4369
|
+
under `direction: rtl` without a second rule. */
|
|
3979
4370
|
.aparte-tooltip__arrow {
|
|
3980
4371
|
position: absolute;
|
|
3981
4372
|
inset-inline-start: 50%;
|
|
@@ -3994,6 +4385,26 @@ body:has(.aparte-dialog[open]) {
|
|
|
3994
4385
|
inset-block-start: calc(var(--aparte-space-4) * -0.5);
|
|
3995
4386
|
}
|
|
3996
4387
|
|
|
4388
|
+
/* On the two inline sides the arrow sits on the edge that faces the anchor, centred
|
|
4389
|
+
vertically: the box is to the START of the trigger, so its arrow is on its END edge,
|
|
4390
|
+
and the reverse. */
|
|
4391
|
+
.aparte-tooltip[data-side='start'] .aparte-tooltip__arrow {
|
|
4392
|
+
inset-block-end: auto;
|
|
4393
|
+
inset-block-start: 50%;
|
|
4394
|
+
inset-inline-start: auto;
|
|
4395
|
+
inset-inline-end: calc(var(--aparte-space-4) * -0.5);
|
|
4396
|
+
margin-inline-start: 0;
|
|
4397
|
+
margin-block-start: calc(var(--aparte-space-4) * -0.5);
|
|
4398
|
+
}
|
|
4399
|
+
|
|
4400
|
+
.aparte-tooltip[data-side='end'] .aparte-tooltip__arrow {
|
|
4401
|
+
inset-block-end: auto;
|
|
4402
|
+
inset-block-start: 50%;
|
|
4403
|
+
inset-inline-start: calc(var(--aparte-space-4) * -0.5);
|
|
4404
|
+
margin-inline-start: 0;
|
|
4405
|
+
margin-block-start: calc(var(--aparte-space-4) * -0.5);
|
|
4406
|
+
}
|
|
4407
|
+
|
|
3997
4408
|
/* ── Forced colors ───────────────────────────────────────────────────────────────
|
|
3998
4409
|
The menu and the popover already carry a real border by default, so those two
|
|
3999
4410
|
boxes survive forced-colors mode once background and box-shadow are both
|
|
@@ -4036,10 +4447,16 @@ aparte-select {
|
|
|
4036
4447
|
align-items: center;
|
|
4037
4448
|
justify-content: space-between;
|
|
4038
4449
|
gap: var(--aparte-space-4);
|
|
4450
|
+
box-sizing: border-box;
|
|
4451
|
+
min-block-size: var(--aparte-field-size);
|
|
4039
4452
|
padding: var(--aparte-space-4) var(--aparte-space-6);
|
|
4453
|
+
/* Its own size: the trigger declared none and took the host page's, so every
|
|
4454
|
+
integrator saw a different select. The control step, like the field and the
|
|
4455
|
+
button. */
|
|
4456
|
+
font-size: var(--aparte-font-size-md);
|
|
4040
4457
|
background: var(--aparte-select-bg, var(--aparte-surface-1));
|
|
4041
|
-
border: var(--aparte-border-width) solid var(--aparte-select-border, var(--aparte-border));
|
|
4042
|
-
border-radius: var(--aparte-
|
|
4458
|
+
border: var(--aparte-border-width) solid var(--aparte-select-border, var(--aparte-border-control));
|
|
4459
|
+
border-radius: var(--aparte-radius-select);
|
|
4043
4460
|
cursor: pointer;
|
|
4044
4461
|
transition: border-color var(--aparte-duration-base), box-shadow var(--aparte-duration-base);
|
|
4045
4462
|
}
|
|
@@ -4048,15 +4465,16 @@ aparte-select {
|
|
|
4048
4465
|
border-color: var(--aparte-select-border-hover, var(--aparte-primary));
|
|
4049
4466
|
}
|
|
4050
4467
|
|
|
4051
|
-
.aparte-select-trigger:focus {
|
|
4052
|
-
outline:
|
|
4053
|
-
|
|
4054
|
-
box-shadow: var(--aparte-select-ring, var(--aparte-focus-ring));
|
|
4468
|
+
.aparte-select-trigger:focus-visible {
|
|
4469
|
+
outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);
|
|
4470
|
+
outline-offset: var(--aparte-focus-outline-offset);
|
|
4055
4471
|
}
|
|
4056
4472
|
|
|
4057
4473
|
aparte-select[disabled] .aparte-select-trigger {
|
|
4058
|
-
opacity: var(--aparte-disabled-opacity);
|
|
4059
4474
|
cursor: not-allowed;
|
|
4475
|
+
background: var(--aparte-surface-2);
|
|
4476
|
+
color: var(--aparte-text-muted);
|
|
4477
|
+
border-color: var(--aparte-border);
|
|
4060
4478
|
}
|
|
4061
4479
|
|
|
4062
4480
|
/* The label is a grid with ONE cell: the visible text and, under it, a hidden stack of
|
|
@@ -4116,8 +4534,10 @@ aparte-select[open] .aparte-select-chevron {
|
|
|
4116
4534
|
position: fixed;
|
|
4117
4535
|
/* top / left / width are set dynamically by _updatePosition() */
|
|
4118
4536
|
background: var(--aparte-select-dropdown-bg, var(--aparte-surface-1));
|
|
4119
|
-
|
|
4120
|
-
|
|
4537
|
+
/* The panel is a region, so its edge is the region border; `--aparte-select-border`
|
|
4538
|
+
is the TRIGGER's edge, a control's, and defaults to the control border. */
|
|
4539
|
+
border: var(--aparte-border-width) solid var(--aparte-border);
|
|
4540
|
+
border-radius: var(--aparte-radius-select);
|
|
4121
4541
|
box-shadow: var(--aparte-select-shadow);
|
|
4122
4542
|
z-index: var(--aparte-select-z, var(--aparte-z-dropdown));
|
|
4123
4543
|
max-height: var(--aparte-select-dropdown-max-height);
|
|
@@ -4141,8 +4561,11 @@ aparte-select[open] .aparte-select-chevron {
|
|
|
4141
4561
|
font-family: inherit;
|
|
4142
4562
|
}
|
|
4143
4563
|
|
|
4144
|
-
|
|
4145
|
-
|
|
4564
|
+
/* Inside, not outside: the field sits at the top of a scrolling panel, and an outset
|
|
4565
|
+
ring would be clipped by it. The one documented exception to the shared offset. */
|
|
4566
|
+
.aparte-select-search:focus-visible {
|
|
4567
|
+
outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);
|
|
4568
|
+
outline-offset: calc(-1 * var(--aparte-focus-outline-width));
|
|
4146
4569
|
}
|
|
4147
4570
|
|
|
4148
4571
|
/* Options container */
|
|
@@ -4279,6 +4702,7 @@ aparte-optgroup[collapsed] .aparte-optgroup-chevron {
|
|
|
4279
4702
|
}
|
|
4280
4703
|
|
|
4281
4704
|
.aparte-spinner-small {
|
|
4705
|
+
box-sizing: border-box;
|
|
4282
4706
|
width: var(--aparte-select-spinner-size);
|
|
4283
4707
|
height: var(--aparte-select-spinner-size);
|
|
4284
4708
|
border: 2px solid currentColor;
|
|
@@ -4286,6 +4710,12 @@ aparte-optgroup[collapsed] .aparte-optgroup-chevron {
|
|
|
4286
4710
|
border-radius: 50%;
|
|
4287
4711
|
/* `aparte-spin` is declared once, in base.css — see the shared-motion block there. */
|
|
4288
4712
|
animation: aparte-spin var(--aparte-duration-spin) linear infinite;
|
|
4713
|
+
}
|
|
4714
|
+
|
|
4715
|
+
/* The element's own sweep in responsive.css already stops this one; the rule is here
|
|
4716
|
+
so the recipe stops on its own terms too, like every other looping recipe. */
|
|
4717
|
+
@media (prefers-reduced-motion: reduce) {
|
|
4718
|
+
.aparte-spinner-small { animation: none; }
|
|
4289
4719
|
}/**
|
|
4290
4720
|
* AparteProgressSpinner — CSS
|
|
4291
4721
|
*
|
|
@@ -4311,16 +4741,23 @@ aparte-progress-spinner svg {
|
|
|
4311
4741
|
overflow: visible;
|
|
4312
4742
|
}
|
|
4313
4743
|
|
|
4744
|
+
/* The same stroke as the CSS ring (`.aparte-spinner`), in SCREEN pixels: without
|
|
4745
|
+
`vector-effect` a stroke is in viewBox units and scaled with the box — 2.5 of 24 was
|
|
4746
|
+
1.67px in a 16px spinner, antialiased everywhere and 50 % lighter than its sibling.
|
|
4747
|
+
The track is a visible reference in the control-edge colour: at 15 % of the ink a
|
|
4748
|
+
determinate 62 % was the percentage of a circle nobody could see. */
|
|
4314
4749
|
.aparte-spinner-track {
|
|
4315
4750
|
fill: none;
|
|
4316
|
-
stroke: var(--aparte-spinner-track,
|
|
4317
|
-
stroke-width: var(--aparte-spinner-
|
|
4751
|
+
stroke: var(--aparte-spinner-track, var(--aparte-border-control));
|
|
4752
|
+
stroke-width: var(--aparte-spinner-thickness);
|
|
4753
|
+
vector-effect: non-scaling-stroke;
|
|
4318
4754
|
}
|
|
4319
4755
|
|
|
4320
4756
|
.aparte-spinner-fill {
|
|
4321
4757
|
fill: none;
|
|
4322
4758
|
stroke: var(--aparte-spinner-color, currentColor);
|
|
4323
|
-
stroke-width: var(--aparte-spinner-
|
|
4759
|
+
stroke-width: var(--aparte-spinner-thickness);
|
|
4760
|
+
vector-effect: non-scaling-stroke;
|
|
4324
4761
|
stroke-linecap: round;
|
|
4325
4762
|
transition: stroke-dashoffset 0.25s var(--aparte-ease);
|
|
4326
4763
|
}
|
|
@@ -4368,6 +4805,10 @@ aparte-chat,
|
|
|
4368
4805
|
height: 100%;
|
|
4369
4806
|
width: 100%;
|
|
4370
4807
|
overflow: hidden;
|
|
4808
|
+
/* The height an author sets IS the box: with the bottom gap below outside it,
|
|
4809
|
+
`style="height: 24rem"` rendered 336px, and a split's seam hung 16px under both
|
|
4810
|
+
panels. */
|
|
4811
|
+
box-sizing: border-box;
|
|
4371
4812
|
/* The composer used to touch the bottom edge as soon as a conversation started: the
|
|
4372
4813
|
viewport gives 16px above the composer and there was nothing below it. An app
|
|
4373
4814
|
cannot fix this from outside without padding the container, which also shrinks the
|
|
@@ -4439,6 +4880,14 @@ aparte-chat[overlay-composer] > :not(aparte-chat-viewport),
|
|
|
4439
4880
|
z-index: var(--aparte-z-raised);
|
|
4440
4881
|
}
|
|
4441
4882
|
|
|
4883
|
+
/* A thing that floats has to be seen floating. The overlaid composer's shell shares the
|
|
4884
|
+
transcript's ground, so a z-index alone left it flat on the words it covers. */
|
|
4885
|
+
aparte-chat[overlay-composer] .aparte-composer-shell,
|
|
4886
|
+
[data-aparte-chat][overlay-composer] .aparte-composer-shell,
|
|
4887
|
+
.aparte-chat-container[overlay-composer] .aparte-composer-shell {
|
|
4888
|
+
box-shadow: var(--aparte-composer-overlay-shadow);
|
|
4889
|
+
}
|
|
4890
|
+
|
|
4442
4891
|
/* center-empty: the shell centers the composer as a welcome state until the first
|
|
4443
4892
|
message, then slides to the normal layout. The component toggles [data-empty]
|
|
4444
4893
|
on itself by watching its viewport — only `flex-grow` animates, so the composer
|
|
@@ -4461,6 +4910,17 @@ aparte-chat[center-empty][data-empty] > aparte-chat-viewport {
|
|
|
4461
4910
|
height: auto;
|
|
4462
4911
|
}
|
|
4463
4912
|
|
|
4913
|
+
/* And no block room at all: at `auto` the empty viewport still stood 32px tall — the
|
|
4914
|
+
rows' wrapper keeps its block padding with no row in it — so `center` centred three
|
|
4915
|
+
items of which the first was invisible, and the welcome group landed 16px low,
|
|
4916
|
+
measured. The padding goes, not the box: capping the viewport itself left a 32px
|
|
4917
|
+
scroll surface inside a 0px box, and "an empty transcript must not overflow" is a
|
|
4918
|
+
browser test every example runs. Inline geometry is kept — the composer's inset is
|
|
4919
|
+
measured from this element. */
|
|
4920
|
+
aparte-chat[center-empty][data-empty] > aparte-chat-viewport .aparte-messages-wrapper {
|
|
4921
|
+
padding-block: 0;
|
|
4922
|
+
}
|
|
4923
|
+
|
|
4464
4924
|
/* =========================================
|
|
4465
4925
|
aparte-chat-viewport
|
|
4466
4926
|
========================================= */
|
|
@@ -4527,16 +4987,16 @@ aparte-chat-viewport.aparte-viewport--framework::-webkit-scrollbar-thumb {
|
|
|
4527
4987
|
|
|
4528
4988
|
/* ─── Scroll-to-bottom button ─────────────────────────────────────────────── */
|
|
4529
4989
|
|
|
4530
|
-
/* The button wears `aparte-btn--lg aparte-btn--circle
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
is
|
|
4990
|
+
/* The button wears `aparte-btn--lg aparte-btn--circle`: `--lg` gives it the 20px glyph
|
|
4991
|
+
and the lg step of the scale (36px), `--circle` the round corner, and
|
|
4992
|
+
`--aparte-scroll-btn-size` (the same step, named for this button so a theme can move
|
|
4993
|
+
it alone) is fed to the recipe below. What stays here is only what is genuinely this
|
|
4994
|
+
button's — where it floats, and its own surface.
|
|
4534
4995
|
|
|
4535
4996
|
It used to redraw all three: its own width/height, its own `border-radius: 50%`, and
|
|
4536
4997
|
an icon-size override that existed only because the box was declared OUTSIDE the size
|
|
4537
|
-
scale, so the glyph stayed at the medium default — a 16px arrow in a 36px circle
|
|
4538
|
-
|
|
4539
|
-
the ratio fall out for free, and the override disappears with it.
|
|
4998
|
+
scale, so the glyph stayed at the medium default — a 16px arrow in a 36px circle.
|
|
4999
|
+
Naming the size on the recipe's own token makes the override disappear.
|
|
4540
5000
|
|
|
4541
5001
|
`--aparte-scroll-btn-size` stays the public knob, fed to the recipe. */
|
|
4542
5002
|
.aparte-scroll-btn {
|
|
@@ -4808,7 +5268,11 @@ aparte-chat-bubble {
|
|
|
4808
5268
|
box-sizing: border-box;
|
|
4809
5269
|
display: flex;
|
|
4810
5270
|
gap: var(--aparte-message-gap);
|
|
4811
|
-
|
|
5271
|
+
/* The two parts, not the composite `--aparte-message-padding` that used to join them
|
|
5272
|
+
on `:root`: a custom property is substituted where it is declared, so the composite
|
|
5273
|
+
froze both parts at the root and the narrow-container override of them in
|
|
5274
|
+
responsive.css never reached this row. */
|
|
5275
|
+
padding: var(--aparte-message-padding-block) var(--aparte-message-padding-inline);
|
|
4812
5276
|
width: 100%;
|
|
4813
5277
|
max-width: var(--aparte-message-max-width);
|
|
4814
5278
|
margin: 0 auto;
|
|
@@ -4896,6 +5360,11 @@ aparte-chat-bubble {
|
|
|
4896
5360
|
opacity: 0;
|
|
4897
5361
|
transition: opacity var(--aparte-duration-base) var(--aparte-ease);
|
|
4898
5362
|
height: var(--aparte-action-bar-btn-size);
|
|
5363
|
+
/* The INK meets the column, not the box: a glyph is centred in its 24px button, so
|
|
5364
|
+
its ink began half the slack — (24 − 12) / 2 — to the right of the paragraph above.
|
|
5365
|
+
Measured at +5 to +8px in 14 previews. The bar takes that slack back and the box
|
|
5366
|
+
overhangs into the message padding, where nothing else lives. */
|
|
5367
|
+
margin-inline-start: calc((var(--aparte-icon-size-sm) - var(--aparte-btn-size-sm)) / 2);
|
|
4899
5368
|
}
|
|
4900
5369
|
|
|
4901
5370
|
.aparte-message:hover .aparte-action-bar,
|
|
@@ -4960,8 +5429,6 @@ aparte-chat-bubble:last-of-type .aparte-message[data-role="assistant"]:not(.apar
|
|
|
4960
5429
|
inset-block-end: calc(-1 * (var(--aparte-message-padding-block) + var(--aparte-message-gap)));
|
|
4961
5430
|
margin: 0;
|
|
4962
5431
|
z-index: 1;
|
|
4963
|
-
/* Smaller than the last reply's 28px: this one lives in the padding. */
|
|
4964
|
-
--aparte-action-bar-btn-size: 24px;
|
|
4965
5432
|
}
|
|
4966
5433
|
}
|
|
4967
5434
|
|
|
@@ -5010,12 +5477,11 @@ aparte-chat-bubble:last-of-type .aparte-message[data-role="assistant"]:not(.apar
|
|
|
5010
5477
|
display: none;
|
|
5011
5478
|
}
|
|
5012
5479
|
|
|
5480
|
+
/* The arrows wear `--sm` and the recipe draws the box: this rule used to redraw
|
|
5481
|
+
width/height to a 20px token over it — two sources for one measure. */
|
|
5013
5482
|
.aparte-branch-prev,
|
|
5014
5483
|
.aparte-branch-next {
|
|
5015
5484
|
color: var(--aparte-branch-picker-btn-color);
|
|
5016
|
-
width: var(--aparte-branch-picker-btn-size);
|
|
5017
|
-
height: var(--aparte-branch-picker-btn-size);
|
|
5018
|
-
font-size: var(--aparte-branch-picker-btn-icon-size);
|
|
5019
5485
|
}
|
|
5020
5486
|
|
|
5021
5487
|
.aparte-branch-prev:hover:not(:disabled),
|
|
@@ -5024,9 +5490,11 @@ aparte-chat-bubble:last-of-type .aparte-message[data-role="assistant"]:not(.apar
|
|
|
5024
5490
|
background: var(--aparte-action-bar-btn-hover-bg);
|
|
5025
5491
|
}
|
|
5026
5492
|
|
|
5493
|
+
/* The button recipe draws the disabled state (a neutral ground, the muted ink); an
|
|
5494
|
+
opacity over it read the arrow at 1.74:1 — as absent, where the picker means "no
|
|
5495
|
+
further". `default` and not `not-allowed`: the end of a list is not a refusal. */
|
|
5027
5496
|
.aparte-branch-prev:disabled,
|
|
5028
5497
|
.aparte-branch-next:disabled {
|
|
5029
|
-
opacity: var(--aparte-disabled-opacity);
|
|
5030
5498
|
cursor: default;
|
|
5031
5499
|
}
|
|
5032
5500
|
|
|
@@ -5126,7 +5594,9 @@ aparte-chat-bubble[data-kind="compaction"] .aparte-message-content {
|
|
|
5126
5594
|
Unscoped so it greys the composer in EVERY host (vanilla shell + all wrappers),
|
|
5127
5595
|
not only a `[data-aparte-chat]` direct child. */
|
|
5128
5596
|
aparte-composer[data-model-gated] {
|
|
5129
|
-
|
|
5597
|
+
/* No group fade: each control draws its own disabled state (the editor's muted ink,
|
|
5598
|
+
the send button's neutral ground), so the text stays legible while it waits. */
|
|
5599
|
+
cursor: not-allowed;
|
|
5130
5600
|
}
|
|
5131
5601
|
aparte-composer[data-model-gated] aparte-composer-send {
|
|
5132
5602
|
pointer-events: none;
|
|
@@ -5282,7 +5752,7 @@ aparte-chat-viewport.aparte-viewport--framework::-webkit-scrollbar-thumb {
|
|
|
5282
5752
|
|
|
5283
5753
|
/* Disabled state */
|
|
5284
5754
|
.aparte-editor[contenteditable="false"] {
|
|
5285
|
-
|
|
5755
|
+
color: var(--aparte-text-muted);
|
|
5286
5756
|
cursor: not-allowed;
|
|
5287
5757
|
}
|
|
5288
5758
|
|
|
@@ -5323,9 +5793,9 @@ aparte-chat-viewport.aparte-viewport--framework::-webkit-scrollbar-thumb {
|
|
|
5323
5793
|
}
|
|
5324
5794
|
|
|
5325
5795
|
.aparte-send-button:disabled {
|
|
5326
|
-
opacity: var(--aparte-disabled-opacity);
|
|
5327
5796
|
cursor: not-allowed;
|
|
5328
|
-
background: var(--aparte-send-disabled-bg, var(--aparte-
|
|
5797
|
+
background: var(--aparte-send-disabled-bg, var(--aparte-surface-2));
|
|
5798
|
+
color: var(--aparte-text-muted);
|
|
5329
5799
|
}
|
|
5330
5800
|
|
|
5331
5801
|
/* =========================================
|
|
@@ -5419,11 +5889,10 @@ aparte-model-selector[hidden],
|
|
|
5419
5889
|
border-color: var(--aparte-primary);
|
|
5420
5890
|
}
|
|
5421
5891
|
|
|
5422
|
-
.aparte-provider-select:focus,
|
|
5423
|
-
.aparte-model-select:focus {
|
|
5424
|
-
outline:
|
|
5425
|
-
|
|
5426
|
-
box-shadow: var(--aparte-focus-ring);
|
|
5892
|
+
.aparte-provider-select:focus-visible,
|
|
5893
|
+
.aparte-model-select:focus-visible {
|
|
5894
|
+
outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);
|
|
5895
|
+
outline-offset: var(--aparte-focus-outline-offset);
|
|
5427
5896
|
}
|
|
5428
5897
|
|
|
5429
5898
|
/* Dark mode overrides for selects */
|
|
@@ -5466,13 +5935,14 @@ aparte-composer {
|
|
|
5466
5935
|
it would then be the containing block of the `position: fixed` select dropdown
|
|
5467
5936
|
that exists to escape `overflow: hidden` (select.css, and the same warning in
|
|
5468
5937
|
split.css). So below 520px the transcript tightens to 8px and this stays at 16.
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5938
|
+
So the composer does not guess: the viewport MEASURES where its rows start — the
|
|
5939
|
+
transcript's padding plus the scrollbar gutter it reserves on both edges, at
|
|
5940
|
+
whatever step its container query has taken — and publishes it on the chat host as
|
|
5941
|
+
`--aparte-transcript-inset`. Padding by that puts the composer's box on the row's
|
|
5942
|
+
box at every width. Measured before: 10px apart at 768, the gutter's half at 1280,
|
|
5943
|
+
three left edges 12px apart on the reading-column demo. The fallback is for a
|
|
5944
|
+
composer with no viewport beside it. */
|
|
5945
|
+
padding-inline: var(--aparte-transcript-inset, var(--aparte-viewport-padding));
|
|
5476
5946
|
}
|
|
5477
5947
|
|
|
5478
5948
|
aparte-composer-input {
|
|
@@ -5485,7 +5955,7 @@ aparte-composer-input {
|
|
|
5485
5955
|
aparte-composer-input .aparte-ci-editor {
|
|
5486
5956
|
display: block;
|
|
5487
5957
|
width: 100%;
|
|
5488
|
-
min-height: var(--aparte-composer-control-size
|
|
5958
|
+
min-height: var(--aparte-composer-control-size);
|
|
5489
5959
|
padding: var(--aparte-input-padding-y) var(--aparte-input-padding-x);
|
|
5490
5960
|
font-size: var(--aparte-input-font-size);
|
|
5491
5961
|
line-height: var(--aparte-input-line-height);
|
|
@@ -5516,7 +5986,7 @@ aparte-composer-input .aparte-ci-editor:empty::before {
|
|
|
5516
5986
|
}
|
|
5517
5987
|
|
|
5518
5988
|
aparte-composer-input .aparte-ci-editor[aria-disabled="true"] {
|
|
5519
|
-
|
|
5989
|
+
color: var(--aparte-text-muted);
|
|
5520
5990
|
cursor: not-allowed;
|
|
5521
5991
|
}
|
|
5522
5992
|
|
|
@@ -5608,7 +6078,7 @@ aparte-composer.aparte-is-dragover:not(:has(.aparte-composer-shell)) {
|
|
|
5608
6078
|
|
|
5609
6079
|
/* Inner row — flex container for input + buttons */
|
|
5610
6080
|
/* Action buttons sized to the input's single-line height — they derive from
|
|
5611
|
-
--aparte-composer-control-size (default
|
|
6081
|
+
--aparte-composer-control-size (default: the lg step, 36px; the touch target under a coarse pointer), so a theme resizes the whole
|
|
5612
6082
|
composer control set with ONE variable and alignment holds. Combined with
|
|
5613
6083
|
the row's `align-items: flex-end` this makes the buttons read as vertically
|
|
5614
6084
|
centered against a single line of text, and keeps them anchored to the
|
|
@@ -5640,7 +6110,11 @@ aparte-composer.aparte-is-dragover:not(:has(.aparte-composer-shell)) {
|
|
|
5640
6110
|
align-items: flex-end;
|
|
5641
6111
|
gap: var(--aparte-space-4);
|
|
5642
6112
|
padding: var(--aparte-space-4) var(--aparte-space-6);
|
|
5643
|
-
|
|
6113
|
+
/* The lg step at rest — 36px, what `--aparte-send-btn-size` says — and the touch
|
|
6114
|
+
target under a coarse pointer (responsive.css). It was 44 everywhere, which made
|
|
6115
|
+
the send button the one control the coarse-pointer block could not enlarge because
|
|
6116
|
+
it already was, and a 62px bar around 15px of text on a desktop. */
|
|
6117
|
+
--aparte-composer-row-control-size: var(--aparte-composer-control-size);
|
|
5644
6118
|
--aparte-btn-size: var(--aparte-composer-row-control-size);
|
|
5645
6119
|
}
|
|
5646
6120
|
|
|
@@ -5758,6 +6232,10 @@ aparte-composer-cancel .aparte-cc-button:hover {
|
|
|
5758
6232
|
`.aparte-accordion__header` / `__item` / `__panel` and adds these two lines: a
|
|
5759
6233
|
quieter face than a real accordion header, and the padding the left rail needs. */
|
|
5760
6234
|
.aparte-thinking-header {
|
|
6235
|
+
/* The chevron sits beside the label: the accordion recipe puts it at the row's far
|
|
6236
|
+
end, which is what an accordion is, but this is a one-line disclosure, and "Reasoning"
|
|
6237
|
+
and its chevron were 692px apart at 1280 (T18). */
|
|
6238
|
+
justify-content: flex-start;
|
|
5761
6239
|
padding: var(--aparte-space-1) 0 var(--aparte-space-1) var(--aparte-space-5);
|
|
5762
6240
|
font-size: var(--aparte-font-size-sm);
|
|
5763
6241
|
color: var(--aparte-thinking-text);
|
|
@@ -5789,7 +6267,7 @@ aparte-composer-cancel .aparte-cc-button:hover {
|
|
|
5789
6267
|
margin-inline-end: var(--aparte-space-4);
|
|
5790
6268
|
/* Logical property */
|
|
5791
6269
|
text-transform: uppercase;
|
|
5792
|
-
opacity:
|
|
6270
|
+
/* The header's ink is already the muted one; an `opacity: .7` on top muted it twice. */
|
|
5793
6271
|
}
|
|
5794
6272
|
|
|
5795
6273
|
|
|
@@ -5848,11 +6326,14 @@ aparte-composer-cancel .aparte-cc-button:hover {
|
|
|
5848
6326
|
flex: none;
|
|
5849
6327
|
width: var(--aparte-space-8);
|
|
5850
6328
|
height: var(--aparte-space-8);
|
|
5851
|
-
opacity: 0.5
|
|
5852
|
-
|
|
6329
|
+
/* Quiet is a colour, not an opacity: at 0.5 the chevron measured 3.00:1 — on the
|
|
6330
|
+
WCAG floor exactly — and it is the control that reveals a `delete_file`'s
|
|
6331
|
+
arguments. The muted ink at rest, the full ink under the pointer. */
|
|
6332
|
+
color: var(--aparte-text-muted);
|
|
6333
|
+
transition: transform var(--aparte-duration-base), color var(--aparte-duration-base);
|
|
5853
6334
|
}
|
|
5854
6335
|
.aparte-tool-toggle svg { width: 100%; height: 100%; display: block; }
|
|
5855
|
-
.aparte-tool-summary:hover .aparte-tool-toggle {
|
|
6336
|
+
.aparte-tool-summary:hover .aparte-tool-toggle { color: var(--aparte-text); }
|
|
5856
6337
|
details[open] > .aparte-tool-summary .aparte-tool-toggle { transform: rotate(180deg); }
|
|
5857
6338
|
/* A rule down the leading edge, so the disclosure reads as belonging to the
|
|
5858
6339
|
pill above it rather than continuing the message text below. Without it
|
|
@@ -5867,7 +6348,7 @@ details[open] > .aparte-tool-summary .aparte-tool-toggle { transform: rotate(180
|
|
|
5867
6348
|
}
|
|
5868
6349
|
.aparte-tool-part-label {
|
|
5869
6350
|
display: block; font-size: var(--aparte-font-size-xs); text-transform: uppercase;
|
|
5870
|
-
letter-spacing: 0.04em;
|
|
6351
|
+
letter-spacing: 0.04em; color: var(--aparte-text-muted); margin-bottom: var(--aparte-space-1);
|
|
5871
6352
|
}
|
|
5872
6353
|
/* A tool's input and result WRAP. They are a message, a path, a JSON object — not
|
|
5873
6354
|
code to read line by line — and a `<pre>` keeps `white-space: pre` by default, so
|
|
@@ -5889,6 +6370,7 @@ details[open] > .aparte-tool-summary .aparte-tool-toggle { transform: rotate(180
|
|
|
5889
6370
|
green, which made a finished step shout louder than the reply it belongs to. */
|
|
5890
6371
|
.aparte-tool-label {
|
|
5891
6372
|
display: inline-flex;
|
|
6373
|
+
vertical-align: middle;
|
|
5892
6374
|
align-items: center;
|
|
5893
6375
|
gap: var(--aparte-space-3);
|
|
5894
6376
|
min-width: 0;
|
|
@@ -5905,7 +6387,7 @@ details[open] > .aparte-tool-summary .aparte-tool-toggle { transform: rotate(180
|
|
|
5905
6387
|
place-items: center;
|
|
5906
6388
|
width: 1em; height: 1em;
|
|
5907
6389
|
flex: none;
|
|
5908
|
-
|
|
6390
|
+
color: var(--aparte-text-muted);
|
|
5909
6391
|
}
|
|
5910
6392
|
.aparte-tool-icon svg { width: 1em; height: 1em; display: block; }
|
|
5911
6393
|
.aparte-tool-name { font-family: var(--aparte-code-font-family); }
|
|
@@ -5913,9 +6395,12 @@ details[open] > .aparte-tool-summary .aparte-tool-toggle { transform: rotate(180
|
|
|
5913
6395
|
Pushed to the far end by DOM order plus an auto inline-start margin, which is
|
|
5914
6396
|
the placement rule this repo already settled on: no left/right in a name, so
|
|
5915
6397
|
a right-to-left locale mirrors it for free. */
|
|
6398
|
+
/* Beside the tool's name, not at the row's far end: with `margin-inline-start: auto` the
|
|
6399
|
+
state word drifted 63px from the name at 375 and 596px at 1280. The state belongs to
|
|
6400
|
+
the label (T18). */
|
|
5916
6401
|
.aparte-tool-state {
|
|
5917
|
-
display: inline-flex;
|
|
5918
|
-
|
|
6402
|
+
display: inline-flex;
|
|
6403
|
+
vertical-align: middle; align-items: center; gap: var(--aparte-space-2);
|
|
5919
6404
|
font-size: var(--aparte-font-size-sm); font-weight: var(--aparte-font-weight-medium);
|
|
5920
6405
|
color: var(--aparte-text-muted);
|
|
5921
6406
|
user-select: none;
|
|
@@ -5951,44 +6436,22 @@ details[open] > .aparte-tool-summary .aparte-tool-toggle { transform: rotate(180
|
|
|
5951
6436
|
}
|
|
5952
6437
|
|
|
5953
6438
|
/* Pipeline waiting */
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
height: var(--aparte-error-icon-size);
|
|
5965
|
-
}
|
|
5966
|
-
|
|
5967
|
-
.aparte-error-content {
|
|
5968
|
-
flex: 1;
|
|
5969
|
-
min-width: 0;
|
|
5970
|
-
}
|
|
5971
|
-
|
|
5972
|
-
.aparte-error-title {
|
|
5973
|
-
font-weight: var(--aparte-font-weight-semibold);
|
|
5974
|
-
font-size: var(--aparte-font-size-lg);
|
|
5975
|
-
margin-bottom: var(--aparte-space-2);
|
|
5976
|
-
color: var(--aparte-error-title);
|
|
5977
|
-
}
|
|
5978
|
-
|
|
5979
|
-
.aparte-error-message {
|
|
5980
|
-
font-size: var(--aparte-font-size-lg);
|
|
5981
|
-
line-height: var(--aparte-line-height-normal);
|
|
5982
|
-
}
|
|
5983
|
-
|
|
5984
|
-
.aparte-error-details {
|
|
5985
|
-
margin-top: var(--aparte-space-4);
|
|
5986
|
-
padding-top: var(--aparte-space-4);
|
|
5987
|
-
border-top: var(--aparte-border-width) solid var(--aparte-error-border);
|
|
6439
|
+
/* The error segment WEARS the alert recipe — frame and parts (`aparte-alert__icon`,
|
|
6440
|
+
`__body`, `__title`, `__message` come from display/alert.css). It used to put the
|
|
6441
|
+
recipe's class on its root and redraw every part under classes of its own, with a 20px
|
|
6442
|
+
literal for the icon among derived tokens and a title colour nothing else used. What is
|
|
6443
|
+
genuinely the segment's own is the one part the alert has no word for: the details
|
|
6444
|
+
block — a transport's stack, a status line — set as code under the message. */
|
|
6445
|
+
.aparte-segment-error__details {
|
|
6446
|
+
margin: var(--aparte-space-4) 0 0;
|
|
6447
|
+
padding-block-start: var(--aparte-space-4);
|
|
6448
|
+
border-block-start: var(--aparte-border-width) solid var(--aparte-error-border);
|
|
5988
6449
|
font-family: var(--aparte-code-font-family);
|
|
5989
6450
|
font-size: var(--aparte-font-size-sm);
|
|
5990
|
-
|
|
6451
|
+
line-height: var(--aparte-line-height-normal);
|
|
6452
|
+
color: var(--aparte-text-secondary);
|
|
5991
6453
|
white-space: pre-wrap;
|
|
6454
|
+
overflow-wrap: anywhere;
|
|
5992
6455
|
}
|
|
5993
6456
|
|
|
5994
6457
|
/* ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -6102,7 +6565,7 @@ details[open] > .aparte-tool-summary .aparte-tool-toggle { transform: rotate(180
|
|
|
6102
6565
|
a consumer's screenshot, then here. The padding gives the ring that much room and
|
|
6103
6566
|
the negative margin gives it back to the layout, so nothing moves; `scroll-padding`
|
|
6104
6567
|
keeps a keyboard-focused option's ring inside the viewport when the list scrolls. */
|
|
6105
|
-
--aparte-elic-ring-room: calc(var(--aparte-focus-outline-width) + var(--aparte-
|
|
6568
|
+
--aparte-elic-ring-room: calc(var(--aparte-focus-outline-width) + var(--aparte-focus-outline-offset));
|
|
6106
6569
|
padding: var(--aparte-elic-ring-room);
|
|
6107
6570
|
margin: calc(-1 * var(--aparte-elic-ring-room));
|
|
6108
6571
|
scroll-padding: var(--aparte-elic-ring-room);
|
|
@@ -6146,7 +6609,10 @@ details[open] > .aparte-tool-summary .aparte-tool-toggle { transform: rotate(180
|
|
|
6146
6609
|
.aparte-elic-options {
|
|
6147
6610
|
display: flex;
|
|
6148
6611
|
flex-direction: column;
|
|
6149
|
-
|
|
6612
|
+
/* At least the focus ring's outset: at 2px the next row painted over the bottom of
|
|
6613
|
+
the focused row's ring and ~8px of it survived. Any list of options that wears an
|
|
6614
|
+
outset ring needs a gap at least as wide as the offset. */
|
|
6615
|
+
gap: var(--aparte-space-3);
|
|
6150
6616
|
}
|
|
6151
6617
|
|
|
6152
6618
|
/* The row is `.aparte-field-choice`; the panel only tightens its padding, because a
|
|
@@ -6220,22 +6686,19 @@ textarea.aparte-elic-text {
|
|
|
6220
6686
|
min-height: var(--aparte-elic-textarea-min-height);
|
|
6221
6687
|
}
|
|
6222
6688
|
|
|
6223
|
-
.aparte-elic-other-input:focus,
|
|
6224
|
-
.aparte-elic-text:focus {
|
|
6225
|
-
border-color: var(--aparte-primary);
|
|
6226
|
-
}
|
|
6227
|
-
|
|
6228
6689
|
.aparte-elic-option--recommended {
|
|
6229
6690
|
border-color: var(--aparte-primary);
|
|
6230
6691
|
background: color-mix(in srgb, var(--aparte-primary) 6%, transparent);
|
|
6231
6692
|
}
|
|
6232
6693
|
/* One ring at a time. The recommended option is the first to receive focus, and its
|
|
6233
6694
|
tinted border under the recipe's focus ring read as a double outline (UI audit
|
|
6234
|
-
2026-08-28, §8.3 #9). While it holds focus the ring is the
|
|
6235
|
-
|
|
6695
|
+
2026-08-28, §8.3 #9). While it holds focus the ring is the one edge, so the border
|
|
6696
|
+
steps aside — and ONLY the border. This rule used to clear the background too, which
|
|
6697
|
+
made the recommended row the only one in the list with no ground at all: sampled on
|
|
6698
|
+
the preview it rendered the page byte for byte while its sisters did not. The
|
|
6699
|
+
recommended state draws in addition to the resting state, never instead of it. */
|
|
6236
6700
|
.aparte-elic-option--recommended:focus-within {
|
|
6237
|
-
|
|
6238
|
-
border-color: var(--aparte-border-focus);
|
|
6701
|
+
border-color: transparent;
|
|
6239
6702
|
}
|
|
6240
6703
|
|
|
6241
6704
|
/* The whole-request escape, in the corner and out of the way of anything that
|
|
@@ -6344,7 +6807,7 @@ textarea.aparte-elic-text {
|
|
|
6344
6807
|
|
|
6345
6808
|
.aparte-approval-args:focus-visible {
|
|
6346
6809
|
outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);
|
|
6347
|
-
outline-offset:
|
|
6810
|
+
outline-offset: var(--aparte-focus-outline-offset);
|
|
6348
6811
|
}
|
|
6349
6812
|
|
|
6350
6813
|
/* The refusal's reason, in the user's words. Quiet by default and one line tall: it is
|
|
@@ -6364,9 +6827,9 @@ textarea.aparte-elic-text {
|
|
|
6364
6827
|
min-height: 1.6em;
|
|
6365
6828
|
}
|
|
6366
6829
|
|
|
6367
|
-
.aparte-approval-instruction:focus {
|
|
6368
|
-
outline:
|
|
6369
|
-
|
|
6830
|
+
.aparte-approval-instruction:focus-visible {
|
|
6831
|
+
outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);
|
|
6832
|
+
outline-offset: var(--aparte-focus-outline-offset);
|
|
6370
6833
|
}
|
|
6371
6834
|
|
|
6372
6835
|
.aparte-approval-instruction::placeholder {
|
|
@@ -6515,8 +6978,8 @@ textarea.aparte-elic-text {
|
|
|
6515
6978
|
/* Inside the edge: the list clips horizontally (`overflow-x: hidden` in base.css), and
|
|
6516
6979
|
a ring drawn outside the row was cut on both sides. */
|
|
6517
6980
|
.aparte-conv-item__select:focus-visible {
|
|
6518
|
-
outline: var(--aparte-focus-outline-width) solid var(--aparte-
|
|
6519
|
-
outline-offset:
|
|
6981
|
+
outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);
|
|
6982
|
+
outline-offset: var(--aparte-focus-outline-offset);
|
|
6520
6983
|
}
|
|
6521
6984
|
|
|
6522
6985
|
.aparte-conv-item__title {
|
|
@@ -6537,15 +7000,22 @@ textarea.aparte-elic-text {
|
|
|
6537
7000
|
font-weight: var(--aparte-conv-item-font-weight-active);
|
|
6538
7001
|
}
|
|
6539
7002
|
|
|
7003
|
+
/* The selection mark: a bar the width of `--aparte-mark-bar` down the row's start
|
|
7004
|
+
edge. Painted as the first pixels of a pseudo the SIZE of the row that inherits its
|
|
7005
|
+
radius, so the bar follows the curve — drawn as a 2px box of its own it stood square
|
|
7006
|
+
in a 9px corner and a sliver of the page's ground showed between the two. Not
|
|
7007
|
+
`overflow: hidden` on the row: that would clip the title button's focus ring. */
|
|
6540
7008
|
.aparte-conv-item--active::before {
|
|
6541
7009
|
content: "";
|
|
6542
7010
|
position: absolute;
|
|
6543
|
-
inset
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
background: var(--aparte-primary-ink);
|
|
7011
|
+
inset: 0;
|
|
7012
|
+
border-radius: inherit;
|
|
7013
|
+
background: linear-gradient(to right, var(--aparte-primary-ink) var(--aparte-mark-bar), transparent 0);
|
|
6547
7014
|
pointer-events: none;
|
|
6548
7015
|
}
|
|
7016
|
+
.aparte-conv-item--active:dir(rtl)::before {
|
|
7017
|
+
background: linear-gradient(to left, var(--aparte-primary-ink) var(--aparte-mark-bar), transparent 0);
|
|
7018
|
+
}
|
|
6549
7019
|
|
|
6550
7020
|
.aparte-conv-item--archived .aparte-conv-item__title {
|
|
6551
7021
|
font-style: italic;
|
|
@@ -6559,8 +7029,8 @@ textarea.aparte-elic-text {
|
|
|
6559
7029
|
.aparte-conv-item__more {
|
|
6560
7030
|
flex-shrink: 0;
|
|
6561
7031
|
opacity: 0;
|
|
6562
|
-
|
|
6563
|
-
|
|
7032
|
+
/* Fed to the recipe, not drawn here: the token names the size, the button draws it. */
|
|
7033
|
+
--aparte-btn-size: var(--aparte-conv-action-btn-size);
|
|
6564
7034
|
color: var(--aparte-text-muted);
|
|
6565
7035
|
}
|
|
6566
7036
|
|
|
@@ -6638,6 +7108,11 @@ aparte-suggestions[data-empty] {
|
|
|
6638
7108
|
flex-wrap: wrap;
|
|
6639
7109
|
gap: var(--aparte-space-2);
|
|
6640
7110
|
padding-block: var(--aparte-space-2);
|
|
7111
|
+
/* The composer's column: past its 800px cap the chips no longer followed the composer,
|
|
7112
|
+
188px of offset on a wide host. Same measure, same centring. */
|
|
7113
|
+
box-sizing: border-box;
|
|
7114
|
+
max-inline-size: var(--aparte-message-max-width);
|
|
7115
|
+
margin-inline: auto;
|
|
6641
7116
|
}
|
|
6642
7117
|
|
|
6643
7118
|
/* A starter's label is a short phrase; it must not become a two-line chip. */
|
|
@@ -6673,6 +7148,9 @@ aparte-context[data-empty] {
|
|
|
6673
7148
|
.aparte-context .aparte-progress {
|
|
6674
7149
|
flex: 1;
|
|
6675
7150
|
min-width: 4rem;
|
|
7151
|
+
/* A gauge has a measure: unbounded, the bar stretched to 1046×4px across a toolbar,
|
|
7152
|
+
88 % of its width for a line that says one number. */
|
|
7153
|
+
max-inline-size: 16rem;
|
|
6676
7154
|
}
|
|
6677
7155
|
|
|
6678
7156
|
.aparte-context__text {
|
|
@@ -6702,10 +7180,13 @@ aparte-context[data-level="danger"] .aparte-progress__bar {
|
|
|
6702
7180
|
transform: rotate(-90deg);
|
|
6703
7181
|
}
|
|
6704
7182
|
|
|
7183
|
+
/* As heavy as the bar variant, in screen pixels: the same gauge was drawn at two
|
|
7184
|
+
weights — a 4px bar and a 2.44px antialiased ring. */
|
|
6705
7185
|
.aparte-context__track,
|
|
6706
7186
|
.aparte-context__value {
|
|
6707
7187
|
fill: none;
|
|
6708
|
-
stroke-width: var(--aparte-
|
|
7188
|
+
stroke-width: var(--aparte-progress-height);
|
|
7189
|
+
vector-effect: non-scaling-stroke;
|
|
6709
7190
|
}
|
|
6710
7191
|
|
|
6711
7192
|
.aparte-context__track {
|
|
@@ -6785,7 +7266,9 @@ aparte-scroll-rail[data-empty] {
|
|
|
6785
7266
|
padding: 0;
|
|
6786
7267
|
border: 0;
|
|
6787
7268
|
border-radius: var(--aparte-radius-full);
|
|
6788
|
-
|
|
7269
|
+
/* A tick is a thing you can press; at rest in the region colour it sat at 1.15:1, a
|
|
7270
|
+
14×2px dash under the perception threshold. */
|
|
7271
|
+
background: var(--aparte-border-control);
|
|
6789
7272
|
cursor: pointer;
|
|
6790
7273
|
transition:
|
|
6791
7274
|
width var(--aparte-duration-fast) var(--aparte-ease),
|
|
@@ -6808,7 +7291,7 @@ aparte-scroll-rail[data-empty] {
|
|
|
6808
7291
|
}
|
|
6809
7292
|
|
|
6810
7293
|
.aparte-scroll-rail__tick:focus-visible {
|
|
6811
|
-
outline: var(--aparte-focus-outline-width) solid var(--aparte-
|
|
7294
|
+
outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);
|
|
6812
7295
|
outline-offset: var(--aparte-focus-outline-offset);
|
|
6813
7296
|
}
|
|
6814
7297
|
|
|
@@ -6891,7 +7374,10 @@ aparte-sidebar,
|
|
|
6891
7374
|
display: flex;
|
|
6892
7375
|
align-items: center;
|
|
6893
7376
|
gap: var(--aparte-space-3);
|
|
6894
|
-
|
|
7377
|
+
/* Inline, every region reads `--aparte-sidebar-inset`: the header and footer padded
|
|
7378
|
+
16 while the search and the body padded 12, and the column's content stood on two
|
|
7379
|
+
vertical axes 4px apart — measured as four left edges on the app-shell demo. */
|
|
7380
|
+
padding: var(--aparte-space-4) var(--aparte-sidebar-inset) var(--aparte-space-2);
|
|
6895
7381
|
}
|
|
6896
7382
|
|
|
6897
7383
|
.aparte-sidebar__brand {
|
|
@@ -6904,8 +7390,8 @@ aparte-sidebar,
|
|
|
6904
7390
|
}
|
|
6905
7391
|
|
|
6906
7392
|
.aparte-sidebar__search {
|
|
6907
|
-
margin: var(--aparte-space-2) var(--aparte-
|
|
6908
|
-
min-width: calc(var(--aparte-sidebar-width) - 2 * var(--aparte-
|
|
7393
|
+
margin: var(--aparte-space-2) var(--aparte-sidebar-inset);
|
|
7394
|
+
min-width: calc(var(--aparte-sidebar-width) - 2 * var(--aparte-sidebar-inset));
|
|
6909
7395
|
}
|
|
6910
7396
|
|
|
6911
7397
|
/* The body is the one region that scrolls; the list inside keeps its own gap. */
|
|
@@ -6914,7 +7400,7 @@ aparte-sidebar,
|
|
|
6914
7400
|
min-height: 0;
|
|
6915
7401
|
overflow-y: auto;
|
|
6916
7402
|
overflow-x: hidden;
|
|
6917
|
-
padding: var(--aparte-space-2) var(--aparte-
|
|
7403
|
+
padding: var(--aparte-space-2) var(--aparte-sidebar-inset);
|
|
6918
7404
|
scrollbar-width: thin;
|
|
6919
7405
|
scrollbar-color: var(--aparte-scrollbar-thumb) var(--aparte-scrollbar-track);
|
|
6920
7406
|
}
|
|
@@ -6923,7 +7409,7 @@ aparte-sidebar,
|
|
|
6923
7409
|
display: flex;
|
|
6924
7410
|
align-items: center;
|
|
6925
7411
|
gap: var(--aparte-space-3);
|
|
6926
|
-
padding: var(--aparte-space-3) var(--aparte-
|
|
7412
|
+
padding: var(--aparte-space-3) var(--aparte-sidebar-inset);
|
|
6927
7413
|
border-block-start: var(--aparte-border-width) solid var(--aparte-border);
|
|
6928
7414
|
font-size: var(--aparte-font-size-sm);
|
|
6929
7415
|
}
|
|
@@ -6968,7 +7454,7 @@ aparte-sidebar,
|
|
|
6968
7454
|
* under 48rem, where the sidebar is a drawer; above it the column is simply there.
|
|
6969
7455
|
*
|
|
6970
7456
|
* <header class="aparte-app-header">
|
|
6971
|
-
* <button class="aparte-btn aparte-btn--icon aparte-app-header__toggle" type="button" aria-label="Toggle the sidebar" data-aparte-sidebar-toggle
|
|
7457
|
+
* <button class="aparte-btn aparte-btn--icon aparte-app-header__toggle" type="button" aria-label="Toggle the sidebar" data-aparte-sidebar-toggle><aparte-icon name="menu"></aparte-icon></button>
|
|
6972
7458
|
* <span class="aparte-app-header__title">Deploy checklist</span>
|
|
6973
7459
|
* <div class="aparte-app-header__actions">
|
|
6974
7460
|
* <span class="aparte-tag">gpt-4.1</span>
|
|
@@ -7029,15 +7515,38 @@ aparte-sidebar,
|
|
|
7029
7515
|
* `height: 100dvh` for a page — and everything inside follows. Under 48rem the sidebar
|
|
7030
7516
|
* leaves the grid to become a drawer, and the header's toggle appears.
|
|
7031
7517
|
*
|
|
7032
|
-
* <div class="aparte-app-shell" style="height:
|
|
7518
|
+
* <div class="aparte-app-shell" style="height: 24rem">
|
|
7033
7519
|
* <aside class="aparte-sidebar">
|
|
7034
|
-
* <div class="aparte-sidebar__header"
|
|
7035
|
-
*
|
|
7520
|
+
* <div class="aparte-sidebar__header">
|
|
7521
|
+
* <span class="aparte-sidebar__brand">aparté</span>
|
|
7522
|
+
* <button class="aparte-btn aparte-btn--icon aparte-btn--sm" type="button" aria-label="New chat"><aparte-icon name="edit"></aparte-icon></button>
|
|
7523
|
+
* </div>
|
|
7524
|
+
* <div class="aparte-sidebar__search aparte-field-group">
|
|
7525
|
+
* <input class="aparte-field aparte-field--sm" type="search" placeholder="Search">
|
|
7526
|
+
* </div>
|
|
7527
|
+
* <div class="aparte-sidebar__body">
|
|
7528
|
+
* <div class="aparte-conv-group" role="group" aria-label="Today">
|
|
7529
|
+
* <div class="aparte-menu__label aparte-conv-group__label" aria-hidden="true">Today</div>
|
|
7530
|
+
* <div class="aparte-menu__item aparte-conv-item aparte-conv-item--active"><button class="aparte-conv-item__select" type="button"><span class="aparte-conv-item__title">Deploy checklist</span></button></div>
|
|
7531
|
+
* <div class="aparte-menu__item aparte-conv-item"><button class="aparte-conv-item__select" type="button"><span class="aparte-conv-item__title">Rename the staging bucket</span></button></div>
|
|
7532
|
+
* </div>
|
|
7533
|
+
* <div class="aparte-conv-group" role="group" aria-label="Yesterday">
|
|
7534
|
+
* <div class="aparte-menu__label aparte-conv-group__label" aria-hidden="true">Yesterday</div>
|
|
7535
|
+
* <div class="aparte-menu__item aparte-conv-item"><button class="aparte-conv-item__select" type="button"><span class="aparte-conv-item__title">Why is total a float?</span></button></div>
|
|
7536
|
+
* </div>
|
|
7537
|
+
* </div>
|
|
7538
|
+
* <div class="aparte-sidebar__footer"><span class="aparte-avatar aparte-avatar--sm">P</span> Paul</div>
|
|
7036
7539
|
* </aside>
|
|
7037
|
-
* <header class="aparte-app-header"
|
|
7540
|
+
* <header class="aparte-app-header">
|
|
7541
|
+
* <span class="aparte-app-header__title">Deploy checklist</span>
|
|
7542
|
+
* <div class="aparte-app-header__actions"><span class="aparte-tag">gpt-4.1</span></div>
|
|
7543
|
+
* </header>
|
|
7038
7544
|
* <main class="aparte-app-shell__main">
|
|
7039
7545
|
* <aparte-chat>
|
|
7040
|
-
* <aparte-chat-viewport
|
|
7546
|
+
* <aparte-chat-viewport>
|
|
7547
|
+
* <aparte-chat-bubble message-id="u1" data-role="user" content="Walk me through the deploy."></aparte-chat-bubble>
|
|
7548
|
+
* <aparte-chat-bubble message-id="a1" data-role="assistant" name="Assistant" content="Build, run the gate, tag, then push the tag — the workflow publishes from it."></aparte-chat-bubble>
|
|
7549
|
+
* </aparte-chat-viewport>
|
|
7041
7550
|
* <aparte-composer>
|
|
7042
7551
|
* <div class="aparte-composer-shell">
|
|
7043
7552
|
* <div class="aparte-composer-row">
|
|
@@ -7094,6 +7603,28 @@ aparte-sidebar,
|
|
|
7094
7603
|
min-height: 0;
|
|
7095
7604
|
height: auto;
|
|
7096
7605
|
}
|
|
7606
|
+
|
|
7607
|
+
/* A phone folds the shell to one column. The sidebar ELEMENT already leaves the grid
|
|
7608
|
+
under 48rem — it becomes a fixed drawer behind the header's toggle — but the grid
|
|
7609
|
+
kept its first column for it, and a class-only sidebar had no story at all: at 375px
|
|
7610
|
+
the shell gave 259 of its 303px to the sidebar and left the chat a 43px band with a
|
|
7611
|
+
send button cut in half. Same query as the element's own, so the recipe teaches the
|
|
7612
|
+
screen the element renders. */
|
|
7613
|
+
@media (max-width: 48rem) {
|
|
7614
|
+
.aparte-app-shell {
|
|
7615
|
+
grid-template-columns: minmax(0, 1fr);
|
|
7616
|
+
}
|
|
7617
|
+
.aparte-app-shell > .aparte-app-header,
|
|
7618
|
+
.aparte-app-shell__main {
|
|
7619
|
+
grid-column: 1;
|
|
7620
|
+
}
|
|
7621
|
+
/* A sidebar that is not (yet) a drawer — the class recipe alone, or the element
|
|
7622
|
+
before it upgrades — folds out of the flow rather than eating the column. */
|
|
7623
|
+
.aparte-app-shell > .aparte-sidebar:not([data-drawer]),
|
|
7624
|
+
.aparte-app-shell > aparte-sidebar:not([data-drawer]) {
|
|
7625
|
+
display: none;
|
|
7626
|
+
}
|
|
7627
|
+
}
|
|
7097
7628
|
/*
|
|
7098
7629
|
* aparté — the seam: two panes side by side, and the grid that lets one grow while
|
|
7099
7630
|
* the other gives way. The fourth piece of the shell, after the sidebar, the header
|
|
@@ -7185,35 +7716,47 @@ aparte-split > [data-aparte-chat],
|
|
|
7185
7716
|
box-sizing: border-box;
|
|
7186
7717
|
border: 0;
|
|
7187
7718
|
padding: 0;
|
|
7188
|
-
|
|
7719
|
+
/* A transparent TRACK: the seam is the 1px line painted by `::before`, centred in it,
|
|
7720
|
+
and the grip is a short bar drawn on the track under the pointer. The track used
|
|
7721
|
+
to be the seam — 4px of paint, four times the kit's rule, with nothing to hold. */
|
|
7722
|
+
background: transparent;
|
|
7723
|
+
background-repeat: no-repeat;
|
|
7724
|
+
background-position: center;
|
|
7189
7725
|
cursor: col-resize;
|
|
7190
7726
|
touch-action: none;
|
|
7727
|
+
}
|
|
7728
|
+
|
|
7729
|
+
.aparte-split__handle::before {
|
|
7730
|
+
content: '';
|
|
7731
|
+
position: absolute;
|
|
7732
|
+
inset-block: 0;
|
|
7733
|
+
inset-inline-start: calc(50% - var(--aparte-split-seam-width) / 2);
|
|
7734
|
+
inline-size: var(--aparte-split-seam-width);
|
|
7735
|
+
background: var(--aparte-border);
|
|
7191
7736
|
transition: background-color var(--aparte-duration-fast) var(--aparte-ease);
|
|
7192
7737
|
}
|
|
7193
7738
|
|
|
7194
|
-
.aparte-split__handle:hover,
|
|
7195
|
-
.aparte-split__handle[data-dragging] {
|
|
7739
|
+
.aparte-split__handle:hover::before,
|
|
7740
|
+
.aparte-split__handle[data-dragging]::before {
|
|
7196
7741
|
background: var(--aparte-primary);
|
|
7197
7742
|
}
|
|
7198
7743
|
|
|
7199
|
-
/*
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
keyboard user has no other way to find.
|
|
7744
|
+
/* The grip: the convention of every editor's split, so the seam reads as something to
|
|
7745
|
+
take hold of. Drawn as a background so it needs no third element. */
|
|
7746
|
+
.aparte-split__handle:hover,
|
|
7747
|
+
.aparte-split__handle[data-dragging] {
|
|
7748
|
+
background-image: linear-gradient(var(--aparte-primary), var(--aparte-primary));
|
|
7749
|
+
background-size: calc(var(--aparte-split-seam-width) * 3) var(--aparte-space-8);
|
|
7750
|
+
}
|
|
7207
7751
|
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
|
|
7212
|
-
|
|
7752
|
+
/* The one ring. The seam's whole keyboard story is arrowing it, and its only focus
|
|
7753
|
+
indicator used to be a soft wash at 1.39:1 against the page — not weak, absent, on a
|
|
7754
|
+
4px band with `border: 0` that a keyboard user has no other way to find. The outline
|
|
7755
|
+
carries the contrast (3.54:1 light, 7.36:1 dark, measured), and it is the same outline
|
|
7756
|
+
every other control draws. */
|
|
7213
7757
|
.aparte-split__handle:focus-visible {
|
|
7214
7758
|
outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);
|
|
7215
|
-
outline-offset:
|
|
7216
|
-
box-shadow: var(--aparte-focus-ring);
|
|
7759
|
+
outline-offset: var(--aparte-focus-outline-offset);
|
|
7217
7760
|
}
|
|
7218
7761
|
|
|
7219
7762
|
/* The hit slop: a 4px seam is a 4px target, and a pointer misses it. The pseudo-element
|
|
@@ -7302,6 +7845,14 @@ aparte-split[orientation='vertical'][primary='end']:not([data-stacked]),
|
|
|
7302
7845
|
aparte-split[orientation='vertical'] > .aparte-split__handle,
|
|
7303
7846
|
.aparte-split--vertical > .aparte-split__handle {
|
|
7304
7847
|
cursor: row-resize;
|
|
7848
|
+
background-size: var(--aparte-space-8) calc(var(--aparte-split-seam-width) * 3);
|
|
7849
|
+
}
|
|
7850
|
+
aparte-split[orientation='vertical'] > .aparte-split__handle::before,
|
|
7851
|
+
.aparte-split--vertical > .aparte-split__handle::before {
|
|
7852
|
+
inset-block: calc(50% - var(--aparte-split-seam-width) / 2) auto;
|
|
7853
|
+
inset-inline: 0;
|
|
7854
|
+
inline-size: auto;
|
|
7855
|
+
block-size: var(--aparte-split-seam-width);
|
|
7305
7856
|
}
|
|
7306
7857
|
|
|
7307
7858
|
aparte-split[orientation='vertical'] > .aparte-split__handle::after,
|
|
@@ -7559,6 +8110,13 @@ aparte-split[disabled] > .aparte-split__handle:hover {
|
|
|
7559
8110
|
.aparte-code-content-wrapper pre {
|
|
7560
8111
|
margin: 0;
|
|
7561
8112
|
padding: var(--aparte-space-6);
|
|
8113
|
+
/* The code typeface, at a code size. This rule declared neither and the block fell
|
|
8114
|
+
into the UA's generic `monospace` (Courier New on Windows) at the PROSE's size and
|
|
8115
|
+
leading; only inline code read the family. On the wrapper's `pre`, whatever class
|
|
8116
|
+
the highlighter puts on its replacement. */
|
|
8117
|
+
font-family: var(--aparte-code-font-family);
|
|
8118
|
+
font-size: var(--aparte-code-block-font-size);
|
|
8119
|
+
line-height: var(--aparte-code-block-line-height);
|
|
7562
8120
|
/* Wrap long lines instead of a horizontal scrollbar. `pre-wrap` honours
|
|
7563
8121
|
newlines + indentation but soft-wraps overflowing lines; `overflow-wrap:
|
|
7564
8122
|
anywhere` breaks unbreakable tokens (long URLs, hashes). Applies to both
|
|
@@ -7594,23 +8152,11 @@ aparte-split[disabled] > .aparte-split__handle:hover {
|
|
|
7594
8152
|
}
|
|
7595
8153
|
}
|
|
7596
8154
|
|
|
7597
|
-
/* Windows high contrast. In forced-colors mode the UA repaints colours and
|
|
7598
|
-
|
|
7599
|
-
|
|
7600
|
-
|
|
7601
|
-
own focus colour, so the indicator survives without fighting the user's
|
|
7602
|
-
palette. Only the indicators that disappear are restated here: everything
|
|
7603
|
-
whose affordance is text or a border already survives on its own — which the
|
|
7604
|
-
split's seam does not, being a 4px band with `border: 0` whose entire keyboard
|
|
7605
|
-
story is arrowing it. */
|
|
8155
|
+
/* Windows high contrast. In forced-colors mode the UA repaints colours and drops
|
|
8156
|
+
box-shadow entirely. Every focus indicator is an outline now, which forced colours
|
|
8157
|
+
paint on their own — the one indicator drawn with a box-shadow is the error ring on
|
|
8158
|
+
an avatar, restated here as an outline so it does not vanish. */
|
|
7606
8159
|
@media (forced-colors: active) {
|
|
7607
|
-
.aparte-thumb--image[role='button']:focus-visible,
|
|
7608
|
-
.aparte-split__handle:focus-visible,
|
|
7609
|
-
.aparte-provider-select:focus,
|
|
7610
|
-
.aparte-model-select:focus {
|
|
7611
|
-
outline: var(--aparte-focus-outline-width) solid Highlight;
|
|
7612
|
-
}
|
|
7613
|
-
|
|
7614
8160
|
.aparte-message[data-error] .aparte-avatar {
|
|
7615
8161
|
outline: var(--aparte-focus-outline-width) solid CanvasText;
|
|
7616
8162
|
}
|
|
@@ -7702,7 +8248,13 @@ aparte-split[disabled] > .aparte-split__handle:hover {
|
|
|
7702
8248
|
/* Hover cannot reveal the row's ⋯ on touch — keep it visible and tappable. */
|
|
7703
8249
|
.aparte-conv-item__more {
|
|
7704
8250
|
opacity: 1;
|
|
7705
|
-
--aparte-conv-action-btn-size:
|
|
8251
|
+
--aparte-conv-action-btn-size: var(--aparte-touch-target-size);
|
|
8252
|
+
}
|
|
8253
|
+
|
|
8254
|
+
/* The composer's own controls: 36 at rest, the touch target here — the send button, the
|
|
8255
|
+
input's action button and the editor's single-line height move together. */
|
|
8256
|
+
.aparte-composer-row {
|
|
8257
|
+
--aparte-composer-control-size: var(--aparte-touch-target-size);
|
|
7706
8258
|
}
|
|
7707
8259
|
|
|
7708
8260
|
/* A 4px seam is no target for a finger — the same modality rule the buttons above
|