@squadbase/vantage 0.6.2-dev1.05a930b → 0.6.2-dev1.100dfeb

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.
Files changed (184) hide show
  1. package/dist/{build-X5NVCQKT.js → build-U7GO26HR.js} +7 -7
  2. package/dist/{build-X5NVCQKT.js.map → build-U7GO26HR.js.map} +1 -1
  3. package/dist/{check-ZBIH422T.js → check-MRVAIHG5.js} +5 -5
  4. package/dist/{check-ZBIH422T.js.map → check-MRVAIHG5.js.map} +1 -1
  5. package/dist/chunk-2IAPIOPS.js +152 -0
  6. package/dist/chunk-2IAPIOPS.js.map +1 -0
  7. package/dist/chunk-57GHR5LN.js +163 -0
  8. package/dist/chunk-57GHR5LN.js.map +1 -0
  9. package/dist/chunk-6OTTGAPO.js +44 -0
  10. package/dist/chunk-6OTTGAPO.js.map +1 -0
  11. package/dist/{chunk-V3LF7BE7.js → chunk-ANZLVSEP.js} +4 -5
  12. package/dist/chunk-ANZLVSEP.js.map +1 -0
  13. package/dist/{chunk-XQUSGIDG.js → chunk-CKWPK4RO.js} +10 -3
  14. package/dist/chunk-CKWPK4RO.js.map +1 -0
  15. package/dist/chunk-JXFCSF3K.js +673 -0
  16. package/dist/chunk-JXFCSF3K.js.map +1 -0
  17. package/dist/{chunk-7DZGJQ3P.js → chunk-PJ4SPXHS.js} +23 -6
  18. package/dist/chunk-PJ4SPXHS.js.map +1 -0
  19. package/dist/{chunk-IUPSBD6W.js → chunk-QOXXOFGL.js} +23 -886
  20. package/dist/chunk-QOXXOFGL.js.map +1 -0
  21. package/dist/{chunk-CEXAQQ5W.js → chunk-S5MDTHH7.js} +180 -18
  22. package/dist/chunk-S5MDTHH7.js.map +1 -0
  23. package/dist/{chunk-2ICNHUG6.js → chunk-SDJTTMLK.js} +64 -7
  24. package/dist/chunk-SDJTTMLK.js.map +1 -0
  25. package/dist/{chunk-WAMZWZJB.js → chunk-SHZM3EXB.js} +19 -8
  26. package/dist/chunk-SHZM3EXB.js.map +1 -0
  27. package/dist/{chunk-JKLLFVEU.js → chunk-UWSQUHX6.js} +4 -99
  28. package/dist/chunk-UWSQUHX6.js.map +1 -0
  29. package/dist/cli.js +6 -6
  30. package/dist/client/index.d.ts +2 -0
  31. package/dist/client/index.js +8 -2
  32. package/dist/client/index.js.map +1 -1
  33. package/dist/components/index.d.ts +11 -247
  34. package/dist/components/index.js +4 -1
  35. package/dist/delta-text-CvwBGJ95.d.ts +15 -0
  36. package/dist/{dev-4XEDTELV.js → dev-O42JFQ64.js} +7 -7
  37. package/dist/{dev-4XEDTELV.js.map → dev-O42JFQ64.js.map} +1 -1
  38. package/dist/doc/index.d.ts +215 -2
  39. package/dist/doc/index.js +307 -3
  40. package/dist/doc/index.js.map +1 -1
  41. package/dist/{doctor-5E2VEZPK.js → doctor-YD3XAAOT.js} +5 -5
  42. package/dist/{doctor-5E2VEZPK.js.map → doctor-YD3XAAOT.js.map} +1 -1
  43. package/dist/format-U0cQl069.d.ts +243 -0
  44. package/dist/query/index.js +2 -2
  45. package/dist/{routes-3SGAFDMC.js → routes-PVKNT777.js} +4 -4
  46. package/dist/{routes-3SGAFDMC.js.map → routes-PVKNT777.js.map} +1 -1
  47. package/dist/slide/index.d.ts +272 -1
  48. package/dist/slide/index.js +972 -2
  49. package/dist/slide/index.js.map +1 -1
  50. package/dist/table/index.js +6 -3
  51. package/dist/table/index.js.map +1 -1
  52. package/dist/{upgrade-JHXZBGQS.js → upgrade-6DT7ADRP.js} +4 -4
  53. package/dist/{upgrade-JHXZBGQS.js.map → upgrade-6DT7ADRP.js.map} +1 -1
  54. package/dist/vite/index.js +3 -3
  55. package/docs/en/agent-skills.md +1 -1
  56. package/docs/en/changelog.md +2 -0
  57. package/docs/en/components.md +13 -2
  58. package/docs/en/doc/delta-text.md +40 -0
  59. package/docs/en/doc/doc-action-items.md +49 -0
  60. package/docs/en/doc/doc-callout.md +38 -0
  61. package/docs/en/doc/doc-data-table.md +60 -0
  62. package/docs/en/doc/doc-executive-summary.md +64 -0
  63. package/docs/en/doc/doc-figure.md +56 -0
  64. package/docs/en/doc/doc-key-findings.md +61 -0
  65. package/docs/en/doc/doc-kpi-scorecard.md +80 -0
  66. package/docs/en/doc/doc-notes-and-sources.md +43 -0
  67. package/docs/en/doc/doc-report-header.md +46 -0
  68. package/docs/en/pages-and-metadata.md +15 -0
  69. package/docs/en/parts/area-chart.md +99 -0
  70. package/docs/en/parts/bar-chart.md +91 -0
  71. package/docs/en/parts/bar-list.md +50 -0
  72. package/docs/en/parts/category-bar.md +37 -0
  73. package/docs/en/parts/chart-frame.md +39 -0
  74. package/docs/en/parts/delta.md +4 -3
  75. package/docs/en/parts/donut-chart.md +72 -0
  76. package/docs/en/parts/funnel-steps.md +2 -1
  77. package/docs/en/parts/heatmap.md +53 -0
  78. package/docs/en/parts/kpi-card.md +2 -1
  79. package/docs/en/parts/legend.md +35 -0
  80. package/docs/en/parts/line-chart.md +94 -0
  81. package/docs/en/parts/progress-circle.md +33 -0
  82. package/docs/en/parts/sample-data.md +39 -0
  83. package/docs/en/parts/scatter-chart.md +56 -0
  84. package/docs/en/parts/tracker.md +36 -0
  85. package/docs/en/parts/updated-at.md +30 -0
  86. package/docs/en/parts/waterfall-chart.md +56 -0
  87. package/docs/en/slide/slide-chart-full.md +48 -0
  88. package/docs/en/slide/slide-chart-insight.md +52 -0
  89. package/docs/en/slide/slide-columns.md +46 -0
  90. package/docs/en/slide/slide-comparison.md +68 -0
  91. package/docs/en/slide/slide-cover.md +39 -0
  92. package/docs/en/slide/slide-exec-summary.md +47 -0
  93. package/docs/en/slide/slide-heading.md +38 -0
  94. package/docs/en/slide/slide-kpi-grid.md +55 -0
  95. package/docs/en/slide/slide-kpi-hero.md +58 -0
  96. package/docs/en/slide/slide-next-actions.md +49 -0
  97. package/docs/en/slide/slide-scorecard-table.md +54 -0
  98. package/docs/en/slide/slide-section.md +44 -0
  99. package/docs/en/slide/slide-source-line.md +34 -0
  100. package/docs/en/ui/alert-dialog.md +42 -0
  101. package/docs/en/ui/avatar.md +31 -0
  102. package/docs/en/ui/combobox.md +74 -0
  103. package/docs/en/ui/drawer.md +41 -0
  104. package/docs/en/ui/empty.md +1 -1
  105. package/docs/en/ui/error-state.md +4 -3
  106. package/docs/en/ui/field.md +42 -0
  107. package/docs/en/ui/hover-card.md +21 -0
  108. package/docs/en/ui/kbd.md +17 -0
  109. package/docs/en/ui/loading.md +1 -1
  110. package/docs/en/ui/pagination.md +36 -0
  111. package/docs/en/ui/radio-group.md +21 -0
  112. package/docs/en/ui/toast.md +44 -0
  113. package/docs/en/ui/toggle-group.md +4 -3
  114. package/docs/index.json +857 -77
  115. package/docs/ja/agent-skills.md +1 -1
  116. package/docs/ja/changelog.md +2 -0
  117. package/docs/ja/components.md +12 -1
  118. package/docs/ja/doc/delta-text.md +40 -0
  119. package/docs/ja/doc/doc-action-items.md +49 -0
  120. package/docs/ja/doc/doc-callout.md +38 -0
  121. package/docs/ja/doc/doc-data-table.md +60 -0
  122. package/docs/ja/doc/doc-executive-summary.md +64 -0
  123. package/docs/ja/doc/doc-figure.md +56 -0
  124. package/docs/ja/doc/doc-key-findings.md +61 -0
  125. package/docs/ja/doc/doc-kpi-scorecard.md +80 -0
  126. package/docs/ja/doc/doc-notes-and-sources.md +43 -0
  127. package/docs/ja/doc/doc-report-header.md +46 -0
  128. package/docs/ja/page-types/slide.md +1 -0
  129. package/docs/ja/pages-and-metadata.md +15 -0
  130. package/docs/ja/parts/area-chart.md +49 -25
  131. package/docs/ja/parts/bar-chart.md +50 -24
  132. package/docs/ja/parts/bar-list.md +31 -8
  133. package/docs/ja/parts/chart-frame.md +36 -0
  134. package/docs/ja/parts/delta.md +5 -4
  135. package/docs/ja/parts/donut-chart.md +45 -26
  136. package/docs/ja/parts/funnel-steps.md +27 -8
  137. package/docs/ja/parts/heatmap.md +45 -0
  138. package/docs/ja/parts/kpi-card.md +2 -1
  139. package/docs/ja/parts/legend.md +36 -0
  140. package/docs/ja/parts/line-chart.md +48 -22
  141. package/docs/ja/parts/sample-data.md +61 -0
  142. package/docs/ja/parts/scatter-chart.md +69 -0
  143. package/docs/ja/parts/updated-at.md +27 -0
  144. package/docs/ja/parts/waterfall-chart.md +61 -0
  145. package/docs/ja/slide/slide-chart-full.md +48 -0
  146. package/docs/ja/slide/slide-chart-insight.md +52 -0
  147. package/docs/ja/slide/slide-columns.md +46 -0
  148. package/docs/ja/slide/slide-comparison.md +68 -0
  149. package/docs/ja/slide/slide-cover.md +39 -0
  150. package/docs/ja/slide/slide-exec-summary.md +47 -0
  151. package/docs/ja/slide/slide-heading.md +38 -0
  152. package/docs/ja/slide/slide-kpi-grid.md +55 -0
  153. package/docs/ja/slide/slide-kpi-hero.md +58 -0
  154. package/docs/ja/slide/slide-next-actions.md +49 -0
  155. package/docs/ja/slide/slide-scorecard-table.md +54 -0
  156. package/docs/ja/slide/slide-section.md +44 -0
  157. package/docs/ja/slide/slide-source-line.md +34 -0
  158. package/docs/ja/ui/empty.md +1 -1
  159. package/docs/ja/ui/error-state.md +4 -3
  160. package/docs/ja/ui/loading.md +1 -1
  161. package/docs/ja/ui/toggle-group.md +4 -3
  162. package/package.json +2 -2
  163. package/registry/ui/delta.tsx +7 -4
  164. package/registry/ui/echart.tsx +123 -1
  165. package/registry/ui/kpi-card.tsx +9 -4
  166. package/skills/vantage-design/SKILL.md +54 -54
  167. package/skills/vantage-design/references/extract.md +26 -27
  168. package/skills/vantage-design/references/format.md +12 -172
  169. package/skills/vantage-design/references/presets.md +50 -110
  170. package/skills/vantage-pitfalls/SKILL.md +5 -0
  171. package/skills/vantage-slide/SKILL.md +54 -36
  172. package/templates/AGENTS.md +32 -16
  173. package/templates/agents/doc.md +48 -0
  174. package/templates/agents/page.md +23 -2
  175. package/templates/agents/slide.md +77 -3
  176. package/theme.css +10 -7
  177. package/dist/chunk-2ICNHUG6.js.map +0 -1
  178. package/dist/chunk-7DZGJQ3P.js.map +0 -1
  179. package/dist/chunk-CEXAQQ5W.js.map +0 -1
  180. package/dist/chunk-IUPSBD6W.js.map +0 -1
  181. package/dist/chunk-JKLLFVEU.js.map +0 -1
  182. package/dist/chunk-V3LF7BE7.js.map +0 -1
  183. package/dist/chunk-WAMZWZJB.js.map +0 -1
  184. package/dist/chunk-XQUSGIDG.js.map +0 -1
