inline-chat-kit 0.60.0 → 0.61.0

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/theming.md CHANGED
@@ -311,8 +311,137 @@ thumb. A product that wants the buttons themselves drawn at 44 says so:
311
311
  }
312
312
  ```
313
313
 
314
- The composer's own three controls are 28px in a row whose layout is animated,
315
- and are not sized from these yet.
314
+ The composer's own controls — the send / stop / save glyph, the "+", the
315
+ microphone — are `--ick-composer-control`, which is `--ick-control-s`: 28 on a
316
+ pointer, 32 on a touch screen. Its own token so the composer's row can move
317
+ without every other `s` button moving with it:
318
+
319
+ ```css
320
+ @media (pointer: coarse) {
321
+ :root { --ick-composer-control: var(--ick-control-l); }
322
+ }
323
+ ```
324
+
325
+ ## Bring your own button
326
+
327
+ Three levels, each one further than the last. Stop at the first that is
328
+ enough.
329
+
330
+ **1. Tokens.** The `--ick-btn-<variant>-*` tokens above, `--ick-btn-radius`,
331
+ `--ick-btn-weight` and the `--ick-control-*` heights. Every button the kit
332
+ draws reads them, the composer's glyph included. Most hosts need nothing else.
333
+
334
+ **2. Per role, in CSS.** The same tokens on a `[data-ick-role]`, or your own
335
+ rules on it — the role table above is what to select on. A role is what the
336
+ button is for, so it survives a translation; a class name is hashed and a
337
+ label is not yours to match on.
338
+
339
+ **3. Your component.** Give `ChatExperience` your button and it draws it in
340
+ place of its own, everywhere but one place:
341
+
342
+ ```tsx
343
+ <ChatExperience components={{ Button: MyButton }} onSend={send} />
344
+ ```
345
+
346
+ Pieces assembled by hand — `ChatTurnRow`, `ChatHeader`, `Approval`,
347
+ `ArtifactPane` — take it from `ComponentsProvider`, as they take words from
348
+ `LabelsProvider`:
349
+
350
+ ```tsx
351
+ <ComponentsProvider components={{ Button: MyButton }}>
352
+ <ChatHeader title="Chat" actions={actions} />
353
+ {turns.map((turn) => <ChatTurnRow key={turn.id} turn={turn} />)}
354
+ </ComponentsProvider>
355
+ ```
356
+
357
+ Declare `MyButton` outside a render. A component made inside one is a new
358
+ component every time, and every button in the chat remounts with it.
359
+
360
+ ### Who owns what
361
+
362
+ The kit owns **layout and behaviour**: the box each button sits in, its height
363
+ from `--ick-control-*` (and on an icon-only button its width), where it goes,
364
+ how it arrives and leaves, the 44px it answers to under a finger, focus moving
365
+ to it, and what pressing it does. Your button **paints**: colour, border,
366
+ radius, font, shadow, what hover and press look like, its icons, and the focus
367
+ ring.
368
+
369
+ So your button fills the box it is given — the kit sets its height over yours,
370
+ a `h-9` included, and its width when it is an icon on its own; its padding,
371
+ radius and everything it paints are left alone — and it never animates or
372
+ positions itself.
373
+
374
+ ### The contract
375
+
376
+ Your component is called with `KitButtonProps`:
377
+
378
+ | Prop | |
379
+ | --- | --- |
380
+ | `kitRole` | What it is for — a role from the table above. Switch on it to pick your own variant |
381
+ | `variant` | What the kit would have drawn: `primary`, `secondary`, `outline`, `ghost`, `glass`. A hint |
382
+ | `size` | `xs` … `xl`: the step of `--ick-control-*` the box already is |
383
+ | `shape` | `square` (an icon on its own), `pill` (a chip: an opener, "Read more", a highlight's action) or `default` (a text button among others). A hint |
384
+ | `icon`, `iconRight`, `children` | The content. An icon-only button is named by its `aria-label` |
385
+ | `disabled`, `loading`, `pressed` | `loading` also sets `disabled` and `aria-busy`; `pressed` also sets `aria-pressed` |
386
+ | `type` | Always `"button"` |
387
+ | `onClick` | And every other handler the kit needs |
388
+ | `aria-*`, `title`, `role`, `tabIndex` | A menu's items arrive with `role="menuitem"` and a roving `tabIndex` |
389
+ | `data-ick-role`, `data-ick-variant`, `data-ick-action` | So the CSS above still reaches your button |
390
+ | `ref` | The `<button>` itself. The kit focuses it and anchors the header's menu to it |
391
+
392
+ Three rules, and the words in the first column of the table are the kit's,
393
+ not attributes — take them out before spreading the rest:
394
+
395
+ 1. **Forward the ref** — `forwardRef`, or in React 19 pass `ref` through.
396
+ 2. **Render a `<button>`.** The keyboard behaviour is the button's.
397
+ 3. **Spread what you do not use** onto it: the `aria-*`, `data-*`, `role`,
398
+ `tabIndex`, `title` and handlers.
399
+
400
+ ```tsx
401
+ const MyButton = forwardRef<HTMLButtonElement, KitButtonProps>(function MyButton(
402
+ { kitRole, variant, size, shape, icon, iconRight, loading, pressed, children, ...rest },
403
+ ref
404
+ ) {
405
+ const look = kitRole === "approval-once" ? "default" : variant === "primary" ? "secondary" : "ghost";
406
+ return (
407
+ <Button ref={ref} variant={look} size={shape === "square" ? "icon" : "sm"} {...rest}>
408
+ {loading ? <Spinner /> : icon}
409
+ {children}
410
+ {iconRight}
411
+ </Button>
412
+ );
413
+ });
414
+ ```
415
+
416
+ In development the kit checks each role's button once and says so in the
417
+ console when it does not forward its ref, is not a `<button>`, drops the
418
+ `aria-*` or `data-ick-*` it was given, or looks no different with keyboard
419
+ focus. One warning per role; none in a production build.
420
+
421
+ ### The one exception: the send glyph
422
+
423
+ The composer's send / stop / save button is always the kit's. It is one button
424
+ whose glyph morphs between the three as the composer changes state, and that
425
+ animation lives inside the button — a host component would have to take it on
426
+ to stand in for it. It follows the tokens instead: `--ick-btn-radius`,
427
+ `--ick-btn-primary-*` (on `[data-ick-role="send"]`, `"stop"` or `"save"` to
428
+ tell them apart) and `--ick-composer-control`.
429
+
430
+ Two more stay the kit's for a different reason: the header's back control and
431
+ a header action given an `href` are links, not buttons, and draw as `<a>`.
432
+
433
+ ### One radius
434
+
435
+ Set `--ick-btn-radius` to your buttons' corner even when you bring your own
436
+ button. It is how the kit's own glyph matches yours, and how the surfaces
437
+ around buttons match them: the composer's corner is the button's plus the
438
+ composer's 8px padding — concentric — up to its own ceiling of
439
+ `--ick-radius-xl`. A 6px button sits in a 14px composer; the default pill sits
440
+ in the 24px composer it always has.
441
+
442
+ ```css
443
+ :root { --ick-btn-radius: 6px; }
444
+ ```
316
445
 
317
446
  ## Motion
318
447