claude-artifact-framework 0.18.0 → 0.19.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/README.md CHANGED
@@ -909,9 +909,10 @@ keys must be unique across steps.
909
909
 
910
910
  **Formats** (`format:` on output/chart/compute items): `money`, `percent`,
911
911
  `number`, `date`. `percent` expects 0–100 (pass `ratio * 100`, not the raw
912
- ratio). `big: true` renders an item at hero size reserve it for the ONE
913
- number that is the screen's main result (a total, a final score), not for
914
- every stat; more than one hero per screen means no hero at all.
912
+ ratio). `big: true` renders an item at hero size AND promotes its block to
913
+ the screen's one feature surface (accent-tinted card) reserve it for the
914
+ ONE number that is the screen's main result (a total, a final score), not
915
+ for every stat; more than one hero per screen means no hero at all.
915
916
 
916
917
  **Quick-add pattern** — a lightweight alternative to `records` when you just
917
918
  need "type something, press a button, it lands in a list":
package/dist/index.esm.js CHANGED
@@ -761,9 +761,10 @@ function OutputBlock({ spec, ctx }) {
761
761
  if (!items.length) {
762
762
  return createElement("div", { className: "caf-block caf-empty" }, spec.empty || "Nothing to show yet.");
763
763
  }
764
+ const hero = items.some((it) => it && it.big);
764
765
  return createElement(
765
766
  "div",
766
- { className: "caf-block caf-output" },
767
+ { className: hero ? "caf-block caf-output caf-block-feature" : "caf-block caf-output" },
767
768
  spec.title ? createElement("h2", { className: "caf-block-title" }, spec.title) : null,
768
769
  createElement(
769
770
  "div",
@@ -780,10 +781,9 @@ function OutputBlock({ spec, ctx }) {
780
781
  )
781
782
  );
782
783
  }
783
- function BannerBlock({ spec, ctx }) {
784
+ function BannerBlock({ spec }) {
784
785
  const b = typeof spec.banner === "string" ? { title: spec.banner } : spec.banner || {};
785
- const [c1, c2] = ctx.colors(2);
786
- const background = b.image ? `linear-gradient(rgba(0,0,0,0.35), rgba(0,0,0,0.35)), url(${JSON.stringify(b.image)}) center / cover` : `linear-gradient(135deg, ${c1}, ${c2})`;
786
+ const background = b.image ? `linear-gradient(rgba(0,0,0,0.35), rgba(0,0,0,0.35)), url(${JSON.stringify(b.image)}) center / cover` : `linear-gradient(135deg, color-mix(in srgb, var(--caf-accent) 82%, #000), var(--caf-accent) 55%, color-mix(in srgb, var(--caf-accent) 72%, #fff))`;
787
787
  return createElement(
788
788
  "div",
789
789
  { className: "caf-banner", style: { background } },
@@ -1100,9 +1100,10 @@ function Summary({ spec, records }) {
1100
1100
  if (!spec.summary) return null;
1101
1101
  const items = spec.summary(records) || [];
1102
1102
  if (!items.length) return null;
1103
+ const hero = items.some((it) => it && it.big);
1103
1104
  return createElement(
1104
1105
  "div",
1105
- { className: "caf-block caf-output" },
1106
+ { className: hero ? "caf-block caf-output caf-block-feature" : "caf-block caf-output" },
1106
1107
  createElement(
1107
1108
  "div",
1108
1109
  { className: "caf-output-grid" },
@@ -1373,7 +1374,8 @@ function ResultScreen({ step, ctx }) {
1373
1374
  { className: "caf-steps-result" },
1374
1375
  createElement(
1375
1376
  "div",
1376
- { className: "caf-block caf-output" },
1377
+ // Same rule as OutputBlock: the hero (big) result gets the feature card.
1378
+ { className: items.some((it) => it && it.big) ? "caf-block caf-output caf-block-feature" : "caf-block caf-output" },
1377
1379
  step.title ? createElement("h2", { className: "caf-block-title" }, step.title) : null,
1378
1380
  createElement(
1379
1381
  "div",
@@ -2446,7 +2448,9 @@ function createApp(spec = {}) {
2446
2448
  const logoEl = !spec.brand ? null : logo && logo.trim().startsWith("<svg") ? createElement("span", { className: "caf-logo caf-logo-svg", dangerouslySetInnerHTML: { __html: logo } }) : logo && /^https?:\/\//.test(logo) ? createElement("img", { className: "caf-logo", src: logo, alt: "" }) : logo ? createElement("span", { className: "caf-logo caf-logo-emoji", "aria-hidden": true }, logo) : createElement(
2447
2449
  "span",
2448
2450
  { className: "caf-logo caf-logo-mono", "aria-hidden": true },
2449
- (spec.title || "?").split(/\s+/).map((w) => w[0]).filter(Boolean).slice(0, 2).join("").toUpperCase()
2451
+ // First letter/digit of each word — punctuation "words" like the
2452
+ // "-" in "Caja - Camión" must not become half the monogram.
2453
+ (spec.title || "?").split(/\s+/).map((w) => (w.match(/[\p{L}\p{N}]/u) || [])[0]).filter(Boolean).slice(0, 2).join("").toUpperCase() || "?"
2450
2454
  );
2451
2455
  return createElement(
2452
2456
  "div",
@@ -2487,7 +2491,9 @@ var BASE_CSS = `
2487
2491
  --caf-control-radius: 7px; /* one radius for every control: buttons, inputs, rows */
2488
2492
  --caf-btn-pad-y: 0.45rem;
2489
2493
  --caf-btn-pad-x: 0.8rem;
2490
- --caf-num-primary: 2.1rem; /* the one size for a block's dominant number */
2494
+ --caf-num-primary: 2.6rem; /* the one size for a block's dominant number \u2014 the largest thing on screen, bigger than the H1 */
2495
+ --caf-accent-tint: color-mix(in srgb, var(--caf-accent) 7%, var(--caf-surface)); /* soft accent surface for the feature card */
2496
+ --caf-block-gap: 1.75rem; /* ONE value for air between blocks, every layout (audited: workspace used a smaller gap for the same visual role) */
2491
2497
  box-sizing: border-box;
2492
2498
  min-height: 100vh;
2493
2499
  padding: 2rem 1.25rem 4rem;
@@ -2503,7 +2509,9 @@ var BASE_CSS = `
2503
2509
  .caf-header h1 { margin: 0; font-size: 1.5rem; font-weight: 650; letter-spacing: -0.01em; text-wrap: balance; }
2504
2510
  .caf-header p { margin: 0.4rem 0 0; color: var(--caf-muted); font-size: 0.95rem; line-height: 1.5; }
2505
2511
  /* Between-card air must beat within-card padding (1.1rem), or groups dissolve. */
2506
- .caf-panel { max-width: 760px; margin: 0 auto; display: grid; grid-template-columns: 1fr 1fr; gap: 1.75rem 1rem; }
2512
+ /* align-items: start \u2014 a short card must not stretch to its tall neighbor's
2513
+ height; the stretched version reads as dead air (audited). */
2514
+ .caf-panel { max-width: 760px; margin: 0 auto; display: grid; grid-template-columns: 1fr 1fr; gap: var(--caf-block-gap) 1rem; align-items: start; }
2507
2515
  .caf-slot { grid-column: span 2; min-width: 0; }
2508
2516
  @media (min-width: 720px) { .caf-slot { grid-column: span 1; } .caf-slot-wide { grid-column: span 2; } }
2509
2517
  .caf-block {
@@ -2597,7 +2605,9 @@ var BASE_CSS = `
2597
2605
  .caf-timer-done { color: var(--caf-accent); }
2598
2606
  .caf-timer-fill { transition: width 0.9s linear; }
2599
2607
  .caf-timer-controls { display: flex; gap: 0.6rem; }
2600
- .caf-btn:disabled { opacity: 0.45; cursor: not-allowed; }
2608
+ /* Disabled goes NEUTRAL, not pale-accent: a washed-out primary reads as
2609
+ broken (audited on the chat Send with an empty input). */
2610
+ .caf-btn:disabled { cursor: not-allowed; background: var(--caf-sunken); color: var(--caf-muted); border-color: var(--caf-border); opacity: 1; }
2601
2611
  .caf-steps-progress { display: flex; flex-direction: column; gap: 0.35rem; margin-bottom: 0.2rem; }
2602
2612
  .caf-steps-result { display: flex; flex-direction: column; gap: 1rem; min-width: 0; }
2603
2613
  .caf-steps-fill { transition: width 0.25s ease; }
@@ -2629,7 +2639,9 @@ var BASE_CSS = `
2629
2639
  .caf-row-wrap { display: flex; align-items: center; gap: 0.4rem; }
2630
2640
  .caf-row-wrap .caf-row { flex: 1; min-width: 0; }
2631
2641
  .caf-row-actions { display: flex; gap: 0.35rem; flex: none; padding-right: 0.3rem; }
2632
- .caf-btn-sm { font-size: 0.78rem; padding: calc(var(--caf-btn-pad-y) * 0.67) calc(var(--caf-btn-pad-x) * 0.75); }
2642
+ /* min-width keeps row actions' left edges aligned across rows even when
2643
+ labels differ ("Hecho" vs "Desmarcar" \u2014 audited misalignment). */
2644
+ .caf-btn-sm { font-size: 0.78rem; padding: calc(var(--caf-btn-pad-y) * 0.67) calc(var(--caf-btn-pad-x) * 0.75); min-width: 76px; text-align: center; }
2633
2645
  .caf-items { display: flex; flex-direction: column; gap: 0.5rem; border-top: 1px solid var(--caf-border); padding-top: 0.7rem; }
2634
2646
  .caf-item-row { display: flex; align-items: flex-end; gap: 0.5rem; }
2635
2647
  .caf-item-row .caf-field { flex: 1; min-width: 0; }
@@ -2643,8 +2655,8 @@ var BASE_CSS = `
2643
2655
  .caf-md-li { display: block; padding-left: 1rem; position: relative; }
2644
2656
  .caf-md-li::before { content: "\u2022"; position: absolute; left: 0.2rem; opacity: 0.6; }
2645
2657
  .caf-workspace { max-width: 1200px; margin: 0 auto; display: flex; gap: 1rem; align-items: flex-start; }
2646
- .caf-ws-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1rem; }
2647
- .caf-ws-side { width: 340px; flex: none; display: flex; flex-direction: column; gap: 1rem; position: sticky; top: 1rem; }
2658
+ .caf-ws-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--caf-block-gap); }
2659
+ .caf-ws-side { width: 340px; flex: none; display: flex; flex-direction: column; gap: var(--caf-block-gap); position: sticky; top: 1rem; }
2648
2660
  .caf-ws-slot { min-width: 0; }
2649
2661
  .caf-chat-panel { display: flex; flex-direction: column; min-height: 0; }
2650
2662
  .caf-ws-side .caf-chat-log { max-height: calc(100vh - 220px); }
@@ -2729,6 +2741,11 @@ var BASE_CSS = `
2729
2741
  /* Never-clip guarantee: content inside a custom block that is wider than the
2730
2742
  phone (column grids, week views) must scroll, not disappear off-screen. */
2731
2743
  .caf-custom { overflow-x: auto; }
2744
+ /* The one differentiated surface per screen: accent top rule + tinted bg on
2745
+ the block holding the hero number. Everything-is-the-same-card was the
2746
+ audited root cause of the "generic template" look. (Late in the sheet on
2747
+ purpose \u2014 it must win over .caf-block's border/background.) */
2748
+ .caf-block-feature { border-top: 3px solid var(--caf-accent); background: var(--caf-accent-tint); }
2732
2749
  /* mobile.nav: "bottom" \u2014 the app-shell tab bar pinned to the thumb zone.
2733
2750
  Tabs share the width evenly so every section stays visible; the root
2734
2751
  reserves space so the bar never covers the end of the content. */