@@ -691,6 +691,116 @@ export function echartSetOptionOpts(prev: EChartsOption | null, next: EChartsOpt
691
691
  return { notMerge: false, replaceMerge: [...types], lazyUpdate: true }
692
692
  }
693
693
 
694
+ /**
695
+ * Component types that get a generated `id` when the option has none, so replaceMerge can match them across updates.
696
+ * Not `dataset` / `graphic` / `tooltip`: they are rebuilt on every update (there is nothing to transition).
697
+ */
698
+ const ID_TYPES = [
699
+ "title", "legend", "grid", "xAxis", "yAxis", "polar", "radiusAxis", "angleAxis", "radar",
700
+ "dataZoom", "visualMap", "geo", "singleAxis", "calendar",
701
+ ] as const
702
+
703
+ /**
704
+ * What `withStableIds` remembers between updates: per generated id, the shape last applied and how many
705
+ * times it changed (the id's `~v<n>` suffix); per component type, the order of its ids.
706
+ */
707
+ export interface StableIdState {
708
+ ids: Map<string, { shape: string; version: number }>
709
+ order: Map<string, string[]>
710
+ }
711
+ export const createStableIdState = (): StableIdState => ({ ids: new Map(), order: new Map() })
712
+
713
+ /**
714
+ * Shape of a component: which keys it has, recursively (arrays only as "array", values not at all —
715
+ * except `type`). A merge keeps keys the new option dropped, so a changed shape must not be merged.
716
+ */
717
+ function shapeOf(v: unknown): string {
718
+ if (Array.isArray(v)) return "[]"
719
+ if (!v || typeof v !== "object" || Object.getPrototypeOf(v) !== Object.prototype) return "."
720
+ const o = v as Record<string, unknown>
721
+ return `{${Object.keys(o)
722
+ .filter((k) => o[k] !== undefined)
723
+ .sort()
724
+ .map((k) => (k === "type" || k === "id" ? `${k}=${String(o[k])}` : `${k}:${shapeOf(o[k])}`))
725
+ .join(",")}}`
726
+ }
727
+
728
+ /**
729
+ * Gives every series (and the common components) an `id` it does not have yet —
730
+ * series: `series:<name ?? index>`, others `<type>:<index>` — so an update that
731
+ * only changes values is matched to the existing component and transitions
732
+ * instead of being rebuilt. If the component's shape changed since `state` last
733
+ * saw it (a key was removed or added, the type changed), the id gets a new
734
+ * `~v<n>` suffix so replaceMerge builds it from scratch: the result then always
735
+ * equals applying the option with `notMerge`. Explicit ids are kept; the input is not mutated.
736
+ */
737
+ export function withStableIds(option: EChartsOption, state: StableIdState = createStableIdState()): EChartsOption {
738
+ const out: Record<string, unknown> = { ...(option as Record<string, unknown>) }
739
+ const taken = new Set<string>()
740
+ const list = (v: unknown): Record<string, unknown>[] | null =>
741
+ Array.isArray(v) ? (v as Record<string, unknown>[]) : v && typeof v === "object" ? [v as Record<string, unknown>] : null
742
+ const give = (key: string, pick: (c: Record<string, unknown>, i: number) => string) => {
743
+ const items = list(out[key])
744
+ if (!items) return
745
+ const used = new Set<string>(items.flatMap((c) => (c && c.id != null ? [String(c.id)] : [])))
746
+ const bases = items.map((c, i) => {
747
+ if (!c || typeof c !== "object" || c.id != null) return null
748
+ let base = pick(c, i)
749
+ for (let n = 2; used.has(base) || taken.has(`${key}\u0000${base}`); n++) base = `${pick(c, i)}#${n}`
750
+ used.add(base)
751
+ taken.add(`${key}\u0000${base}`)
752
+ return base
753
+ })
754
+ // A merge fills the empty slots a removal left and keeps the slot order of what exists, so after any
755
+ // change in which components there are (or their order) it would not match a fresh render: rebuild them all.
756
+ const prevOrder = state.order.get(key) ?? []
757
+ const cur = bases.filter((b): b is string => b !== null)
758
+ const inOrder = cur.length === prevOrder.length && cur.every((b, i) => b === prevOrder[i])
759
+ state.order.set(key, cur)
760
+ out[key] = items.map((c, i) => {
761
+ const base = bases[i]
762
+ if (base == null) return c
763
+ const shape = shapeOf(c)
764
+ const prev = state.ids.get(base)
765
+ const version = !prev ? 0 : prev.shape === shape && inOrder ? prev.version : prev.version + 1
766
+ state.ids.set(base, { shape, version })
767
+ return { ...c, id: version ? `${base}~v${version}` : base }
768
+ })
769
+ }
770
+ give("series", (c, i) => `series:${typeof c.name === "string" && c.name ? c.name : i}`)
771
+ for (const key of ID_TYPES) give(key, (_c, i) => `${key}:${i}`)
772
+ return out as EChartsOption
773
+ }
774
+
775
+ /** Copy of the plain objects / arrays of an option (functions, class instances, Dates stay shared), to compare against later in-place edits. */
776
+ export function snapshotOption<T>(v: T): T {
777
+ if (Array.isArray(v)) return v.map(snapshotOption) as T
778
+ if (v && typeof v === "object") {
779
+ const proto = Object.getPrototypeOf(v)
780
+ if (proto === Object.prototype || proto === null) {
781
+ return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, snapshotOption(x)])) as T
782
+ }
783
+ }
784
+ return v
785
+ }
786
+
787
+ /** Structural equality for option trees; functions and other non-plain objects compare by reference. */
788
+ export function optionEquals(a: unknown, b: unknown): boolean {
789
+ if (Object.is(a, b)) return true
790
+ if (typeof a !== "object" || typeof b !== "object" || !a || !b) return false
791
+ if (Array.isArray(a) !== Array.isArray(b)) return false
792
+ if (Array.isArray(a)) {
793
+ const bb = b as unknown[]
794
+ return a.length === bb.length && a.every((v, i) => optionEquals(v, bb[i]))
795
+ }
796
+ if (a instanceof Date || b instanceof Date) return a instanceof Date && b instanceof Date && a.getTime() === b.getTime()
797
+ const pa = Object.getPrototypeOf(a)
798
+ if ((pa !== Object.prototype && pa !== null) || Object.getPrototypeOf(b) !== pa) return false
799
+ const ka = Object.keys(a)
800
+ const kb = Object.keys(b)
801
+ return ka.length === kb.length && ka.every((k) => Object.hasOwn(b, k) && optionEquals((a as Record<string, unknown>)[k], (b as Record<string, unknown>)[k]))
802
+ }
803
+
694
804
  /** `graphic` text doesn't inherit the global `textStyle`, so give it the inherited font unless it sets its own. */
