claude-artifact-framework 0.8.1 → 0.9.1
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 +24 -3
- package/dist/index.esm.js +76 -31
- package/dist/index.esm.js.map +2 -2
- package/dist/index.global.js +68 -39
- package/dist/index.global.js.map +3 -3
- package/package.json +1 -1
- package/src/app.js +76 -29
- package/src/blocks.js +1 -1
- package/src/chat.js +4 -1
package/README.md
CHANGED
|
@@ -323,7 +323,9 @@ blocks: [
|
|
|
323
323
|
|
|
324
324
|
Action keys: `label`, `run`, optional `when` (hides the button when false)
|
|
325
325
|
and `kind` (`"primary"` / `"danger"`). Row actions receive the record;
|
|
326
|
-
block actions receive data. Changes persist like any other update.
|
|
326
|
+
block actions receive data. Changes persist like any other update. Give the
|
|
327
|
+
constructive action `kind: "primary"` — a "Vote"/"Add"/"Done" left as the
|
|
328
|
+
neutral default reads weaker than a `"danger"` next to it.
|
|
327
329
|
|
|
328
330
|
Chat bubbles render the assistant's **markdown** (bold, code, lists,
|
|
329
331
|
headings) automatically — HTML is escaped first, so model output can't
|
|
@@ -345,7 +347,8 @@ ArtifactKit.createApp({
|
|
|
345
347
|
{ output: (d) => [{ label: "Palabras", value: countWords(d.nota), big: true }] },
|
|
346
348
|
],
|
|
347
349
|
sidebar: [
|
|
348
|
-
{
|
|
350
|
+
{ title: "Asistente",
|
|
351
|
+
chat: {
|
|
349
352
|
system: (d) => "Sos un copiloto de escritura. Nota actual: " + (d.nota || ""),
|
|
350
353
|
tools: {
|
|
351
354
|
reescribir_nota: {
|
|
@@ -359,6 +362,9 @@ ArtifactKit.createApp({
|
|
|
359
362
|
})
|
|
360
363
|
```
|
|
361
364
|
|
|
365
|
+
Give the chat block a `title` (like every other block) — a chat card that
|
|
366
|
+
opens straight into a bubble is the one block on screen without a heading.
|
|
367
|
+
|
|
362
368
|
`main` is required; `sidebar` is optional. Both take the same blocks as
|
|
363
369
|
`panel`. The panes sit side by side and stack on narrow screens. One chat
|
|
364
370
|
block per app — the conversation persists under `data.chat` and there is
|
|
@@ -409,6 +415,19 @@ ArtifactKit.createApp({
|
|
|
409
415
|
- **`libs: ["https://..."]`** loads external scripts before the app mounts,
|
|
410
416
|
deduped by URL, with a "Loading libraries…" state and a named error block
|
|
411
417
|
if one fails. No hand-written `loadScript` in custom code.
|
|
418
|
+
- **Call the library's method, not its global.** Modern CDN builds expose a
|
|
419
|
+
*namespace object*, and APIs you remember may have moved: `marked` v5+ is
|
|
420
|
+
an object (`marked.parse(text)` works, `marked(text)` throws "marked is
|
|
421
|
+
not a function"). When unsure, prefer the method form — `marked.parse(md)`,
|
|
422
|
+
`Papa.parse(csv)`, `QRCode.toCanvas(...)` or `new QRCode(el, ...)`
|
|
423
|
+
depending on the lib — and pin the version in the URL so the API can't
|
|
424
|
+
drift under you.
|
|
425
|
+
- **Unsure of the file path? Use the bare package URL.** jsDelivr serves the
|
|
426
|
+
package's default build at `https://cdn.jsdelivr.net/npm/<pkg>@<version>`
|
|
427
|
+
with no file path — guessed inner paths (`/marked.umd.js`,
|
|
428
|
+
`/dist/foo.min.js`) 404 more often than versions do. If a full path you
|
|
429
|
+
wrote does 404, the framework retries the bare package URL once before
|
|
430
|
+
showing the error.
|
|
412
431
|
- **`type: "file"`** fields hand the live `File` to your code at
|
|
413
432
|
`ctx.files[key]` (in memory only — the 5MB/key storage limit makes real
|
|
414
433
|
files unpersistable); `data[key]` gets serializable metadata
|
|
@@ -533,7 +552,9 @@ keys must be unique across steps.
|
|
|
533
552
|
|
|
534
553
|
**Formats** (`format:` on output/chart/compute items): `money`, `percent`,
|
|
535
554
|
`number`, `date`. `percent` expects 0–100 (pass `ratio * 100`, not the raw
|
|
536
|
-
ratio).
|
|
555
|
+
ratio). `big: true` renders an item at hero size — reserve it for the ONE
|
|
556
|
+
number that is the screen's main result (a total, a final score), not for
|
|
557
|
+
every stat; more than one hero per screen means no hero at all.
|
|
537
558
|
|
|
538
559
|
**Quick-add pattern** — a lightweight alternative to `records` when you just
|
|
539
560
|
need "type something, press a button, it lands in a list":
|
package/dist/index.esm.js
CHANGED
|
@@ -507,7 +507,7 @@ function Bubble({ m }) {
|
|
|
507
507
|
dangerouslySetInnerHTML: { __html: renderMarkdown(m.content) }
|
|
508
508
|
});
|
|
509
509
|
}
|
|
510
|
-
function ChatPanel({ cfg, ctx }) {
|
|
510
|
+
function ChatPanel({ cfg, ctx, title }) {
|
|
511
511
|
const c = cfg;
|
|
512
512
|
const [draft, setDraft] = useState("");
|
|
513
513
|
const [busy, setBusy] = useState(false);
|
|
@@ -578,6 +578,9 @@ Continue with this information. If you have everything you need, answer in natur
|
|
|
578
578
|
createElement(
|
|
579
579
|
"div",
|
|
580
580
|
{ className: "caf-block caf-chat-log" },
|
|
581
|
+
// Every other block opens with its title label — a chat card that
|
|
582
|
+
// starts straight at a bubble was the one heading-less block on screen.
|
|
583
|
+
title ? createElement("h2", { className: "caf-block-title" }, title) : null,
|
|
581
584
|
c.greeting ? createElement("div", { className: "caf-msg caf-msg-assistant", dangerouslySetInnerHTML: { __html: renderMarkdown(c.greeting) } }) : null,
|
|
582
585
|
log.map((m, i) => createElement(Bubble, { key: i, m })),
|
|
583
586
|
live !== null ? createElement("div", { className: "caf-msg caf-msg-assistant caf-msg-live", dangerouslySetInnerHTML: { __html: renderMarkdown(live || "\u2026") } }) : null,
|
|
@@ -1008,7 +1011,7 @@ var BLOCKS = {
|
|
|
1008
1011
|
chart: ChartBlock,
|
|
1009
1012
|
timer: TimerBlock,
|
|
1010
1013
|
actions: ActionsBlock,
|
|
1011
|
-
chat: ({ spec, ctx }) => createElement(ChatPanel, { cfg: spec.chat, ctx }),
|
|
1014
|
+
chat: ({ spec, ctx }) => createElement(ChatPanel, { cfg: spec.chat, ctx, title: spec.title }),
|
|
1012
1015
|
custom: CustomBlock
|
|
1013
1016
|
};
|
|
1014
1017
|
var BLOCK_NAMES = Object.keys(BLOCKS);
|
|
@@ -1717,7 +1720,9 @@ function Panel({ spec, ctx }) {
|
|
|
1717
1720
|
(spec.blocks || []).map(
|
|
1718
1721
|
(block, i) => createElement(
|
|
1719
1722
|
"section",
|
|
1720
|
-
|
|
1723
|
+
// A collapsible is always full-width: half-width accordions leave an
|
|
1724
|
+
// orphaned right edge on desktop and change width across breakpoints.
|
|
1725
|
+
{ key: i, className: block.wide || block.collapsible ? "caf-slot caf-slot-wide" : "caf-slot" },
|
|
1721
1726
|
createElement(Block, { block, ctx })
|
|
1722
1727
|
)
|
|
1723
1728
|
)
|
|
@@ -1735,7 +1740,9 @@ function RecordsWithBlocks({ spec, ctx }) {
|
|
|
1735
1740
|
spec.blocks.map(
|
|
1736
1741
|
(block, i) => createElement(
|
|
1737
1742
|
"section",
|
|
1738
|
-
|
|
1743
|
+
// A collapsible is always full-width: half-width accordions leave an
|
|
1744
|
+
// orphaned right edge on desktop and change width across breakpoints.
|
|
1745
|
+
{ key: i, className: block.wide || block.collapsible ? "caf-slot caf-slot-wide" : "caf-slot" },
|
|
1739
1746
|
createElement(Block, { block, ctx })
|
|
1740
1747
|
)
|
|
1741
1748
|
)
|
|
@@ -1767,7 +1774,14 @@ function loadScript(src) {
|
|
|
1767
1774
|
tag.dataset.cafReady = "1";
|
|
1768
1775
|
resolve();
|
|
1769
1776
|
};
|
|
1770
|
-
tag.onerror = () =>
|
|
1777
|
+
tag.onerror = () => {
|
|
1778
|
+
const m = src.match(/^(https:\/\/cdn\.jsdelivr\.net\/npm\/(?:@[^/]+\/)?[^/]+)\/.+/);
|
|
1779
|
+
if (m && m[1] !== src) {
|
|
1780
|
+
loadScript(m[1]).then(resolve, () => reject(new Error(`failed to load ${src} (also tried package default ${m[1]})`)));
|
|
1781
|
+
} else {
|
|
1782
|
+
reject(new Error(`failed to load ${src}`));
|
|
1783
|
+
}
|
|
1784
|
+
};
|
|
1771
1785
|
document.head.appendChild(tag);
|
|
1772
1786
|
});
|
|
1773
1787
|
}
|
|
@@ -1817,7 +1831,9 @@ function createApp(spec = {}) {
|
|
|
1817
1831
|
const Layout = LAYOUT_COMPONENTS[layout];
|
|
1818
1832
|
return createElement(
|
|
1819
1833
|
"div",
|
|
1820
|
-
|
|
1834
|
+
// The layout class lets shared CSS follow the active container width —
|
|
1835
|
+
// e.g. the header must share the workspace's wider axis, not panel's.
|
|
1836
|
+
{ className: `caf-root caf-layout-${layout}` },
|
|
1821
1837
|
createElement("style", { dangerouslySetInnerHTML: { __html: css } }),
|
|
1822
1838
|
createElement(
|
|
1823
1839
|
"header",
|
|
@@ -1837,6 +1853,10 @@ function createApp(spec = {}) {
|
|
|
1837
1853
|
var BASE_CSS = `
|
|
1838
1854
|
.caf-root {
|
|
1839
1855
|
--caf-radius: 10px;
|
|
1856
|
+
--caf-control-radius: 7px; /* one radius for every control: buttons, inputs, rows */
|
|
1857
|
+
--caf-btn-pad-y: 0.45rem;
|
|
1858
|
+
--caf-btn-pad-x: 0.8rem;
|
|
1859
|
+
--caf-num-primary: 2.1rem; /* the one size for a block's dominant number */
|
|
1840
1860
|
box-sizing: border-box;
|
|
1841
1861
|
min-height: 100vh;
|
|
1842
1862
|
padding: 2rem 1.25rem 4rem;
|
|
@@ -1847,9 +1867,12 @@ var BASE_CSS = `
|
|
|
1847
1867
|
}
|
|
1848
1868
|
.caf-root *, .caf-root *::before, .caf-root *::after { box-sizing: inherit; }
|
|
1849
1869
|
.caf-header { max-width: 760px; margin: 0 auto 1.5rem; }
|
|
1870
|
+
/* One left axis per screen: the header adopts the workspace's wider container. */
|
|
1871
|
+
.caf-layout-workspace .caf-header { max-width: 1200px; }
|
|
1850
1872
|
.caf-header h1 { margin: 0; font-size: 1.5rem; font-weight: 650; letter-spacing: -0.01em; text-wrap: balance; }
|
|
1851
1873
|
.caf-header p { margin: 0.4rem 0 0; color: var(--caf-muted); font-size: 0.95rem; line-height: 1.5; }
|
|
1852
|
-
|
|
1874
|
+
/* Between-card air must beat within-card padding (1.1rem), or groups dissolve. */
|
|
1875
|
+
.caf-panel { max-width: 760px; margin: 0 auto; display: grid; grid-template-columns: 1fr 1fr; gap: 1.75rem 1rem; }
|
|
1853
1876
|
.caf-slot { grid-column: span 2; min-width: 0; }
|
|
1854
1877
|
@media (min-width: 720px) { .caf-slot { grid-column: span 1; } .caf-slot-wide { grid-column: span 2; } }
|
|
1855
1878
|
.caf-block {
|
|
@@ -1862,12 +1885,14 @@ var BASE_CSS = `
|
|
|
1862
1885
|
gap: 0.8rem;
|
|
1863
1886
|
height: 100%;
|
|
1864
1887
|
}
|
|
1865
|
-
.caf-block-title { margin: 0; font-size: 0.
|
|
1888
|
+
.caf-block-title { margin: 0; font-size: 0.78rem; font-weight: 650; text-transform: uppercase; letter-spacing: 0.06em; color: var(--caf-muted); }
|
|
1866
1889
|
.caf-field { display: flex; flex-direction: column; gap: 0.3rem; }
|
|
1867
|
-
|
|
1890
|
+
/* A full size+weight step below .caf-block-title \u2014 the uppercase alone was
|
|
1891
|
+
not enough to tell "card title" from "field label" at a glance. */
|
|
1892
|
+
.caf-field label { font-size: 0.68rem; font-weight: 500; color: var(--caf-muted); }
|
|
1868
1893
|
.caf-field input, .caf-field select, .caf-field textarea {
|
|
1869
1894
|
font: inherit; font-size: 0.95rem; padding: 0.5rem 0.6rem;
|
|
1870
|
-
border-radius:
|
|
1895
|
+
border-radius: var(--caf-control-radius); border: 1px solid var(--caf-border);
|
|
1871
1896
|
background: var(--caf-sunken); color: var(--caf-text); width: 100%;
|
|
1872
1897
|
}
|
|
1873
1898
|
.caf-field input:focus-visible, .caf-field select:focus-visible, .caf-field textarea:focus-visible,
|
|
@@ -1876,17 +1901,21 @@ var BASE_CSS = `
|
|
|
1876
1901
|
.caf-check { display: flex; align-items: center; gap: 0.5rem; font-size: 0.9rem; }
|
|
1877
1902
|
.caf-hint { color: var(--caf-muted); font-size: 0.75rem; }
|
|
1878
1903
|
.caf-error { color: var(--caf-danger); font-size: 0.75rem; font-weight: 600; }
|
|
1879
|
-
.caf-output-grid { display:
|
|
1880
|
-
|
|
1881
|
-
.caf-stat
|
|
1882
|
-
.caf-stat-
|
|
1883
|
-
.caf-stat-
|
|
1884
|
-
.caf-stat-big
|
|
1904
|
+
.caf-output-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 0.9rem 1.1rem; }
|
|
1905
|
+
/* Every stat shares one axis: label above value, both flush left. */
|
|
1906
|
+
.caf-stat { display: flex; flex-direction: column; align-items: flex-start; gap: 0.1rem; }
|
|
1907
|
+
.caf-stat-label { color: var(--caf-muted); font-size: 0.78rem; font-weight: 600; letter-spacing: 0.02em; }
|
|
1908
|
+
.caf-stat-value { font-size: 1.05rem; font-weight: 650; font-variant-numeric: tabular-nums; }
|
|
1909
|
+
.caf-stat-big { grid-column: 1 / -1; }
|
|
1910
|
+
/* The divider exists to separate the hero from stats below it \u2014 alone in the
|
|
1911
|
+
grid it would be an orphaned rule, so it only renders when followed. */
|
|
1912
|
+
.caf-stat-big:not(:last-child) { padding-bottom: 0.5rem; border-bottom: 1px solid var(--caf-border); }
|
|
1913
|
+
.caf-stat-big .caf-stat-value { font-size: var(--caf-num-primary); font-weight: 700; letter-spacing: -0.02em; color: var(--caf-accent); line-height: 1.15; }
|
|
1885
1914
|
.caf-list { padding: 0.4rem; gap: 0; }
|
|
1886
1915
|
.caf-row {
|
|
1887
1916
|
font: inherit; text-align: left; cursor: pointer;
|
|
1888
1917
|
display: flex; flex-direction: column; gap: 0.15rem;
|
|
1889
|
-
padding: 0.65rem 0.7rem; border: none; border-radius:
|
|
1918
|
+
padding: 0.65rem 0.7rem; border: none; border-radius: var(--caf-control-radius);
|
|
1890
1919
|
background: transparent; color: inherit; width: 100%;
|
|
1891
1920
|
}
|
|
1892
1921
|
.caf-row:hover { background: var(--caf-sunken); }
|
|
@@ -1894,12 +1923,15 @@ var BASE_CSS = `
|
|
|
1894
1923
|
.caf-row-sub { font-size: 0.78rem; color: var(--caf-muted); }
|
|
1895
1924
|
.caf-panel-single { grid-template-columns: 1fr; }
|
|
1896
1925
|
.caf-panel-single .caf-slot, .caf-panel-single > * { grid-column: span 1; }
|
|
1897
|
-
|
|
1926
|
+
/* Bottom padding matches the inter-row rhythm so the toolbar doesn't sit
|
|
1927
|
+
closer to the first row than rows sit to each other. */
|
|
1928
|
+
.caf-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 0.6rem; padding: 0.3rem 0.3rem 0.75rem; }
|
|
1898
1929
|
.caf-count { font-size: 0.8rem; color: var(--caf-muted); font-variant-numeric: tabular-nums; }
|
|
1899
1930
|
.caf-btn {
|
|
1900
1931
|
font: inherit; font-size: 0.85rem; font-weight: 600; cursor: pointer;
|
|
1901
|
-
padding:
|
|
1902
|
-
border: 1px solid var(--caf-border); background: var(--caf-
|
|
1932
|
+
padding: var(--caf-btn-pad-y) var(--caf-btn-pad-x); border-radius: var(--caf-control-radius);
|
|
1933
|
+
border: 1px solid var(--caf-border); background: var(--caf-surface); color: var(--caf-text);
|
|
1934
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
|
|
1903
1935
|
}
|
|
1904
1936
|
.caf-btn:hover { filter: brightness(0.97); }
|
|
1905
1937
|
.caf-btn:focus-visible { outline: 2px solid var(--caf-accent); outline-offset: 1px; }
|
|
@@ -1918,16 +1950,19 @@ var BASE_CSS = `
|
|
|
1918
1950
|
.caf-line-meta { display: flex; justify-content: space-between; align-items: baseline; gap: 0.6rem; }
|
|
1919
1951
|
.caf-line-last { font-weight: 650; font-variant-numeric: tabular-nums; color: var(--caf-accent); }
|
|
1920
1952
|
.caf-donut { display: flex; align-items: center; gap: 1.1rem; flex-wrap: wrap; }
|
|
1921
|
-
|
|
1953
|
+
/* -3px optical correction: a circle beside flush-left text reads indented. */
|
|
1954
|
+
.caf-donut-svg { width: 110px; height: 110px; flex: none; transform: rotate(0.001deg); margin-left: -3px; }
|
|
1922
1955
|
.caf-donut-seg { fill: none; stroke-width: 6; }
|
|
1923
|
-
|
|
1956
|
+
/* Capped width keeps each value NEXT TO its label in wide cards \u2014 the pair
|
|
1957
|
+
must read as a pair regardless of container width. */
|
|
1958
|
+
.caf-donut-legend { display: flex; flex-direction: column; gap: 0.4rem; min-width: 0; flex: 1; max-width: 260px; }
|
|
1924
1959
|
.caf-legend-row { display: flex; align-items: center; gap: 0.5rem; font-size: 0.84rem; }
|
|
1925
1960
|
.caf-legend-swatch { width: 10px; height: 10px; border-radius: 3px; flex: none; }
|
|
1926
1961
|
.caf-legend-label { color: var(--caf-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1927
1962
|
.caf-legend-value { margin-left: auto; font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
1928
1963
|
.caf-timer { align-items: center; text-align: center; }
|
|
1929
1964
|
.caf-timer .caf-bar-track { width: 100%; }
|
|
1930
|
-
.caf-timer-time { font-size:
|
|
1965
|
+
.caf-timer-time { font-size: var(--caf-num-primary); font-weight: 700; font-variant-numeric: tabular-nums; letter-spacing: -0.02em; color: var(--caf-accent); }
|
|
1931
1966
|
.caf-timer-done { color: var(--caf-accent); }
|
|
1932
1967
|
.caf-timer-fill { transition: width 0.9s linear; }
|
|
1933
1968
|
.caf-timer-controls { display: flex; gap: 0.6rem; }
|
|
@@ -1944,8 +1979,11 @@ var BASE_CSS = `
|
|
|
1944
1979
|
.caf-msg-tools { align-self: flex-start; font-size: 0.74rem; color: var(--caf-muted); padding: 0.15rem 0.55rem; border: 1px solid var(--caf-border); border-radius: 999px; }
|
|
1945
1980
|
.caf-msg-error { align-self: stretch; max-width: none; display: flex; flex-direction: column; gap: 0.2rem; background: transparent; border: 1px solid var(--caf-danger); }
|
|
1946
1981
|
.caf-msg-error strong { color: var(--caf-danger); font-size: 0.85rem; }
|
|
1947
|
-
.caf-chat-input {
|
|
1948
|
-
|
|
1982
|
+
.caf-chat-input {
|
|
1983
|
+
display: flex; gap: 0.6rem; margin-top: 1rem; padding: 0.6rem;
|
|
1984
|
+
background: var(--caf-surface); border: 1px solid var(--caf-border); border-radius: var(--caf-radius);
|
|
1985
|
+
}
|
|
1986
|
+
.caf-chat-input input { flex: 1; font: inherit; font-size: 0.95rem; padding: 0.55rem 0.7rem; border-radius: var(--caf-control-radius); border: 1px solid var(--caf-border); background: var(--caf-sunken); color: var(--caf-text); }
|
|
1949
1987
|
.caf-chat-input input:focus-visible { outline: 2px solid var(--caf-accent); outline-offset: 1px; }
|
|
1950
1988
|
.caf-search {
|
|
1951
1989
|
font: inherit; font-size: 0.9rem; width: 100%;
|
|
@@ -1959,7 +1997,7 @@ var BASE_CSS = `
|
|
|
1959
1997
|
.caf-row-wrap { display: flex; align-items: center; gap: 0.4rem; }
|
|
1960
1998
|
.caf-row-wrap .caf-row { flex: 1; min-width: 0; }
|
|
1961
1999
|
.caf-row-actions { display: flex; gap: 0.35rem; flex: none; padding-right: 0.3rem; }
|
|
1962
|
-
.caf-btn-sm { font-size: 0.78rem; padding:
|
|
2000
|
+
.caf-btn-sm { font-size: 0.78rem; padding: calc(var(--caf-btn-pad-y) * 0.67) calc(var(--caf-btn-pad-x) * 0.75); }
|
|
1963
2001
|
.caf-items { display: flex; flex-direction: column; gap: 0.5rem; border-top: 1px solid var(--caf-border); padding-top: 0.7rem; }
|
|
1964
2002
|
.caf-item-row { display: flex; align-items: flex-end; gap: 0.5rem; }
|
|
1965
2003
|
.caf-item-row .caf-field { flex: 1; min-width: 0; }
|
|
@@ -1991,17 +2029,24 @@ var BASE_CSS = `
|
|
|
1991
2029
|
}
|
|
1992
2030
|
.caf-tab:hover { color: var(--caf-text); }
|
|
1993
2031
|
.caf-tab:focus-visible { outline: 2px solid var(--caf-accent); outline-offset: -2px; }
|
|
1994
|
-
|
|
2032
|
+
/* :hover on the active tab must not win the specificity race and mute it. */
|
|
2033
|
+
.caf-tab-active, .caf-tab-active:hover { color: var(--caf-accent); border-bottom-color: var(--caf-accent); }
|
|
1995
2034
|
.caf-tabs-body { display: flex; flex-direction: column; gap: 0.8rem; }
|
|
1996
2035
|
.caf-collapse { display: flex; flex-direction: column; gap: 0.5rem; min-width: 0; }
|
|
1997
2036
|
.caf-collapse-head {
|
|
1998
|
-
|
|
2037
|
+
/* Same treatment as .caf-block-title: a collapsible header IS a card title. */
|
|
2038
|
+
font: inherit; font-size: 0.78rem; font-weight: 650; text-transform: uppercase;
|
|
2039
|
+
letter-spacing: 0.06em; color: var(--caf-muted); cursor: pointer;
|
|
1999
2040
|
display: flex; align-items: center; justify-content: space-between; gap: 0.6rem;
|
|
2000
|
-
padding: 0.7rem 1rem; border: 1px solid var(--caf-border); border-radius: var(--caf-radius);
|
|
2001
|
-
background: var(--caf-surface);
|
|
2041
|
+
padding: 0.7rem 1.1rem; border: 1px solid var(--caf-border); border-radius: var(--caf-radius);
|
|
2042
|
+
background: var(--caf-surface); width: 100%; text-align: left;
|
|
2002
2043
|
}
|
|
2044
|
+
.caf-collapse-head { box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08); }
|
|
2045
|
+
.caf-collapse-head:hover { filter: brightness(0.97); }
|
|
2003
2046
|
.caf-collapse-head:focus-visible { outline: 2px solid var(--caf-accent); outline-offset: 1px; }
|
|
2004
|
-
|
|
2047
|
+
/* Accent chevron: the one interaction cue that distinguishes a collapsible
|
|
2048
|
+
header from a static card title without breaking their shared typography. */
|
|
2049
|
+
.caf-chevron { transition: transform 0.15s ease; color: var(--caf-accent); }
|
|
2005
2050
|
.caf-chevron-closed { transform: rotate(-90deg); }
|
|
2006
2051
|
.caf-block-fill { border: none; background: transparent; padding: 0; overflow: auto; min-height: 320px; }
|
|
2007
2052
|
.caf-empty, .caf-loading { color: var(--caf-muted); font-size: 0.9rem; text-align: center; padding: 1.6rem 1rem; }
|