695
805
  function withGraphicFont(option: EChartsOption, fontFamily: string | undefined): EChartsOption {
696
806
  const graphic = (option as { graphic?: unknown }).graphic
@@ -748,6 +858,9 @@ export const EChart = forwardRef<HTMLDivElement, EChartProps>(function EChart(
748
858
  const reducedMotion = usePrefersReducedMotion()
749
859
  // What the current instance last received, to pick the merge mode of the next update.
750
860
  const appliedRef = useRef<EChartsOption | null>(null)
861
+ // The option (before ids) as it was when applied, deep-copied; and the shape history behind the generated ids.
862
+ const snapshotRef = useRef<EChartsOption | null>(null)
863
+ const idStateRef = useRef<StableIdState>(createStableIdState())
751
864
 
752
865
  const autoTheme = useMemo(() => (tokens ? buildChartTheme(tokens) : undefined), [tokens])
753
866
  const resolvedTheme = theme ?? autoTheme
@@ -759,6 +872,7 @@ export const EChart = forwardRef<HTMLDivElement, EChartProps>(function EChart(
759
872
  if (!container) return
760
873
  const inst = init(container, resolvedTheme, { renderer: "canvas" })
761
874
  appliedRef.current = null
875
+ snapshotRef.current = null
762
876
  // `data-rendering` stays on until the (animated) paint is done, so anything
763
877
  // capturing the page — printing a document — can wait for real pixels.
764
878
  container.dataset.rendering = ""
@@ -786,10 +900,18 @@ export const EChart = forwardRef<HTMLDivElement, EChartProps>(function EChart(
786
900
 
787
901
  useEffect(() => {
788
902
  if (!instance || instance.isDisposed()) return
903
+ const prepared = withGraphicFont(
904
+ withMotionPreference(option, reducedMotion),
905
+ theme === undefined ? tokens?.fontFamily : undefined,
906
+ )
907
+ // Same content as last time (a re-render that built an equal option): nothing to redraw.
908
+ // Compared with a snapshot, so an in-place edit of a nested array of the caller's option still counts as a change.
909
+ if (snapshotRef.current && optionEquals(snapshotRef.current, prepared)) return
789
910
  if (containerRef.current) containerRef.current.dataset.rendering = ""
790
- const next = withGraphicFont(withMotionPreference(option, reducedMotion), theme === undefined ? tokens?.fontFamily : undefined)
911
+ const next = withStableIds(prepared, idStateRef.current)
791
912
  instance.setOption(next, echartSetOptionOpts(appliedRef.current, next))
792
913
  appliedRef.current = next
914
+ snapshotRef.current = snapshotOption(prepared)
793
915
  }, [instance, option, reducedMotion, theme, tokens?.fontFamily])
794
916
 
795
917
  useEffect(() => {
@@ -43,7 +43,7 @@
43
43
  * line takes its name from it ("昨日 617"), the same way a `label` on any `vs` does.
44
44
  * - **Two or more comparisons sit under the value**, not beside it, so the value
45
45
  * keeps its place and its width whatever the card holds.
46
- * - **Polarity drives everything.** With `polarity="lower-is-better"` the target
46
+ * - **`betterWhen` drives everything.** With `betterWhen="down"` the target
47
47
  * is met at or below it, and the record badge marks the lowest value.
48
48
  * - **Rates change in points.** With a `percent` format the delta and
49
49
  * `{difference}` are in percentage points ("-0.5pt"); `change="relative"` opts
@@ -147,7 +147,9 @@ export interface KpiCardProps extends Omit<ComponentProps<"div">, "children"> {
147
147
  * @example provisional / provisional={{ label: "暫定" }}
148
148
  */
149
149
  provisional?: boolean | { label?: string }
150
- /** Whether a rise is good. Default `"higher-is-better"`. `"neutral"` never colours the change and skips the target and record logic. */
150
+ /** Which direction is good: `"up"` (default) or `"down"` (churn, error rate). Wins over `polarity`. */
151
+ betterWhen?: "up" | "down"
152
+ /** @deprecated Older name of `betterWhen` (`"higher-is-better"` / `"lower-is-better"`). `"neutral"` (never colours the change, skips the target and record logic) is only reachable here. `betterWhen` wins if both are given. */
151
153
  polarity?: Polarity
152
154
  /** `"relative"` ("+12.4%"), `"points"` ("-0.5pt") or `"difference"` ("+¥530万", in `format`). Default: points for a `percent` format, else relative. */
153
155
  change?: ChangeDisplay
@@ -160,7 +162,7 @@ export interface KpiCardProps extends Omit<ComponentProps<"div">, "children"> {
160
162
  /**
161
163
  * Target for the current period. Under the value it adds a 6px bar (actual, the pace
162
164
  * projection, a tick at the target) and one line "目標の 49%・このペースなら 98%"
163
- * (the projection only while `period` is in progress). With `polarity="lower-is-better"`
165
+ * (the projection only while `period` is in progress). With `betterWhen="down"`
164
166
  * or a rate target (points) there is no bar: "目標 … まであと …" in the footer.
165
167
  */
166
168
  target?: number
@@ -325,7 +327,8 @@ export function KpiCard({
325
327
  comparison: comparisonProp,
326
328
  comparisons,
327
329
  provisional,
328
- polarity,
330
+ betterWhen,
331
+ polarity: polarityProp,
329
332
  change,
330
333
  neutralBand,
331
334
  showPrevious = true,
@@ -352,6 +355,8 @@ export function KpiCard({
352
355
  const isProvisional = provisional !== undefined && provisional !== false
353
356
  const provisionalLabel = typeof provisional === "object" && provisional.label ? provisional.label : "速報"
354
357
  const sample = useSampleMark()
358
+ // `betterWhen` is the documented name and wins over the older `polarity`.
359
+ const polarity: Polarity | undefined = betterWhen ? (betterWhen === "down" ? "lower-is-better" : "higher-is-better") : polarityProp
355
360
  const neutral = polarity === "neutral"
356
361
  const hasPrevious = comparison !== undefined && showPrevious
357
362
  // Two or more badges would squeeze the value from the right; they get their own row under it instead.
@@ -1,71 +1,76 @@
1
1
  ---
2
2
  name: vantage-design
3
- description: Vantage(@squadbase/vantage)アプリの見た目の意図 `DESIGN.md` と、実際の値を持つ `styles.css` をまとめて作る / 更新する手順。配色・ブランドカラー・テーマ・チャートの色・フォント・角丸・トーンを変えたいとき、既存サイトやロゴ・資料・外部の DESIGN.md に合わせたいとき、プロジェクト作成時の初期デザイン(プリセット default / calm / vivid / mono)を決めるときに使う。「色を変えて」「ブランドに合わせて」「このサイトに合わせて」「落ち着いた感じに」「フォントを変えて」「テーマを作って」「DESIGN.md を作って」、"change the colors", "match our brand", "match this website", "theme", "color palette", "font", "design system", "DESIGN.md" で起動。
3
+ description: Vantage(@squadbase/vantage)アプリのテーマ(色・チャートの色・書体・角丸)を、`styles.css` とテーマコマンドで作る / 更新する手順。配色・ブランドカラー・テーマ・チャートの色・フォント・角丸・トーンを変えたいとき、既存サイトやロゴ・資料に合わせたいとき、プロジェクト作成時の初期デザイン(テーマ neutral / ocean / forest / sunset / violet / rose / mono)を決めるときに使う。「色を変えて」「ブランドに合わせて」「このサイトに合わせて」「落ち着いた感じに」「フォントを変えて」「テーマを作って」、"change the colors", "match our brand", "match this website", "theme", "color palette", "font", "design system" で起動。
4
4
  ---
5
5
 
6
- # DESIGN.md と styles.css をまとめて更新する
6
+ # テーマと styles.css を更新する
7
7
 
8
- Vantage アプリの見た目は 2 つのファイルで決まる。
8
+ Vantage アプリの色・書体・角丸の値と色の割り当ては、SquadKit のテーマだけが持つ。
9
9
 
10
- | ファイル | 持つもの | 所有者 |
11
- | --- | --- | --- |
12
- | `DESIGN.md`(プロジェクトルート) | 意図とルール。**トークン名だけ**を書き、色コードは書かない(150 行以内) | ユーザー |
13
- | `styles.css`(ページと同じ側。通常 `src/styles.css`) | トークンの実際の値(`:root` と `.dark`) | ユーザー |
10
+ | 手段 | 何を決めるか |
11
+ | --- | --- |
12
+ | ThemeDialog / `theme <id>` / `chart <preset>`(CLI) | 出発点のテーマとチャートの配色 |
13
+ | `styles.css`(ページと同じ側。通常 `src/styles.css`)の `/* squadkit-theme:end */` より後ろ | ブランド色・書体など、テーマに無い値の上書き |
14
14
 
15
- **2 つは必ず同じステップで更新する。** 片方だけ直すと、次にページを書くエージェントが古い意図に
16
- 従ってしまう。デザイン変更の依頼をページ単位の `className` 修正で済ませない。
15
+ デザイン変更の依頼(「もっと落ち着いた色に」など)は、**テーマの切り替えかスタイルシートの上書きで完結させる。**
16
+ `DESIGN.md` へは記録しない。今のテーマ名は `styles.css` の `/* theme: <name> */` マーカーから読む。
17
+ ページ単位の `className` 修正で済ませない。
17
18
 
18
- 一般規約(トークンで色を書く・設定ファイルを作らない)は `AGENTS.md` が正本。ここでは手順だけを扱う。
19
+ 見た目の規則(意味トークンの使い分け・カードで包む・KPI → チャート → 表など)は `AGENTS.md` の
20
+ 「見た目の規約」節が正本。ここでは手順だけを扱い、規則は重複させない。
21
+ 任意の `DESIGN.md`(ユーザー固有の意図を書くファイル)は、ユーザーが望んだときだけ扱う(`references/format.md`)。
19
22
 
20
23
  ## リファレンス
21
24
 
22
25
  | ファイル | 読む場面 |
23
26
  | --- | --- |
24
- | `references/format.md` | `DESIGN.md` を書く / 直すとき(書式・節の順番・雛形) |
25
- | `references/extract.md` | URL・画像・pptx/pdf・言葉・外部 DESIGN.md から色と書体を取り出すとき |
26
- | `references/presets.md` | プロジェクト作成時の初期デザイン(`default` / `calm` / `vivid` / `mono`) |
27
+ | `references/format.md` | ユーザーが任意の `DESIGN.md` を望んだとき(位置づけと優先順位) |
28
+ | `references/extract.md` | URL・画像・pptx/pdf・言葉から色と書体を取り出すとき |
29
+ | `references/presets.md` | テーマ(`neutral` / `ocean` / `forest` / `sunset` / `violet` / `rose` / `mono`)の選び方と `theme` コマンド。旧プリセット id の読み替え |
27
30
  | `scripts/contrast.mjs` | 文字色と背景色のコントラスト比を確かめるとき |
28
31
 
29
32
  ## 手順
30
33
 
31
34
  ### 1. 入力を集める
32
35
 
33
- - 何に合わせるのか(URL・ロゴ画像・資料・言葉・外部の `DESIGN.md`)を確認し、
36
+ - 何に合わせるのか(URL・ロゴ画像・資料・言葉)を確認し、
34
37
  `references/extract.md` の該当する節の手順で色・書体・トーンを取り出す。
35
- - 既存の `DESIGN.md` と `styles.css` を読む。既にあるルールで今回触らないものは残す。
38
+ - 既存の `styles.css` を読み、マーカーから今のテーマ名を確かめる。今回触らない上書きは残す。
39
+ - プロジェクトルートに `DESIGN.md` があれば読む(ユーザー所有。書き換えない)。色の割り当てはテーマが優先。
36
40
  - スライド(`*.slide.tsx`)があるかを `ls` / `vantage routes` で確認しておく。
37
41
 
38
42
  ### 2. 提案して確認を取る
39
43
 
40
44
  書き込む前に、次の 2 つをユーザーに見せて確認を取る。
41
45
 
42
- - **役割 → トークンの対応表**(主色・強調・良い/悪い/注意・背景の段階・文字・チャート 5 色・書体)。
46
+ - **役割 → トークンの対応表**(主色・背景の段階・文字・チャート 5 色・書体。良い/悪い/注意/情報/中立はテーマの意味トークンに任せる)。
43
47
  各行に「何に合わせてその色にしたか」を 1 語添える。
44
- - **トーンの説明**(2〜3 行。`DESIGN.md` の「全体のトーン」にそのまま入る文)。
45
-
46
- プリセットで足りる場合はプリセット名(`references/presets.md`)で提案してよい。
47
-
48
- ### 3. styles.css と DESIGN.md を同じステップで書く
49
-
50
- `styles.css`:
51
-
52
- - **マーカーブロックの中は手で編集しない。**
53
- ``/* vantage-template:chart-preset — regenerated by `chart`; edits here are lost */`` 〜
54
- `/* vantage-template:chart-preset:end */` の間はコマンドが丸ごと書き換える生成物で、手で直した
55
- 値は次の適用で消える。
56
- - チャートの 5 色は必ずコマンドで選ぶ: `npx @squadbase/vantage-template chart <preset>`
57
- (`blue` / `ocean` / `sunset` / `forest` / `berry` / `mono`。プロジェクトルートで実行。
58
- `--dry-run` で書き込まずに結果を見られる)。マーカーの中だけが置き換わり、他の記述は残る。
59
- - プリセットに無いチャート色(ブランド色を `--chart-1` にしたいなど)は、マーカーブロックの**後ろ**に
48
+ - **トーンの説明**(2〜3 行)。
49
+
50
+ テーマで足りる場合はテーマ id(`references/presets.md`)で提案してよい。旧プリセット id(`default` / `calm` / `vivid` / `mono`)で頼まれたら新 id に読み替える。
51
+
52
+ ### 3. テーマを当て、styles.css を書く
53
+
54
+ - **テーマ範囲・チャート範囲のマーカーの中は手で編集しない。** 色・角丸・書体の出発点は 7 テーマ
55
+ (`neutral` / `ocean` / `forest` / `sunset` / `violet` / `rose` / `mono`)から選び、コマンドで当てる:
56
+ `npx @squadbase/vantage-template theme <id>`(プロジェクトルートで実行。`--dry-run` で書き込まずに
57
+ 結果を見られる)。``/* squadkit-theme — regenerated by `theme`; edits here are lost */`` 〜
58
+ `/* squadkit-theme:end */` の間はコマンドが丸ごと書き換える生成物で、手で直した値は次の適用で消える。
59
+ 旧プリセット id(`default` → `neutral` / `calm` → `ocean` / `vivid` → `sunset` / `mono`)もコマンドが受ける。
60
+ - チャートの 5 色はテーマが持つ。テーマと別の配色にしたいときだけ
61
+ `npx @squadbase/vantage-template chart <preset>`(`blue` / `ocean` / `sunset` / `forest` / `berry` / `mono`)を使う。
62
+ ``/* vantage-template:chart-preset … */`` 〜 `/* vantage-template:chart-preset:end */` の間が置き換わり、
63
+ テーマのチャート色より優先される。テーマに揃え直すには `theme <id> --reset-chart`。
64
+ - プリセットに無いチャート色(ブランド色を `--chart-1` にしたいなど)は、**マーカーブロックの後ろ**に
60
65
  別の `:root { --chart-1: …; }` と `.dark, [data-theme="dark"] { --chart-1: …; }` を足して上書きする
61
- (後に書いた方が勝つ)。上書きするのは変えたい番号だけにし、残りはプリセットに任せる。
62
- - `--primary` / `--primary-foreground` / `--radius` などチャート以外のトークンは、マーカーの**外**
63
- (ファイル先頭の `:root` ブロック)に書く。色は `:root` と `.dark, [data-theme="dark"]` の
64
- **両方**に書く。片方だけだとダークモードで既定に戻る。値は `oklch()` か Tailwind のパレット変数
65
- (`var(--color-sky-600)`)で書く。
66
- - 書体は既定でシステムフォント。**ページの書体を決めるのは `body` セレクタの `font-family`**
67
- (フレームワークの `theme.css` が `body` にシステムフォントを直接指定しているため、`:root` や
68
- `html` に `font-family` を書いても本文には効かない)。スライドの書体は `:root` の
66
+ (後に書いた方が勝つ)。上書きするのは変えたい番号だけにし、残りはテーマに任せる。
67
+ - `--primary` / `--primary-foreground` / `--radius` などテーマに無い値・ブランド色の上書きも、
68
+ **マーカーブロックの後ろ**(ファイル末尾)に書く。マーカーより前に書いた値はテーマに負ける。
69
+ 色は `:root` と `.dark, [data-theme="dark"]` の**両方**に書く。片方だけだとダークモードでテーマの値に
70
+ 戻る。値は `oklch()` か Tailwind のパレット変数(`var(--color-sky-600)`)で書く。
71
+ - 書体は既定でシステムフォント。**ページの書体は `--font-sans`**(`styles.css` の `body` が
72
+ `font-family: var(--font-sans)` で参照する。テーマ範囲も `--font-sans` を持つので、書体を変えるには
73
+ マーカーブロックの後ろで `:root { --font-sans: …; }` を上書きする)。スライドの書体は `:root` の
69
74
  `--slide-font-display`(見出し)/ `--slide-font-body`(本文)で決まる。Web フォントを使うときの
70
75
  レシピ:
71
76
  1. Google Fonts の CSS
@@ -73,13 +78,11 @@ Vantage アプリの見た目は 2 つのファイルで決まる。
73
78
  から使う subset の `@font-face` ブロックだけを `styles.css` の先頭に貼る。
74
79
  **`@import url(...)` は使わない** ― `styles.css` はテーマの後ろに連結されるので
75
80
  「@import rules must precede all rules」の警告とともにビルドで捨てられる。
76
- 2. マーカーの外に次を書く(フォールバックは `theme.css` の既定と同じ並び):
81
+ 2. マーカーブロックの後ろに次を書く(フォールバックは `theme.css` の既定と同じ並び):
77
82
 
78
83
  ```css
79
- body {
80
- font-family: "<Font>", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Hiragino Sans", "Hiragino Kaku Gothic ProN", "Noto Sans JP", Meiryo, ui-sans-serif, system-ui, sans-serif;
81
- }
82
84
  :root {
85
+ --font-sans: "<Font>", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Hiragino Sans", "Hiragino Kaku Gothic ProN", "Noto Sans JP", Meiryo, ui-sans-serif, system-ui, sans-serif;
83
86
  --slide-font-display: "<Font>", -apple-system, BlinkMacSystemFont, "Segoe UI", "Hiragino Kaku Gothic ProN", "Hiragino Sans", "Yu Gothic", Meiryo, system-ui, sans-serif;
84
87
  --slide-font-body: var(--slide-font-display);
85
88
  }
@@ -90,18 +93,15 @@ Vantage アプリの見た目は 2 つのファイルで決まる。
90
93
  `ui-sans-serif` / `system-ui` は日本語の face より**後ろ**に置く。日本語の Windows ではどちらも
91
94
  Yu Gothic UI(UI のリサイズを避けるために字幅を詰めた書体)に解決され、カナがここで決まって
92
95
  半角風に見える。macOS の Chrome は `-apple-system` を解釈しないので `BlinkMacSystemFont` も並べる。
93
- 既存アプリの `styles.css` に `ui-sans-serif, system-ui, …` が先頭にある、または `"Yu Gothic UI"` が
94
- 残っているときは、`body` の `font-family` を上の並びに直す(ほかの行は書き換えない)。
96
+ 既存アプリの `styles.css` の `body` に `font-family` が直書きされている(`var(--font-sans)` ではない)、
97
+ または `ui-sans-serif, system-ui, …` が先頭にある、`"Yu Gothic UI"` が残っているときは、
98
+ `body { font-family: var(--font-sans); }` に直し、書体は `--font-sans` に上の並びで書く。
95
99
  日本語の Web フォントは face が 100 を超えるので原則使わない(pptx 書き出しも壊れる。
96
100
  `vantage-slide` の `references/tokens.md` 参照)。
97
101
 
98
102
  長い表(ダッシュボード): カードに `max-h-*` + `overflow-y-auto` の縦スクロール領域を作らず、
99
103
  ページング付きの `DataTablePreset`(`enablePagination`)にする(ホイールや End キーがページに届かなくなるため)。
100
104
 
101
- `DESIGN.md`: `references/format.md` の書式で書く。**色コードを書かない**(トークン名だけ)。
102
- 150 行を超えない(超えると `vantage check` が `DESIGN_MD_TOO_LONG` を出す)。ロゴなどの素材は
103
- `public/brand/logo.svg` のように `public/` 配下のパスで「ブランド素材」行に書く。
104
-
105
105
  ### 4. コントラストを確かめる
106
106
 
107
107
  主な文字色 / 背景色の組を、**ライトとダークの両方で**確かめる。
@@ -131,11 +131,11 @@ node <このスキルの場所>/scripts/contrast.mjs '<文字色>' '<背景色>'
131
131
  npx vantage check
132
132
  ```
133
133
 
134
- `HARDCODED_COLOR`(ページ内の色コード)や `DESIGN_MD_TOO_LONG` が出たらその場で直す。
134
+ `HARDCODED_COLOR`(ページ内の色コード)などが出たらその場で直す。
135
135
 
136
136
  ### 7. 完了を報告する
137
137
 
138
- 報告には、更新したファイル(`DESIGN.md` / `styles.css`)に加えて次を**必ず**入れる。
138
+ 報告には、更新したファイル(`styles.css` など)と当てたテーマ名に加えて次を**必ず**入れる。
139
139
 
140
140
  - プレビューページのパスと URL(`src/design-preview.tsx` → `/design-preview`)。
141
141
  - 「ホームなど既存の画面は一部の色しか使っていないので、変更が見えないことがある。配色はプレビュー
@@ -143,5 +143,5 @@ npx vantage check
143
143
 
144
144
  ## 会話中の指示との関係
145
145
 
146
- - 会話中の個別の指示(「このグラフだけ赤で」)は `DESIGN.md` より優先する。そのページだけ直せばよい。
147
- - 同じ指示が 2 回以上出たら、`DESIGN.md` への反映を提案する(反映するときはこの手順の 2 から)。
146
+ - 会話中の個別の指示(「このグラフだけ赤で」)はテーマより優先する。そのページだけ直せばよい。
147
+ - 同じ指示が 2 回以上出たら、`styles.css` の上書きへ反映することを提案する(この手順の 2 から)。
@@ -7,12 +7,13 @@
7
7
  3. **書体**(見出しと本文の `font-family`。システムフォントで足りるか)
8
8
  4. **トーン**(2〜3 語: 「落ち着いた」「明るい」「堅い」…)
9
9
 
10
- チャートの 5 色は、主色から直接作るより**主色に最も近いチャートプリセット**を選び、
11
- `npx @squadbase/vantage-template chart <preset>` で適用する方が安全(5 色の明度差・色覚多様性への
12
- 配慮が済んでいる)。`styles.css` の `vantage-template:chart-preset` マーカーブロックの中は
10
+ チャートの 5 色は、主色から直接作るより**主色に最も近いテーマ**を選び、
11
+ `npx @squadbase/vantage-template theme <id>` で適用する方が安全(5 色の明度差・色覚多様性への
12
+ 配慮が済んでいる)。テーマと別のチャート配色が要るときは `chart <preset>`。`styles.css` の
13
+ `squadkit-theme` / `vantage-template:chart-preset` のマーカーブロックの中は
13
14
  **手で編集しない**(次の適用で消える)。主色を `--chart-1` に入れたいときだけ、マーカーブロックの
14
15
  **後ろ**に `:root` と `.dark, [data-theme="dark"]` の `--chart-1` 上書きを足し、残り 4 色は
15
- プリセットに任せる。`--primary` や `--radius` もマーカーの外に書く。
16
+ プリセットに任せる。`--primary` や `--radius` はマーカーブロックの後ろに書く。
16
17
 
17
18
  ## URL(既存サイトに合わせる)
18
19
 
@@ -36,14 +37,14 @@ grep -oE 'font-family:[^;]+' /tmp/site.css | sort | uniq -c | sort -rn | head
36
37
  3. **ボタン・リンクの色**: `a {` `.btn` `button` `[class*=primary]` のセレクタの `color` /
37
38
  `background-color`。出現回数の多い有彩色が主色。
38
39
  4. **背景**: `body` / `html` の `background`。暗ければ「暗い基調」。
39
- 5. **書体**: `body` の `font-family`。Google Fonts の `<link>` があればその family 名。
40
+ 5. **書体**: `body` の `font-family`(書き込み先は `--font-sans`)。Google Fonts の `<link>` があればその family 名。
40
41
 
41
42
  注意:
42
43
 
43
44
  - SPA で HTML がほぼ空のときは、`<script>` より先に CSS の `<link>` を探す。それも無ければ
44
45
  ユーザーにスクリーンショットかブランドカラーを聞く(推測で埋めない)。
45
46
  - 取得できない(認証・ボット対策)ときは、その旨を伝えてスクリーンショットをもらう。
46
- - ロゴを使うなら `public/brand/` に保存し、`DESIGN.md` の「ブランド素材」行にパスを書く。
47
+ - ロゴを使うなら `public/brand/` に保存する。
47
48
 
48
49
  ## 画像・ロゴ
49
50
 
@@ -66,27 +67,26 @@ grep -oE 'font-family:[^;]+' /tmp/site.css | sort | uniq -c | sort -rn | head
66
67
 
67
68
  ## 言葉だけ(「落ち着いた感じに」)
68
69
 
69
- 形容詞を `presets.md` の 4 つに寄せ、そのうえで主色だけを調整する。
70
+ 形容詞を `presets.md` の 7 テーマに寄せ、そのうえで主色だけを調整する。
70
71
 
71
72
  | 言われたこと | 出発点 |
72
73
  | --- | --- |
73
- | 落ち着いた・信頼感・堅い・経営向け・青系 | `calm` |
74
- | 明るい・元気・勢い・暖かい・営業向け | `vivid` |
74
+ | 落ち着いた・信頼感・堅い・経営向け・青系 | `ocean` |
75
+ | 明るい・元気・勢い・暖かい・営業向け | `sunset` |
75
76
  | シンプル・モノクロ・印刷する・資料に貼る | `mono` |
76
- | 特に無し・標準・おまかせ | `default` |
77
- | 自然・環境・緑 | `default` の構成でチャートを `forest` に |
78
- | 華やか・ポップ・紫やピンク | `default` の構成でチャートを `berry` に |
77
+ | 特に無し・標準・おまかせ | `neutral` |
78
+ | 自然・環境・緑 | `forest` |
79
+ | 華やか・ポップ・紫 | `violet` |
80
+ | 華やか・ポップ・ピンク | `rose` |
79
81
 
80
- 当てはまらない言葉は、2 案(例: `calm` と `default`)を並べて選んでもらう。
82
+ 当てはまらない言葉は、2 案(例: `ocean` と `neutral`)を並べて選んでもらう。
81
83
 
82
84
  ## 外部の DESIGN.md(Stitch などが書いたもの)
83
85
 
84
- 外部の DESIGN.md は YAML frontmatter や本文に**値**(hex・px・フォント名)を持っていることが多い。
85
- Vantage の `DESIGN.md` は値を持たないので、**値を `styles.css` へ移し、`DESIGN.md` をトークン参照に
86
- 書き直す**。
86
+ 外部の DESIGN.md が値(hex・px・フォント名)を持っているときは、値だけを拾って**テーマ側**へ移す。
87
+ Vantage に `DESIGN.md` は作らない。
87
88
 
88
- 1. 値を拾う: primary / secondary / surface / background / on-* / error / success / warning、
89
- フォント、角丸。
89
+ 1. 値を拾う: primary / secondary / surface / background / on-* / error、フォント、角丸。
90
90
  2. 対応づける:
91
91
 
92
92
  | 外部の名前 | Vantage のトークン |
@@ -96,15 +96,14 @@ Vantage の `DESIGN.md` は値を持たないので、**値を `styles.css` へ
96
96
  | background / surface / surface-container | `--background` / `--card` / `--muted` |
97
97
  | on-background / on-surface / on-surface-variant | `--foreground` / `--card-foreground` / `--muted-foreground` |
98
98
  | outline | `--border` |
99
- | error | `--destructive` |
100
- | success / warning | 専用トークンなし → `--chart-*` のどれかに割り当てて `DESIGN.md` に書く |
99
+ | error | `--destructive`(意味トークンの negative) |
100
+ | success / warning | テーマの意味トークン(positive / warning)に任せる。チャートの色には割り当てない |
101
101
  | radius / corner | `--radius` |
102
- | display / headline font | `--slide-font-display`(+ ページの見出しなら `body` の font-family) |
103
- | body font | `body` セレクタの font-family と `--slide-font-body`(`:root` の font-family は効かない) |
102
+ | display / headline font | `--slide-font-display`(+ ページの見出しなら `--font-sans`) |
103
+ | body font | `--font-sans`(ページ本文)と `--slide-font-body` |
104
104
 
105
105
  3. `styles.css` のマーカーブロックの外に、`:root` と `.dark, [data-theme="dark"]` の両方で書く
106
- (チャート色・書体の書き方は SKILL.md の手順 3)。外部の DESIGN.md に
107
- ダークの値が無ければ、ライトの色相を保って明度だけ反転した値を作り、コントラストを確かめる。
108
- 4. `DESIGN.md` を `format.md` の節の順番で書き直す。外部の文章(トーン・Do/Don't・コンポーネントの
109
- ルール)は活かし、値だけをトークン名に置き換える。150 行に収まらない分は要約する。
110
- 5. 元のファイルは消さずに残すか(例: `docs/design-source.md`)、ユーザーに確認して削除する。
106
+ (チャート色・書体の書き方は SKILL.md の手順 3)。外部にダークの値が無ければ、ライトの色相を保って
107
+ 明度だけ反転した値を作り、コントラストを確かめる。
108
+ 4. 外部のトーン・Do/Don't・コンポーネントのルールは、ユーザーが望んだときだけ任意の `DESIGN.md`
109
+ (`format.md`)に要約して残す。値は書かない。