@cloudgrid-io/ui 0.6.0 → 0.8.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.
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * ## The mapping is a table, and the table is the thing a designer edits
10
10
  *
11
- * Nine statuses, five tones. `ENTITY_STATUS_TONE` is the whole mapping and
11
+ * Ten statuses, six tones. `ENTITY_STATUS_TONE` is the whole mapping and
12
12
  * nothing else in this file branches on a status, so retuning what colour
13
13
  * `paused` is means editing one line. `ENTITY_STATUS_LABEL` is the same
14
14
  * arrangement for the words.
@@ -41,7 +41,7 @@
41
41
  *
42
42
  * ## Grounds
43
43
  *
44
- * Four tones sit on their own fill. `offline` sits on the card ground with a
44
+ * Five tones sit on their own fill. `offline` sits on the card ground with a
45
45
  * control border instead, which is #111's decision rather than this component's:
46
46
  * the muted neutral on a neutral tint measures 2.90:1, under the bar, so absence
47
47
  * of state deliberately has no fill of its own.
@@ -55,10 +55,10 @@
55
55
  * both tables below and typecheck says so, which is the only arrangement where
56
56
  * a half-added status cannot ship.
57
57
  */
58
- export declare const ENTITY_STATUS_ORDER: readonly ["live", "building", "charged", "draft", "paused", "stopped", "archived", "expired", "error"];
58
+ export declare const ENTITY_STATUS_ORDER: readonly ["live", "unhealthy", "building", "charged", "draft", "paused", "stopped", "archived", "expired", "error"];
59
59
  export type EntityStatusName = (typeof ENTITY_STATUS_ORDER)[number];
60
- /** The five families of the state palette. One token pair each. */
61
- export type EntityStatusTone = "online" | "pending" | "offline" | "expired" | "danger";
60
+ /** The six families of the state palette. One token pair each. */
61
+ export type EntityStatusTone = "online" | "unhealthy" | "pending" | "offline" | "expired" | "danger";
62
62
  /**
63
63
  * THE MAPPING. Edit this to change what a status looks like.
64
64
  *
@@ -80,6 +80,14 @@ export type EntityStatusTone = "online" | "pending" | "offline" | "expired" | "d
80
80
  * `live` is the library's green, explicitly not the console's lime: the brand
81
81
  * has already spent lime on decoration, so a lime "live" would not read as a
82
82
  * signal.
83
+ *
84
+ * `unhealthy` is its OWN tone, added 2026-08-25 (#122). Before it existed the
85
+ * console had nowhere to put `live_unhealthy` and folded it onto `live`, so a
86
+ * degraded entity rendered green and a broken app looked healthy unless you
87
+ * read the word. It is deliberately not `expired` and not `danger`: expired is
88
+ * terminal, danger is failed, and this one is still serving and still fixable.
89
+ * `--cg-unhealthy` is a real value rather than an alias, so retuning it cannot
90
+ * move either of them.
83
91
  */
84
92
  export declare const ENTITY_STATUS_TONE: Record<EntityStatusName, EntityStatusTone>;
85
93
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"entity-status.d.ts","sourceRoot":"","sources":["../../src/blocks/entity-status.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,wGAUtB,CAAA;AAEV,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEnE,mEAAmE;AACnE,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAA;AAEtF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,gBAAgB,EAAE,gBAAgB,CAUzE,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAUhE,CAAA;AAkBD,iBAAS,YAAY,CAAC,EACpB,MAAM,EACN,KAAK,EACL,SAAS,EACT,GAAG,KAAK,EACT,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,GAAG;IAClD,MAAM,EAAE,gBAAgB,CAAA;IACxB,sFAAsF;IACtF,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CACxB,+BAqCA;AAED,OAAO,EAAE,YAAY,EAAE,CAAA"}
1
+ {"version":3,"file":"entity-status.d.ts","sourceRoot":"","sources":["../../src/blocks/entity-status.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,qHAWtB,CAAA;AAEV,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEnE,kEAAkE;AAClE,MAAM,MAAM,gBAAgB,GACxB,QAAQ,GACR,WAAW,GACX,SAAS,GACT,SAAS,GACT,SAAS,GACT,QAAQ,CAAA;AAEZ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,gBAAgB,EAAE,gBAAgB,CAWzE,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAehE,CAAA;AAmBD,iBAAS,YAAY,CAAC,EACpB,MAAM,EACN,KAAK,EACL,SAAS,EACT,GAAG,KAAK,EACT,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,GAAG;IAClD,MAAM,EAAE,gBAAgB,CAAA;IACxB,sFAAsF;IACtF,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CACxB,+BAqCA;AAED,OAAO,EAAE,YAAY,EAAE,CAAA"}
@@ -10,7 +10,7 @@ import { cn } from "../lib/cloudgrid-cn.js";
10
10
  *
11
11
  * ## The mapping is a table, and the table is the thing a designer edits
12
12
  *
13
- * Nine statuses, five tones. `ENTITY_STATUS_TONE` is the whole mapping and
13
+ * Ten statuses, six tones. `ENTITY_STATUS_TONE` is the whole mapping and
14
14
  * nothing else in this file branches on a status, so retuning what colour
15
15
  * `paused` is means editing one line. `ENTITY_STATUS_LABEL` is the same
16
16
  * arrangement for the words.
@@ -43,7 +43,7 @@ import { cn } from "../lib/cloudgrid-cn.js";
43
43
  *
44
44
  * ## Grounds
45
45
  *
46
- * Four tones sit on their own fill. `offline` sits on the card ground with a
46
+ * Five tones sit on their own fill. `offline` sits on the card ground with a
47
47
  * control border instead, which is #111's decision rather than this component's:
48
48
  * the muted neutral on a neutral tint measures 2.90:1, under the bar, so absence
49
49
  * of state deliberately has no fill of its own.
@@ -59,6 +59,7 @@ import { cn } from "../lib/cloudgrid-cn.js";
59
59
  */
60
60
  export const ENTITY_STATUS_ORDER = [
61
61
  "live",
62
+ "unhealthy",
62
63
  "building",
63
64
  "charged",
64
65
  "draft",
@@ -89,9 +90,18 @@ export const ENTITY_STATUS_ORDER = [
89
90
  * `live` is the library's green, explicitly not the console's lime: the brand
90
91
  * has already spent lime on decoration, so a lime "live" would not read as a
91
92
  * signal.
93
+ *
94
+ * `unhealthy` is its OWN tone, added 2026-08-25 (#122). Before it existed the
95
+ * console had nowhere to put `live_unhealthy` and folded it onto `live`, so a
96
+ * degraded entity rendered green and a broken app looked healthy unless you
97
+ * read the word. It is deliberately not `expired` and not `danger`: expired is
98
+ * terminal, danger is failed, and this one is still serving and still fixable.
99
+ * `--cg-unhealthy` is a real value rather than an alias, so retuning it cannot
100
+ * move either of them.
92
101
  */
93
102
  export const ENTITY_STATUS_TONE = {
94
103
  live: "online",
104
+ unhealthy: "unhealthy",
95
105
  building: "pending",
96
106
  charged: "offline",
97
107
  draft: "offline",
@@ -108,6 +118,11 @@ export const ENTITY_STATUS_TONE = {
108
118
  */
109
119
  export const ENTITY_STATUS_LABEL = {
110
120
  live: "Live",
121
+ // Not "Live, degraded". The word is the whole meaning when the colour is
122
+ // unfamiliar, and a comma makes it read as a variant of Live rather than as
123
+ // its own state. A surface that knows more passes `label`, e.g. "Failing
124
+ // health checks".
125
+ unhealthy: "Unhealthy",
111
126
  building: "Building",
112
127
  charged: "Charged",
113
128
  draft: "Draft",
@@ -126,6 +141,7 @@ export const ENTITY_STATUS_LABEL = {
126
141
  */
127
142
  const TONE_STYLE = {
128
143
  online: { chip: "border-transparent bg-online-fill", dot: "bg-online" },
144
+ unhealthy: { chip: "border-transparent bg-unhealthy-fill", dot: "bg-unhealthy" },
129
145
  pending: { chip: "border-transparent bg-pending-fill", dot: "bg-pending" },
130
146
  expired: { chip: "border-transparent bg-expired-fill", dot: "bg-expired" },
131
147
  danger: { chip: "border-transparent bg-danger-fill", dot: "bg-danger" },
@@ -1 +1 @@
1
- {"version":3,"file":"entity-status.js","sourceRoot":"","sources":["../../src/blocks/entity-status.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,oBAAoB,CAAA;AAEvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAEH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,MAAM;IACN,UAAU;IACV,SAAS;IACT,OAAO;IACP,QAAQ;IACR,SAAS;IACT,UAAU;IACV,SAAS;IACT,OAAO;CACC,CAAA;AAOV;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAA+C;IAC5E,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,SAAS;IACnB,OAAO,EAAE,SAAS;IAClB,KAAK,EAAE,SAAS;IAChB,MAAM,EAAE,SAAS;IACjB,OAAO,EAAE,SAAS;IAClB,QAAQ,EAAE,SAAS;IACnB,OAAO,EAAE,SAAS;IAClB,KAAK,EAAE,QAAQ;CAChB,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAqC;IACnE,IAAI,EAAE,MAAM;IACZ,QAAQ,EAAE,UAAU;IACpB,OAAO,EAAE,SAAS;IAClB,KAAK,EAAE,OAAO;IACd,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,SAAS;IAClB,QAAQ,EAAE,UAAU;IACpB,OAAO,EAAE,SAAS;IAClB,KAAK,EAAE,OAAO;CACf,CAAA;AAED;;;;;;GAMG;AACH,MAAM,UAAU,GAA4D;IAC1E,MAAM,EAAE,EAAE,IAAI,EAAE,mCAAmC,EAAE,GAAG,EAAE,WAAW,EAAE;IACvE,OAAO,EAAE,EAAE,IAAI,EAAE,oCAAoC,EAAE,GAAG,EAAE,YAAY,EAAE;IAC1E,OAAO,EAAE,EAAE,IAAI,EAAE,oCAAoC,EAAE,GAAG,EAAE,YAAY,EAAE;IAC1E,MAAM,EAAE,EAAE,IAAI,EAAE,mCAAmC,EAAE,GAAG,EAAE,WAAW,EAAE;IACvE,8DAA8D;IAC9D,OAAO,EAAE,EAAE,IAAI,EAAE,qBAAqB,EAAE,GAAG,EAAE,YAAY,EAAE;CAC5D,CAAA;AAED,SAAS,YAAY,CAAC,EACpB,MAAM,EACN,KAAK,EACL,SAAS,EACT,GAAG,KAAK,EAKT;IACC,MAAM,IAAI,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAA;IACvC,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,CAAA;IAE9B,OAAO,CACL,6BACY,eAAe,iBAIZ,MAAM,eACR,IAAI,EACf,SAAS,EAAE,EAAE,CACX,uIAAuI,EACvI,KAAK,CAAC,IAAI,EACV,SAAS,CACV,KACG,KAAK,aAET,8BAGc,MAAM,eACR,mBAAmB,EAC7B,SAAS,EAAE,EAAE,CACX,gCAAgC,EAChC,KAAK,CAAC,GAAG;gBACT,uEAAuE;gBACvE,uEAAuE;gBACvE,qEAAqE;gBACrE,8CAA8C;gBAC9C,MAAM,KAAK,UAAU,IAAI,2BAA2B,CACrD,GACD,EACD,KAAK,IAAI,mBAAmB,CAAC,MAAM,CAAC,IAChC,CACR,CAAA;AACH,CAAC;AAED,OAAO,EAAE,YAAY,EAAE,CAAA"}
1
+ {"version":3,"file":"entity-status.js","sourceRoot":"","sources":["../../src/blocks/entity-status.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,oBAAoB,CAAA;AAEvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAEH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,MAAM;IACN,WAAW;IACX,UAAU;IACV,SAAS;IACT,OAAO;IACP,QAAQ;IACR,SAAS;IACT,UAAU;IACV,SAAS;IACT,OAAO;CACC,CAAA;AAaV;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAA+C;IAC5E,IAAI,EAAE,QAAQ;IACd,SAAS,EAAE,WAAW;IACtB,QAAQ,EAAE,SAAS;IACnB,OAAO,EAAE,SAAS;IAClB,KAAK,EAAE,SAAS;IAChB,MAAM,EAAE,SAAS;IACjB,OAAO,EAAE,SAAS;IAClB,QAAQ,EAAE,SAAS;IACnB,OAAO,EAAE,SAAS;IAClB,KAAK,EAAE,QAAQ;CAChB,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAqC;IACnE,IAAI,EAAE,MAAM;IACZ,yEAAyE;IACzE,4EAA4E;IAC5E,yEAAyE;IACzE,kBAAkB;IAClB,SAAS,EAAE,WAAW;IACtB,QAAQ,EAAE,UAAU;IACpB,OAAO,EAAE,SAAS;IAClB,KAAK,EAAE,OAAO;IACd,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,SAAS;IAClB,QAAQ,EAAE,UAAU;IACpB,OAAO,EAAE,SAAS;IAClB,KAAK,EAAE,OAAO;CACf,CAAA;AAED;;;;;;GAMG;AACH,MAAM,UAAU,GAA4D;IAC1E,MAAM,EAAE,EAAE,IAAI,EAAE,mCAAmC,EAAE,GAAG,EAAE,WAAW,EAAE;IACvE,SAAS,EAAE,EAAE,IAAI,EAAE,sCAAsC,EAAE,GAAG,EAAE,cAAc,EAAE;IAChF,OAAO,EAAE,EAAE,IAAI,EAAE,oCAAoC,EAAE,GAAG,EAAE,YAAY,EAAE;IAC1E,OAAO,EAAE,EAAE,IAAI,EAAE,oCAAoC,EAAE,GAAG,EAAE,YAAY,EAAE;IAC1E,MAAM,EAAE,EAAE,IAAI,EAAE,mCAAmC,EAAE,GAAG,EAAE,WAAW,EAAE;IACvE,8DAA8D;IAC9D,OAAO,EAAE,EAAE,IAAI,EAAE,qBAAqB,EAAE,GAAG,EAAE,YAAY,EAAE;CAC5D,CAAA;AAED,SAAS,YAAY,CAAC,EACpB,MAAM,EACN,KAAK,EACL,SAAS,EACT,GAAG,KAAK,EAKT;IACC,MAAM,IAAI,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAA;IACvC,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,CAAA;IAE9B,OAAO,CACL,6BACY,eAAe,iBAIZ,MAAM,eACR,IAAI,EACf,SAAS,EAAE,EAAE,CACX,uIAAuI,EACvI,KAAK,CAAC,IAAI,EACV,SAAS,CACV,KACG,KAAK,aAET,8BAGc,MAAM,eACR,mBAAmB,EAC7B,SAAS,EAAE,EAAE,CACX,gCAAgC,EAChC,KAAK,CAAC,GAAG;gBACT,uEAAuE;gBACvE,uEAAuE;gBACvE,qEAAqE;gBACrE,8CAA8C;gBAC9C,MAAM,KAAK,UAAU,IAAI,2BAA2B,CACrD,GACD,EACD,KAAK,IAAI,mBAAmB,CAAC,MAAM,CAAC,IAChC,CACR,CAAA;AACH,CAAC;AAED,OAAO,EAAE,YAAY,EAAE,CAAA"}
@@ -80,11 +80,21 @@ export interface OnboardingOption {
80
80
  description?: string;
81
81
  disabled?: boolean;
82
82
  }
83
- /** Two columns is the Figma arrangement. One reads as a list, three as a picker. */
83
+ /**
84
+ * Two columns is the Figma arrangement. One reads as a list, three as a picker.
85
+ *
86
+ * Four is the install screen's tool grid (#124) — eight AI tools laid out 4x2,
87
+ * where the options are marks with a short caption rather than sentences, so
88
+ * they stay legible much narrower than a worded answer does. It steps
89
+ * 1 -> 2 -> 4 and deliberately skips 3: an eight-item set over three columns
90
+ * leaves a two-item orphan row, and the jump lands on the breakpoint where four
91
+ * tiles actually fit.
92
+ */
84
93
  declare const COLUMNS: {
85
94
  readonly 1: "grid-cols-1";
86
95
  readonly 2: "grid-cols-1 sm:grid-cols-2";
87
96
  readonly 3: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3";
97
+ readonly 4: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-4";
88
98
  };
89
99
  /** Everything both modes share. The mode-specific half is the union below. */
90
100
  type OnboardingQuestionBaseProps = Omit<React.ComponentProps<"section">, "ref" | "title" | "defaultValue" | "onChange"> & {
@@ -1 +1 @@
1
- {"version":3,"file":"onboarding-question.d.ts","sourceRoot":"","sources":["../../src/blocks/onboarding-question.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAS9B,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAA;AAGxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiEG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAA;IACb,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,oFAAoF;AACpF,QAAA,MAAM,OAAO;;;;CAIH,CAAA;AAsFV,8EAA8E;AAC9E,KAAK,2BAA2B,GAAG,IAAI,CACrC,KAAK,CAAC,cAAc,CAAC,SAAS,CAAC,EAC/B,KAAK,GAAG,OAAO,GAAG,cAAc,GAAG,UAAU,CAC9C,GAAG;IACF,KAAK,EAAE,MAAM,CAAA;IACb,2DAA2D;IAC3D,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,gBAAgB,EAAE,CAAA;IAC3B,OAAO,CAAC,EAAE,MAAM,OAAO,OAAO,CAAA;IAC9B,4EAA4E;IAC5E,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;IACb,WAAW,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAA;IAC9D,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACxB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED,KAAK,uBAAuB,GAAG,2BAA2B,GACxD,CACI;IACE,uEAAuE;IACvE,IAAI,CAAC,EAAE,QAAQ,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;CAC/C,GACD;IACE,0EAA0E;IAC1E,IAAI,EAAE,OAAO,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAA;CAC1C,CACJ,CAAA;AAEH,iBAAS,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,qBA8GzD;AAED,OAAO,EAAE,kBAAkB,EAAE,CAAA"}
1
+ {"version":3,"file":"onboarding-question.d.ts","sourceRoot":"","sources":["../../src/blocks/onboarding-question.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAS9B,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAA;AAGxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiEG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAA;IACb,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED;;;;;;;;;GASG;AACH,QAAA,MAAM,OAAO;;;;;CAKH,CAAA;AAsFV,8EAA8E;AAC9E,KAAK,2BAA2B,GAAG,IAAI,CACrC,KAAK,CAAC,cAAc,CAAC,SAAS,CAAC,EAC/B,KAAK,GAAG,OAAO,GAAG,cAAc,GAAG,UAAU,CAC9C,GAAG;IACF,KAAK,EAAE,MAAM,CAAA;IACb,2DAA2D;IAC3D,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,gBAAgB,EAAE,CAAA;IAC3B,OAAO,CAAC,EAAE,MAAM,OAAO,OAAO,CAAA;IAC9B,4EAA4E;IAC5E,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;IACb,WAAW,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAA;IAC9D,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACxB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED,KAAK,uBAAuB,GAAG,2BAA2B,GACxD,CACI;IACE,uEAAuE;IACvE,IAAI,CAAC,EAAE,QAAQ,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;CAC/C,GACD;IACE,0EAA0E;IAC1E,IAAI,EAAE,OAAO,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAA;CAC1C,CACJ,CAAA;AAEH,iBAAS,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,qBA8GzD;AAED,OAAO,EAAE,kBAAkB,EAAE,CAAA"}
@@ -10,11 +10,21 @@ import { Button } from "../ui/button.js";
10
10
  import { OptionCard } from "./option-card.js";
11
11
  import { PageHeading } from "./page-heading.js";
12
12
  import { cn } from "../lib/cloudgrid-cn.js";
13
- /** Two columns is the Figma arrangement. One reads as a list, three as a picker. */
13
+ /**
14
+ * Two columns is the Figma arrangement. One reads as a list, three as a picker.
15
+ *
16
+ * Four is the install screen's tool grid (#124) — eight AI tools laid out 4x2,
17
+ * where the options are marks with a short caption rather than sentences, so
18
+ * they stay legible much narrower than a worded answer does. It steps
19
+ * 1 -> 2 -> 4 and deliberately skips 3: an eight-item set over three columns
20
+ * leaves a two-item orphan row, and the jump lands on the breakpoint where four
21
+ * tiles actually fit.
22
+ */
14
23
  const COLUMNS = {
15
24
  1: "grid-cols-1",
16
25
  2: "grid-cols-1 sm:grid-cols-2",
17
26
  3: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3",
27
+ 4: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-4",
18
28
  };
19
29
  /**
20
30
  * The card, in both modes. Every class here is a utility over a `--cg-*` token,
@@ -1 +1 @@
1
- {"version":3,"file":"onboarding-question.js","sourceRoot":"","sources":["../../src/blocks/onboarding-question.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"onboarding-question.js","sourceRoot":"","sources":["../../src/blocks/onboarding-question.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,103 @@
1
+ import * as React from "react";
2
+ import { PageHeading } from "./page-heading.js";
3
+ /**
4
+ * Getting CloudGrid into the tool someone already works in: a tool to pick, and
5
+ * for that tool the exact thing to paste, copyable in one press.
6
+ *
7
+ * One component for two surfaces (#126). The console asks this as the last step
8
+ * of onboarding; the site serves the same thing as a public setup guide. They
9
+ * were heading for two implementations of one screen, which is how the two come
10
+ * to disagree about the same setup with nothing to catch it.
11
+ *
12
+ * ## The visual treatment here is PROVISIONAL
13
+ *
14
+ * Founder, 2026-08-25: a better component design exists and is not ready yet —
15
+ * "make this nice and we will replace the full component shortly". So this file
16
+ * is written to be *replaced*, and the effort went where replacement is cheap:
17
+ *
18
+ * - **The props are the contract.** `tools`, `value`/`defaultValue`/
19
+ * `onValueChange`, `onContinue`. A redesign changes what this renders, not
20
+ * what a consumer passes, so the console and the site keep compiling.
21
+ * - **The behaviour is Base UI's, not this file's.** Roving focus, arrow-key
22
+ * traversal and the tab/panel wiring come from `Tabs`; copy and its announced
23
+ * result come from `CodeBlock`. Those are the parts that are expensive to get
24
+ * right and silent when wrong, and a redesign inherits them for free.
25
+ * - **The paint is a handful of utilities over `--cg-*` tokens** and nothing
26
+ * more. There is no bespoke geometry to unpick.
27
+ *
28
+ * Do not build anything on the internals — the slots and the class lists are
29
+ * the disposable half.
30
+ *
31
+ * ## Presentation only
32
+ *
33
+ * The line #117 and #121 drew, held again. This component carries no CloudGrid
34
+ * prompt, no MCP command, no tool list, and no idea what comes next. It renders
35
+ * the tools it is handed and reports which is selected. That is exactly what
36
+ * lets one component serve onboarding and a public guide: the strings differ,
37
+ * the screen does not.
38
+ *
39
+ * ## Why tabs and not the picker beside it
40
+ *
41
+ * `OnboardingQuestion` asks a question and reports an answer. This does not ask
42
+ * anything — every tool is a valid thing to look at, looking is free, and
43
+ * nothing is being recorded by looking. A radio group would tell assistive tech
44
+ * that an answer had been given when the user was reading. Tabs say "same
45
+ * screen, different content", which is what this is.
46
+ */
47
+ export interface ToolSetupTool {
48
+ /** What `onValueChange` reports. Stable, and never shown. */
49
+ value: string;
50
+ /** The words on the tab. Two short words wrap; they are not truncated. */
51
+ label: string;
52
+ /**
53
+ * The lead-in over the prompt — "Copy and send this to Claude Code". Written
54
+ * per tool because the sentence names the tool, and a generic one reads as
55
+ * boilerplate at the exact moment the user needs to know where the text goes.
56
+ */
57
+ instruction?: string;
58
+ /** The text to paste. Multi-line is normal here; `CodeBlock` preserves it. */
59
+ prompt: string;
60
+ /** Shown beside the instruction. For a tool with a manual path as well. */
61
+ alternative?: {
62
+ label: string;
63
+ href: string;
64
+ };
65
+ disabled?: boolean;
66
+ }
67
+ declare function ToolSetup({ tools, value, defaultValue, onValueChange, title, sub, eyebrow, level, headingSize, continueLabel, onContinue, canContinue, footer, className, ...props }: Omit<React.ComponentProps<"section">, "ref" | "title" | "defaultValue" | "onChange"> & {
68
+ tools: ToolSetupTool[];
69
+ value?: string;
70
+ defaultValue?: string;
71
+ onValueChange?: (value: string) => void;
72
+ /** Omitted, no heading renders — the site puts this under its own page title. */
73
+ title?: string;
74
+ sub?: string;
75
+ eyebrow?: string;
76
+ /** Forwarded to `PageHeading`. `h2` for a section inside a larger page. */
77
+ level?: 1 | 2;
78
+ headingSize?: React.ComponentProps<typeof PageHeading>["size"];
79
+ /**
80
+ * `null` renders no button at all. The site's copy of this screen ends at the
81
+ * prompt — there is nothing to continue to, and a dead button saying
82
+ * otherwise is worse than no button.
83
+ *
84
+ * The absence is keyed on the LABEL and not on `onContinue` deliberately. A
85
+ * handler is a function, and the gallery that reviews this component is a
86
+ * server module by design, so gating the button on one made the button
87
+ * unrenderable in the only place a designer sees it — caught by "Event
88
+ * handlers cannot be passed to Client Component props" at prerender. A
89
+ * declarative opt-out works from both.
90
+ */
91
+ continueLabel?: string | null;
92
+ onContinue?: () => void;
93
+ /**
94
+ * Defaults to `true`, unlike `OnboardingQuestion`'s. Nothing here is an answer
95
+ * — the user goes away to their tool and comes back, so gating the button on a
96
+ * selection would strand anyone who set up from the tab that was already open.
97
+ */
98
+ canContinue?: boolean;
99
+ /** Under the button. The console puts "I will do this later" here. */
100
+ footer?: React.ReactNode;
101
+ }): React.JSX.Element;
102
+ export { ToolSetup };
103
+ //# sourceMappingURL=tool-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-setup.d.ts","sourceRoot":"","sources":["../../src/blocks/tool-setup.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAA;AAIxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,MAAM,WAAW,aAAa;IAC5B,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAA;IACb,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAA;IACb;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,8EAA8E;IAC9E,MAAM,EAAE,MAAM,CAAA;IACd,2EAA2E;IAC3E,WAAW,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IAC7C,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAsBD,iBAAS,SAAS,CAAC,EACjB,KAAK,EACL,KAAK,EACL,YAAY,EACZ,aAAa,EACb,KAAK,EACL,GAAG,EACH,OAAO,EACP,KAAS,EACT,WAAkB,EAClB,aAA0B,EAC1B,UAAU,EACV,WAAkB,EAClB,MAAM,EACN,SAAS,EACT,GAAG,KAAK,EACT,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,SAAS,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,cAAc,GAAG,UAAU,CAAC,GAAG;IACxF,KAAK,EAAE,aAAa,EAAE,CAAA;IACtB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACvC,iFAAiF;IACjF,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,2EAA2E;IAC3E,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;IACb,WAAW,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAA;IAC9D;;;;;;;;;;;OAWG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,sEAAsE;IACtE,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CACzB,qBA4HA;AAED,OAAO,EAAE,SAAS,EAAE,CAAA"}
@@ -0,0 +1,61 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Button } from "../ui/button.js";
5
+ import { CodeBlock } from "../ui/code-block.js";
6
+ import { PageHeading } from "./page-heading.js";
7
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs.js";
8
+ import { cn } from "../lib/cloudgrid-cn.js";
9
+ /**
10
+ * The tab. Sized for two lines because the real labels are "Claude Desktop" and
11
+ * "ChatGPT Desktop", and a strip that truncates them makes two tools read alike.
12
+ *
13
+ * The overrides earn their place: `TabsList` pins itself to `h-8` for a compact
14
+ * strip, and `TabsTrigger` sets `flex-1` and `whitespace-nowrap`. Eight tools at
15
+ * a fixed height with nowrap is a horizontal scrollbar on a laptop, so height
16
+ * goes auto and the list wraps to a second row instead.
17
+ */
18
+ const TRIGGER_CLASS = cn("h-auto min-h-[3.75rem] flex-none basis-[7.5rem] grow flex-col gap-0.5 whitespace-normal", "rounded-lg px-3 py-2 text-center text-body-sm leading-tight",
19
+ // The dark selected pill is `bg-code`/`text-on-code` — the library's OWN
20
+ // ink-surface pair, the one AgentPrompt and CodeBlock already paint with. A
21
+ // first pass invented `bg-ink-strong`/`text-on-ink`, which are not tokens:
22
+ // `--color-ink` is a TEXT colour and the other two do not exist, so Tailwind
23
+ // emitted nothing and the selected tab rendered identically to the rest.
24
+ "data-active:bg-code data-active:text-on-code");
25
+ function ToolSetup({ tools, value, defaultValue, onValueChange, title, sub, eyebrow, level = 1, headingSize = "md", continueLabel = "Continue", onContinue, canContinue = true, footer, className, ...props }) {
26
+ // Uncontrolled falls back to the first tool rather than nothing: a tab strip
27
+ // with no panel under it reads as broken, and there is no "unselected" state
28
+ // this screen means.
29
+ const first = tools[0]?.value;
30
+ const headingId = React.useId();
31
+ return (_jsxs("section", { "data-slot": "tool-setup", className: cn("flex flex-col gap-8", className), ...props, children: [title ? (_jsx(PageHeading, { id: headingId, eyebrow: eyebrow, title: title, sub: sub, size: headingSize, level: level })) : null, _jsxs(Tabs, { value: value, defaultValue: defaultValue ?? first, onValueChange: (next) => onValueChange?.(String(next)), className: "flex flex-col gap-4", children: [_jsx(TabsList
32
+ // Labelled by the lockup when there is one, so the strip is announced
33
+ // as the setup's tool list rather than an unnamed set of tabs.
34
+ , { ...(title ? { "aria-labelledby": headingId } : {}), "data-slot": "tool-setup-tabs",
35
+ // `h-auto!` — important, and that is load-bearing.
36
+ //
37
+ // `tabsListVariants` pins the strip with `group-data-horizontal/tabs:h-8`
38
+ // for a compact single row. Eight tools do not fit one row, so the list
39
+ // must grow — and a bare `h-auto` does not make it: both utilities
40
+ // survive `cn()` because they carry different variant prefixes, and the
41
+ // prefixed rule wins the cascade. Measured, not assumed:
42
+ // `getComputedStyle` reported a 32px list around a 60px tab.
43
+ //
44
+ // The consequence was invisible. The eighth tool wrapped into height
45
+ // that was never reserved and the panel painted straight over it, while
46
+ // every other assertion passed — the tab was present, focusable,
47
+ // correctly announced and correctly painted. It was simply underneath.
48
+ //
49
+ // Important settles it without depending on which utility `cn()` keeps
50
+ // or what order Tailwind emits them in.
51
+ className: "h-auto! w-full flex-wrap justify-start gap-2 bg-transparent p-0", children: tools.map((tool) => (_jsx(TabsTrigger, { value: tool.value, disabled: tool.disabled, "data-slot": "tool-setup-tab", className: TRIGGER_CLASS, children: tool.label }, tool.value))) }), tools.map((tool) => (_jsx(TabsContent, { value: tool.value, "data-slot": "tool-setup-panel", className: "rounded-lg border border-hairline bg-card p-5", children: _jsxs("div", { className: "flex flex-col gap-4", children: [tool.instruction || tool.alternative ? (_jsxs("div", { className: "flex flex-wrap items-baseline justify-between gap-x-4 gap-y-1", children: [tool.instruction ? (_jsx("p", { className: "m-0 text-body-sm text-body", children: tool.instruction })) : (_jsx("span", {})), tool.alternative ? (_jsx("a", { href: tool.alternative.href, "data-slot": "tool-setup-alternative",
52
+ // `--cg-link` is a token but NOT a generated utility —
53
+ // there is no `--color-link`, so `text-link` emits
54
+ // nothing and the link paints as body text. The arbitrary
55
+ // form is how the library reaches an ungenerated token,
56
+ // the same way onboarding-question writes
57
+ // `ring-(--cg-border-strong)`.
58
+ className: "text-body-sm text-(--cg-link) underline-offset-4 hover:text-(--cg-link-hover) hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring", children: tool.alternative.label })) : null] })) : null, _jsx(CodeBlock, { code: tool.prompt })] }) }, tool.value)))] }), continueLabel !== null || footer ? (_jsxs("div", { "data-slot": "tool-setup-actions", className: "flex flex-col items-start gap-4", children: [continueLabel !== null ? (_jsx(Button, { type: "button", size: "lg", "data-slot": "tool-setup-continue", onClick: onContinue, disabled: !canContinue, children: continueLabel })) : null, footer] })) : null] }));
59
+ }
60
+ export { ToolSetup };
61
+ //# sourceMappingURL=tool-setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-setup.js","sourceRoot":"","sources":["../../src/blocks/tool-setup.tsx"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -44,6 +44,7 @@ export * from "./blocks/rich-text.js";
44
44
  export * from "./blocks/stage-panel.js";
45
45
  export * from "./blocks/step-item.js";
46
46
  export * from "./blocks/tool-logo.js";
47
+ export * from "./blocks/tool-setup.js";
47
48
  export * from "./blocks/tool-marks.js";
48
49
  export * from "./lib/cloudgrid-cn.js";
49
50
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAiBA,cAAc,mBAAmB,CAAA;AACjC,cAAc,eAAe,CAAA;AAC7B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,cAAc,CAAA;AAC5B,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,oBAAoB,CAAA;AAClC,cAAc,kBAAkB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AACnC,cAAc,eAAe,CAAA;AAC7B,cAAc,sBAAsB,CAAA;AACpC,cAAc,eAAe,CAAA;AAC7B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,mBAAmB,CAAA;AACjC,cAAc,kBAAkB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,eAAe,CAAA;AAC7B,cAAc,cAAc,CAAA;AAC5B,cAAc,kBAAkB,CAAA;AAChC,cAAc,iBAAiB,CAAA;AAG/B,cAAc,0BAA0B,CAAA;AACxC,cAAc,uBAAuB,CAAA;AACrC,cAAc,2BAA2B,CAAA;AACzC,cAAc,2BAA2B,CAAA;AACzC,cAAc,wBAAwB,CAAA;AACtC,cAAc,uBAAuB,CAAA;AACrC,cAAc,yBAAyB,CAAA;AACvC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,2BAA2B,CAAA;AACzC,cAAc,2BAA2B,CAAA;AACzC,cAAc,uBAAuB,CAAA;AACrC,cAAc,kBAAkB,CAAA;AAChC,cAAc,iCAAiC,CAAA;AAC/C,cAAc,yBAAyB,CAAA;AACvC,cAAc,0BAA0B,CAAA;AACxC,cAAc,yBAAyB,CAAA;AACvC,cAAc,gCAAgC,CAAA;AAC9C,cAAc,2BAA2B,CAAA;AACzC,cAAc,mBAAmB,CAAA;AACjC,cAAc,uBAAuB,CAAA;AACrC,cAAc,yBAAyB,CAAA;AACvC,cAAc,uBAAuB,CAAA;AACrC,cAAc,uBAAuB,CAAA;AACrC,cAAc,wBAAwB,CAAA;AAGtC,cAAc,uBAAuB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAiBA,cAAc,mBAAmB,CAAA;AACjC,cAAc,eAAe,CAAA;AAC7B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,cAAc,CAAA;AAC5B,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,oBAAoB,CAAA;AAClC,cAAc,kBAAkB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AACnC,cAAc,eAAe,CAAA;AAC7B,cAAc,sBAAsB,CAAA;AACpC,cAAc,eAAe,CAAA;AAC7B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,mBAAmB,CAAA;AACjC,cAAc,kBAAkB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,eAAe,CAAA;AAC7B,cAAc,cAAc,CAAA;AAC5B,cAAc,kBAAkB,CAAA;AAChC,cAAc,iBAAiB,CAAA;AAG/B,cAAc,0BAA0B,CAAA;AACxC,cAAc,uBAAuB,CAAA;AACrC,cAAc,2BAA2B,CAAA;AACzC,cAAc,2BAA2B,CAAA;AACzC,cAAc,wBAAwB,CAAA;AACtC,cAAc,uBAAuB,CAAA;AACrC,cAAc,yBAAyB,CAAA;AACvC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,2BAA2B,CAAA;AACzC,cAAc,2BAA2B,CAAA;AACzC,cAAc,uBAAuB,CAAA;AACrC,cAAc,kBAAkB,CAAA;AAChC,cAAc,iCAAiC,CAAA;AAC/C,cAAc,yBAAyB,CAAA;AACvC,cAAc,0BAA0B,CAAA;AACxC,cAAc,yBAAyB,CAAA;AACvC,cAAc,gCAAgC,CAAA;AAC9C,cAAc,2BAA2B,CAAA;AACzC,cAAc,mBAAmB,CAAA;AACjC,cAAc,uBAAuB,CAAA;AACrC,cAAc,yBAAyB,CAAA;AACvC,cAAc,uBAAuB,CAAA;AACrC,cAAc,uBAAuB,CAAA;AACrC,cAAc,wBAAwB,CAAA;AACtC,cAAc,wBAAwB,CAAA;AAGtC,cAAc,uBAAuB,CAAA"}
package/dist/index.js CHANGED
@@ -61,6 +61,7 @@ export * from "./blocks/rich-text.js";
61
61
  export * from "./blocks/stage-panel.js";
62
62
  export * from "./blocks/step-item.js";
63
63
  export * from "./blocks/tool-logo.js";
64
+ export * from "./blocks/tool-setup.js";
64
65
  export * from "./blocks/tool-marks.js";
65
66
  // The class-merge helper, which the components use and consumers may too.
66
67
  export * from "./lib/cloudgrid-cn.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,EAAE;AACF,gFAAgF;AAChF,8EAA8E;AAC9E,oEAAoE;AACpE,EAAE;AACF,gFAAgF;AAChF,+EAA+E;AAC/E,yEAAyE;AACzE,gFAAgF;AAChF,4EAA4E;AAC5E,EAAE;AACF,kEAAkE;AAClE,8EAA8E;AAC9E,uEAAuE;AAEvE,cAAc;AACd,cAAc,mBAAmB,CAAA;AACjC,cAAc,eAAe,CAAA;AAC7B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,cAAc,CAAA;AAC5B,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,oBAAoB,CAAA;AAClC,cAAc,kBAAkB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AACnC,cAAc,eAAe,CAAA;AAC7B,cAAc,sBAAsB,CAAA;AACpC,cAAc,eAAe,CAAA;AAC7B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,mBAAmB,CAAA;AACjC,cAAc,kBAAkB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,eAAe,CAAA;AAC7B,cAAc,cAAc,CAAA;AAC5B,cAAc,kBAAkB,CAAA;AAChC,cAAc,iBAAiB,CAAA;AAE/B,mBAAmB;AACnB,cAAc,0BAA0B,CAAA;AACxC,cAAc,uBAAuB,CAAA;AACrC,cAAc,2BAA2B,CAAA;AACzC,cAAc,2BAA2B,CAAA;AACzC,cAAc,wBAAwB,CAAA;AACtC,cAAc,uBAAuB,CAAA;AACrC,cAAc,yBAAyB,CAAA;AACvC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,2BAA2B,CAAA;AACzC,cAAc,2BAA2B,CAAA;AACzC,cAAc,uBAAuB,CAAA;AACrC,cAAc,kBAAkB,CAAA;AAChC,cAAc,iCAAiC,CAAA;AAC/C,cAAc,yBAAyB,CAAA;AACvC,cAAc,0BAA0B,CAAA;AACxC,cAAc,yBAAyB,CAAA;AACvC,cAAc,gCAAgC,CAAA;AAC9C,cAAc,2BAA2B,CAAA;AACzC,cAAc,mBAAmB,CAAA;AACjC,cAAc,uBAAuB,CAAA;AACrC,cAAc,yBAAyB,CAAA;AACvC,cAAc,uBAAuB,CAAA;AACrC,cAAc,uBAAuB,CAAA;AACrC,cAAc,wBAAwB,CAAA;AAEtC,0EAA0E;AAC1E,cAAc,uBAAuB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,EAAE;AACF,gFAAgF;AAChF,8EAA8E;AAC9E,oEAAoE;AACpE,EAAE;AACF,gFAAgF;AAChF,+EAA+E;AAC/E,yEAAyE;AACzE,gFAAgF;AAChF,4EAA4E;AAC5E,EAAE;AACF,kEAAkE;AAClE,8EAA8E;AAC9E,uEAAuE;AAEvE,cAAc;AACd,cAAc,mBAAmB,CAAA;AACjC,cAAc,eAAe,CAAA;AAC7B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,cAAc,CAAA;AAC5B,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,oBAAoB,CAAA;AAClC,cAAc,kBAAkB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AACnC,cAAc,eAAe,CAAA;AAC7B,cAAc,sBAAsB,CAAA;AACpC,cAAc,eAAe,CAAA;AAC7B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,mBAAmB,CAAA;AACjC,cAAc,kBAAkB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,eAAe,CAAA;AAC7B,cAAc,cAAc,CAAA;AAC5B,cAAc,kBAAkB,CAAA;AAChC,cAAc,iBAAiB,CAAA;AAE/B,mBAAmB;AACnB,cAAc,0BAA0B,CAAA;AACxC,cAAc,uBAAuB,CAAA;AACrC,cAAc,2BAA2B,CAAA;AACzC,cAAc,2BAA2B,CAAA;AACzC,cAAc,wBAAwB,CAAA;AACtC,cAAc,uBAAuB,CAAA;AACrC,cAAc,yBAAyB,CAAA;AACvC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,2BAA2B,CAAA;AACzC,cAAc,2BAA2B,CAAA;AACzC,cAAc,uBAAuB,CAAA;AACrC,cAAc,kBAAkB,CAAA;AAChC,cAAc,iCAAiC,CAAA;AAC/C,cAAc,yBAAyB,CAAA;AACvC,cAAc,0BAA0B,CAAA;AACxC,cAAc,yBAAyB,CAAA;AACvC,cAAc,gCAAgC,CAAA;AAC9C,cAAc,2BAA2B,CAAA;AACzC,cAAc,mBAAmB,CAAA;AACjC,cAAc,uBAAuB,CAAA;AACrC,cAAc,yBAAyB,CAAA;AACvC,cAAc,uBAAuB,CAAA;AACrC,cAAc,uBAAuB,CAAA;AACrC,cAAc,wBAAwB,CAAA;AACtC,cAAc,wBAAwB,CAAA;AAEtC,0EAA0E;AAC1E,cAAc,uBAAuB,CAAA"}
package/dist/theme.css CHANGED
@@ -98,7 +98,8 @@
98
98
  --color-danger: var(--cg-danger);
99
99
 
100
100
  /* ---- state ----
101
- What an entity IS: online, offline, pending, expired, and danger above.
101
+ What an entity IS: online, offline, pending, unhealthy, expired, and danger
102
+ above.
102
103
 
103
104
  #111 added the tokens and stopped there, so for one release the state
104
105
  palette was reachable only as raw var(--cg-online). That is the shape #30
@@ -117,12 +118,21 @@
117
118
  #111's note explains why, and it is a design decision rather than an
118
119
  omission: the muted neutral on a neutral tint measures 2.90:1, under the
119
120
  3:1 bar, so an unplugged chip uses the card ground and border-control
120
- instead. A utility here would have to invent the value it maps. */
121
+ instead. A utility here would have to invent the value it maps.
122
+
123
+ unhealthy (#122) arrives with its token rather than a release later, which
124
+ is the whole point of the guard that walks this block. #113 shipped the
125
+ `indicator` group with no bridge at all and nothing went red, because that
126
+ guard walks the `state` group and only that one. A token whose utility
127
+ lands in a later version is a token the first consumer reaches for and
128
+ does not find. */
121
129
  --color-online: var(--cg-online);
122
130
  --color-online-fill: var(--cg-online-fill);
123
131
  --color-offline: var(--cg-offline);
124
132
  --color-pending: var(--cg-pending);
125
133
  --color-pending-fill: var(--cg-pending-fill);
134
+ --color-unhealthy: var(--cg-unhealthy);
135
+ --color-unhealthy-fill: var(--cg-unhealthy-fill);
126
136
  --color-expired: var(--cg-expired);
127
137
  --color-expired-fill: var(--cg-expired-fill);
128
138
  --color-danger-fill: var(--cg-danger-fill);
@@ -52,7 +52,7 @@
52
52
  }
53
53
  },
54
54
  "state": {
55
- "$description": "state: what an entity IS, not what it means editorially.\n\nAdopted from the Console rather than invented, 2026-08-24 (#96). The Console\nhad already chosen and contrast-tested these — --color-live and\n--color-attention — while the brand carried no state colour at all beyond the\ndanger placeholder. Taking its values means the Console's appearance does not\nchange when it starts consuming this package, and the two stop disagreeing\nabout what 'live' looks like.\n\nEach carries per-surface values in surface-panel and surface-ink, for the same\nreason danger does: measured against all three grounds, no single value works.\nThe live green is 1.72:1 on the purple panel, and the brand's own lime is\n1.54:1 on white. A flat value would be invisible somewhere.\n\nNo --cg-offline-fill, deliberately. Measured, the pair does not work: the\nmuted neutral on a neutral tint is 2.90:1, under the 3:1 bar for a dot or a\nborder. Rather than invent a value to satisfy the ratio, an unplugged chip\nuses the existing surface-card ground with border-control, and --cg-offline\nis the dot on that ground, where it measures 3.28:1. Absence of state should\nnot need a colour of its own.\n\npending joined them on 2026-08-24 (#114) as an ALIAS of expired rather than a\nnew value. Same amber today, separate name, so the two can diverge later with\na one-line edit here and no change in any consumer. Aliases compile to var(),\nso the flip survives.",
55
+ "$description": "state: what an entity IS, not what it means editorially.\n\nAdopted from the Console rather than invented, 2026-08-24 (#96). The Console\nhad already chosen and contrast-tested these — --color-live and\n--color-attention — while the brand carried no state colour at all beyond the\ndanger placeholder. Taking its values means the Console's appearance does not\nchange when it starts consuming this package, and the two stop disagreeing\nabout what 'live' looks like.\n\nEach carries per-surface values in surface-panel and surface-ink, for the same\nreason danger does: measured against all three grounds, no single value works.\nThe live green is 1.72:1 on the purple panel, and the brand's own lime is\n1.54:1 on white. A flat value would be invisible somewhere.\n\nNo --cg-offline-fill, deliberately. Measured, the pair does not work: the\nmuted neutral on a neutral tint is 2.90:1, under the 3:1 bar for a dot or a\nborder. Rather than invent a value to satisfy the ratio, an unplugged chip\nuses the existing surface-card ground with border-control, and --cg-offline\nis the dot on that ground, where it measures 3.28:1. Absence of state should\nnot need a colour of its own.\n\npending joined them on 2026-08-24 (#114) as an ALIAS of expired rather than a\nnew value. Same amber today, separate name, so the two can diverge later with\na one-line edit here and no change in any consumer. Aliases compile to var(),\nso the flip survives.\n\nunhealthy joined them on 2026-08-25 (#122) as a REAL value, not an alias, and\nit is the one tone here that was searched for rather than adopted. Founder\ndecision: expired is terminal, unhealthy is live-and-fixable, and sharing a\ntoken would mean retuning one silently retunes the other. It is a warmer,\nbrighter gold on the same ramp as the amber rather than a new hue family,\nbecause green/amber/red is the traffic light an operator already reads and\n'serving but degraded' is its middle rung. Its own note carries the numbers\nand the three families that were measured and rejected.",
56
56
  "online": {
57
57
  "$value": "#00a05c",
58
58
  "$type": "color",
@@ -91,6 +91,16 @@
91
91
  }
92
92
  }
93
93
  },
94
+ "unhealthy": {
95
+ "$value": "#7F7301",
96
+ "$type": "color",
97
+ "$description": "Serving, and degraded. The entity is up and answering; its health checks are not. 4.82:1 on white and 4.46:1 on its own fill — an indicator, like every tone here, never body text.\n\nA REAL value rather than an alias, which is the whole decision. Founder, 2026-08-25, issue #122: expired is terminal, unhealthy is live-and-fixable, and one token for both would mean a designer retuning one silently retunes the other. That is the same reasoning that gave pending its own NAME in #114 — this one also needs its own VALUE, because it must not read as either of the states it sits between.\n\nWHY A GOLD, measured rather than asserted. Green/amber/red is the traffic light an operator already reads, and this palette's amber is spent on two states that are both benign: pending is transitional, expired is terminal. Unhealthy is the warning rung, so it takes the warmer, brighter, more saturated seat on the same ramp rather than a different hue family.\n\nThe alternatives were searched, not guessed, over every hue at 5-degree steps against all four existing tones on all three surfaces:\n\n magenta h339 13.7 from danger on the panel, where danger is a pale pink.\n The one confusion that matters most, and a new palette worst.\n azure h235 the best normal-vision numbers of anything measured (21.5),\n and 1.2 from panel offline for a deuteranope — a degraded\n entity would be indistinguishable from a stopped one.\n chartreuse 10-14 from --cg-secondary and --cg-volt. It IS the brand lime,\n and #114 already ruled a lime cannot signal state.\n\ndE2000 on white: online 28.4, offline 26.6, expired/pending 22.3, danger 43.1. The tightest pair is expired, as expected for the neighbouring rung, and 22.3 is wider than the tightest pair this palette already ships on white (19.4, expired/danger).\n\nKNOWN, and recorded rather than tuned away: on ink the pair is 18.3, which is a new worst for that surface (was 25.3). It stays above the palette's global floor of 16.5, and buying more would mean walking into --cg-secondary (16.5 away) or --cg-volt (17.8). A light amber and a light gold have the least room on a dark ground; that is the cost of putting them on one ramp deliberately.",
98
+ "$extensions": {
99
+ "io.cloudgrid": {
100
+ "css": "--cg-unhealthy"
101
+ }
102
+ }
103
+ },
94
104
  "online-fill": {
95
105
  "$value": "#e6f7ef",
96
106
  "$type": "color",
@@ -120,6 +130,16 @@
120
130
  }
121
131
  }
122
132
  },
133
+ "unhealthy-fill": {
134
+ "$value": "#fdf6df",
135
+ "$type": "color",
136
+ "$description": "The chip ground for unhealthy. --cg-unhealthy measures 4.46:1 on it.\n\nA pale tint of its own foreground, in the same family as online-fill and expired-fill, so the five chips read as one set rather than as four tints and an exception. It is deliberately NOT expired-fill: a shared ground would put the two warm states on the same card and undo half of what the separate foreground buys.",
137
+ "$extensions": {
138
+ "io.cloudgrid": {
139
+ "css": "--cg-unhealthy-fill"
140
+ }
141
+ }
142
+ },
123
143
  "danger-fill": {
124
144
  "$value": "#fceded",
125
145
  "$type": "color",
@@ -134,7 +134,7 @@
134
134
  }
135
135
  },
136
136
  "state": {
137
- "$description": "state: the per-surface values for online/offline/expired. Same necessity as danger — measured, not assumed. pending is an alias of expired (#114), declared here so it flips rather than freezing to the base scope.",
137
+ "$description": "state: the per-surface values for online/offline/expired. Same necessity as danger — measured, not assumed. pending is an alias of expired (#114), declared here so it flips rather than freezing to the base scope. unhealthy (#122) is a real value rather than an alias, and needs the same per-surface treatment for the same reason every other tone here does: its base gold is unreadable on this ground.",
138
138
  "online": {
139
139
  "$value": "#5FD99A",
140
140
  "$type": "color",
@@ -174,6 +174,16 @@
174
174
  }
175
175
  }
176
176
  },
177
+ "unhealthy": {
178
+ "$value": "#F1DC03",
179
+ "$type": "color",
180
+ "$description": "11.35:1 on #222, 8.88:1 on its own fill. Ink has headroom, so the gold stays a gold instead of washing to cream.\n\nThis is the tightest the tone gets anywhere: 18.3 from expired, which is a new worst pair for this surface (was 25.3, offline/danger) while staying above the palette's global floor of 16.5. Recorded rather than tuned away — a light amber and a light gold have the least room on a dark ground, and the next 2 points of separation would walk into --cg-secondary (16.5 away) or --cg-volt (17.8). The pair that matters most is unaffected: 31.3 from online.",
181
+ "$extensions": {
182
+ "io.cloudgrid": {
183
+ "css": "--cg-unhealthy"
184
+ }
185
+ }
186
+ },
177
187
  "online-fill": {
178
188
  "$value": "rgba(255, 255, 255, 0.08)",
179
189
  "$type": "color",
@@ -203,6 +213,16 @@
203
213
  }
204
214
  }
205
215
  },
216
+ "unhealthy-fill": {
217
+ "$value": "rgba(255, 255, 255, 0.08)",
218
+ "$type": "color",
219
+ "$description": "Translucent white; the ink foreground measures 11.42:1 on it.",
220
+ "$extensions": {
221
+ "io.cloudgrid": {
222
+ "css": "--cg-unhealthy-fill"
223
+ }
224
+ }
225
+ },
206
226
  "danger-fill": {
207
227
  "$value": "rgba(255, 255, 255, 0.08)",
208
228
  "$type": "color",
@@ -118,7 +118,7 @@
118
118
  }
119
119
  },
120
120
  "state": {
121
- "$description": "state: the per-surface values for online/offline/expired. Same necessity as danger — measured, not assumed. pending is an alias of expired (#114), declared here so it flips rather than freezing to the base scope.",
121
+ "$description": "state: the per-surface values for online/offline/expired. Same necessity as danger — measured, not assumed. pending is an alias of expired (#114), declared here so it flips rather than freezing to the base scope. unhealthy (#122) is a real value rather than an alias, and needs the same per-surface treatment for the same reason every other tone here does: its base gold is unreadable on this ground.",
122
122
  "online": {
123
123
  "$value": "#C8F5DC",
124
124
  "$type": "color",
@@ -158,6 +158,16 @@
158
158
  }
159
159
  }
160
160
  },
161
+ "unhealthy": {
162
+ "$value": "#F7E20C",
163
+ "$type": "color",
164
+ "$description": "4.41:1 on the purple, 3.27:1 on its own fill. The source gold is 1.09:1 on this ground — invisible, same class as the danger override.\n\nThis is the surface that DECIDED the value. A dot on a panel chip has to clear 3:1 against rgba(255,255,255,0.16) over #5a47f0, whose relative luminance is 0.192; that forces the dot's own luminance above 0.677, i.e. Lab L >= 86, which is why every state value here is a near-white tint. Four already occupy that sliver at hues 29, 81, 159 and 297, and a fifth has to fit between them. The gold sits at hue 97, 20.0 from expired and 29.5 from online.\n\nIt is louder than its four siblings, deliberately: a warning that recedes is not doing its job, and chroma is the only axis left once lightness is pinned by the contrast floor.",
165
+ "$extensions": {
166
+ "io.cloudgrid": {
167
+ "css": "--cg-unhealthy"
168
+ }
169
+ }
170
+ },
161
171
  "online-fill": {
162
172
  "$value": "rgba(255, 255, 255, 0.16)",
163
173
  "$type": "color",
@@ -187,6 +197,16 @@
187
197
  }
188
198
  }
189
199
  },
200
+ "unhealthy-fill": {
201
+ "$value": "rgba(255, 255, 255, 0.16)",
202
+ "$type": "color",
203
+ "$description": "Translucent white rather than a tint, like every other chip ground here; the panel foreground measures 4.33:1 on it. Written out rather than aliased to online-fill for the same reason the others are: four tokens that happen to share a value are not one token.",
204
+ "$extensions": {
205
+ "io.cloudgrid": {
206
+ "css": "--cg-unhealthy-fill"
207
+ }
208
+ }
209
+ },
190
210
  "danger-fill": {
191
211
  "$value": "rgba(255, 255, 255, 0.16)",
192
212
  "$type": "color",
package/dist/tokens.css CHANGED
@@ -98,7 +98,16 @@
98
98
  pending joined them on 2026-08-24 (#114) as an ALIAS of expired rather than a
99
99
  new value. Same amber today, separate name, so the two can diverge later with
100
100
  a one-line edit here and no change in any consumer. Aliases compile to var(),
101
- so the flip survives. */
101
+ so the flip survives.
102
+
103
+ unhealthy joined them on 2026-08-25 (#122) as a REAL value, not an alias, and
104
+ it is the one tone here that was searched for rather than adopted. Founder
105
+ decision: expired is terminal, unhealthy is live-and-fixable, and sharing a
106
+ token would mean retuning one silently retunes the other. It is a warmer,
107
+ brighter gold on the same ramp as the amber rather than a new hue family,
108
+ because green/amber/red is the traffic light an operator already reads and
109
+ 'serving but degraded' is its middle rung. Its own note carries the numbers
110
+ and the three families that were measured and rejected. */
102
111
  /*
103
112
  Serving. From the Console's --color-live. 3.40:1 on white — clears the 3:1 bar for an indicator, which is all this is ever used as. Never body text on white. */
104
113
  --cg-online: #00a05c;
@@ -115,6 +124,27 @@
115
124
 
116
125
  Founder decision 2026-08-24, issue #114: "building is like something in progress, something in between amber and yellow and orange." */
117
126
  --cg-pending: var(--cg-expired);
127
+ /*
128
+ Serving, and degraded. The entity is up and answering; its health checks are not. 4.82:1 on white and 4.46:1 on its own fill — an indicator, like every tone here, never body text.
129
+
130
+ A REAL value rather than an alias, which is the whole decision. Founder, 2026-08-25, issue #122: expired is terminal, unhealthy is live-and-fixable, and one token for both would mean a designer retuning one silently retunes the other. That is the same reasoning that gave pending its own NAME in #114 — this one also needs its own VALUE, because it must not read as either of the states it sits between.
131
+
132
+ WHY A GOLD, measured rather than asserted. Green/amber/red is the traffic light an operator already reads, and this palette's amber is spent on two states that are both benign: pending is transitional, expired is terminal. Unhealthy is the warning rung, so it takes the warmer, brighter, more saturated seat on the same ramp rather than a different hue family.
133
+
134
+ The alternatives were searched, not guessed, over every hue at 5-degree steps against all four existing tones on all three surfaces:
135
+
136
+ magenta h339 13.7 from danger on the panel, where danger is a pale pink.
137
+ The one confusion that matters most, and a new palette worst.
138
+ azure h235 the best normal-vision numbers of anything measured (21.5),
139
+ and 1.2 from panel offline for a deuteranope — a degraded
140
+ entity would be indistinguishable from a stopped one.
141
+ chartreuse 10-14 from --cg-secondary and --cg-volt. It IS the brand lime,
142
+ and #114 already ruled a lime cannot signal state.
143
+
144
+ dE2000 on white: online 28.4, offline 26.6, expired/pending 22.3, danger 43.1. The tightest pair is expired, as expected for the neighbouring rung, and 22.3 is wider than the tightest pair this palette already ships on white (19.4, expired/danger).
145
+
146
+ KNOWN, and recorded rather than tuned away: on ink the pair is 18.3, which is a new worst for that surface (was 25.3). It stays above the palette's global floor of 16.5, and buying more would mean walking into --cg-secondary (16.5 away) or --cg-volt (17.8). A light amber and a light gold have the least room on a dark ground; that is the cost of putting them on one ramp deliberately. */
147
+ --cg-unhealthy: #7F7301;
118
148
  /*
119
149
  From the Console's --color-live-fill. --cg-online measures 3.06:1 on it. */
120
150
  --cg-online-fill: #e6f7ef;
@@ -124,6 +154,11 @@
124
154
  /*
125
155
  The chip ground for pending, aliased to expired's for the same reason the foreground is. Splitting pending from expired later means editing these two entries and nothing else. */
126
156
  --cg-pending-fill: var(--cg-expired-fill);
157
+ /*
158
+ The chip ground for unhealthy. --cg-unhealthy measures 4.46:1 on it.
159
+
160
+ A pale tint of its own foreground, in the same family as online-fill and expired-fill, so the five chips read as one set rather than as four tints and an exception. It is deliberately NOT expired-fill: a shared ground would put the two warm states on the same card and undo half of what the separate foreground buys. */
161
+ --cg-unhealthy-fill: #fdf6df;
127
162
  /*
128
163
  From the Console's --color-error-fill. --cg-danger measures 3.72:1 on it. danger had no fill before; a chip needs one. */
129
164
  --cg-danger-fill: #fceded;
@@ -388,7 +423,7 @@
388
423
  --cg-danger: #FFDAD6;
389
424
 
390
425
  /* ---- state ----
391
- state: the per-surface values for online/offline/expired. Same necessity as danger — measured, not assumed. pending is an alias of expired (#114), declared here so it flips rather than freezing to the base scope. */
426
+ state: the per-surface values for online/offline/expired. Same necessity as danger — measured, not assumed. pending is an alias of expired (#114), declared here so it flips rather than freezing to the base scope. unhealthy (#122) is a real value rather than an alias, and needs the same per-surface treatment for the same reason every other tone here does: its base gold is unreadable on this ground. */
392
427
  /*
393
428
  4.88:1 on the purple. The source green is 1.72:1 here — invisible, same class as the danger override. */
394
429
  --cg-online: #C8F5DC;
@@ -401,6 +436,13 @@
401
436
  /*
402
437
  Aliased to expired, which is 4.68:1 on the purple. Declared here as well as at :root because an alias is substituted where it is DECLARED — a :root-only pending would freeze to the white-surface amber and stop flipping, with no error. */
403
438
  --cg-pending: var(--cg-expired);
439
+ /*
440
+ 4.41:1 on the purple, 3.27:1 on its own fill. The source gold is 1.09:1 on this ground — invisible, same class as the danger override.
441
+
442
+ This is the surface that DECIDED the value. A dot on a panel chip has to clear 3:1 against rgba(255,255,255,0.16) over #5a47f0, whose relative luminance is 0.192; that forces the dot's own luminance above 0.677, i.e. Lab L >= 86, which is why every state value here is a near-white tint. Four already occupy that sliver at hues 29, 81, 159 and 297, and a fifth has to fit between them. The gold sits at hue 97, 20.0 from expired and 29.5 from online.
443
+
444
+ It is louder than its four siblings, deliberately: a warning that recedes is not doing its job, and chroma is the only axis left once lightness is pinned by the contrast floor. */
445
+ --cg-unhealthy: #F7E20C;
404
446
  /*
405
447
  Translucent white rather than a tint; the panel foreground measures 3.62:1 on it. */
406
448
  --cg-online-fill: rgba(255, 255, 255, 0.16);
@@ -410,6 +452,9 @@
410
452
  /*
411
453
  Aliased to expired-fill. Declared per scope for the same reason the foreground is. */
412
454
  --cg-pending-fill: var(--cg-expired-fill);
455
+ /*
456
+ Translucent white rather than a tint, like every other chip ground here; the panel foreground measures 4.33:1 on it. Written out rather than aliased to online-fill for the same reason the others are: four tokens that happen to share a value are not one token. */
457
+ --cg-unhealthy-fill: rgba(255, 255, 255, 0.16);
413
458
  /*
414
459
  Translucent white rather than a tint; the panel foreground measures 3.35:1 on it. */
415
460
  --cg-danger-fill: rgba(255, 255, 255, 0.16);
@@ -451,7 +496,7 @@
451
496
  --cg-danger: #FF8A80;
452
497
 
453
498
  /* ---- state ----
454
- state: the per-surface values for online/offline/expired. Same necessity as danger — measured, not assumed. pending is an alias of expired (#114), declared here so it flips rather than freezing to the base scope. */
499
+ state: the per-surface values for online/offline/expired. Same necessity as danger — measured, not assumed. pending is an alias of expired (#114), declared here so it flips rather than freezing to the base scope. unhealthy (#122) is a real value rather than an alias, and needs the same per-surface treatment for the same reason every other tone here does: its base gold is unreadable on this ground. */
455
500
  /*
456
501
  9.00:1 on #222. Ink has headroom, so this stays a recognisable green rather than washing to mint. */
457
502
  --cg-online: #5FD99A;
@@ -462,6 +507,11 @@
462
507
  /*
463
508
  Aliased to expired, which is 8.63:1 on #222. Declared here as well as at :root because an alias is substituted where it is DECLARED — a :root-only pending would freeze to the white-surface amber and stop flipping, with no error. */
464
509
  --cg-pending: var(--cg-expired);
510
+ /*
511
+ 11.35:1 on #222, 8.88:1 on its own fill. Ink has headroom, so the gold stays a gold instead of washing to cream.
512
+
513
+ This is the tightest the tone gets anywhere: 18.3 from expired, which is a new worst pair for this surface (was 25.3, offline/danger) while staying above the palette's global floor of 16.5. Recorded rather than tuned away — a light amber and a light gold have the least room on a dark ground, and the next 2 points of separation would walk into --cg-secondary (16.5 away) or --cg-volt (17.8). The pair that matters most is unaffected: 31.3 from online. */
514
+ --cg-unhealthy: #F1DC03;
465
515
  /* Translucent white; the ink foreground measures 7.04:1 on it. */
466
516
  --cg-online-fill: rgba(255, 255, 255, 0.08);
467
517
  /* Translucent white; the ink foreground measures 6.76:1 on it. */
@@ -469,6 +519,8 @@
469
519
  /*
470
520
  Aliased to expired-fill. Declared per scope for the same reason the foreground is. */
471
521
  --cg-pending-fill: var(--cg-expired-fill);
522
+ /* Translucent white; the ink foreground measures 11.42:1 on it. */
523
+ --cg-unhealthy-fill: rgba(255, 255, 255, 0.08);
472
524
  /* Translucent white; the ink foreground measures 5.45:1 on it. */
473
525
  --cg-danger-fill: rgba(255, 255, 255, 0.08);
474
526
 
package/dist/tokens.json CHANGED
@@ -12,9 +12,11 @@
12
12
  "--cg-offline": "var(--cg-neutral-500)",
13
13
  "--cg-expired": "#b0662a",
14
14
  "--cg-pending": "var(--cg-expired)",
15
+ "--cg-unhealthy": "#7F7301",
15
16
  "--cg-online-fill": "#e6f7ef",
16
17
  "--cg-expired-fill": "#fdf3e0",
17
18
  "--cg-pending-fill": "var(--cg-expired-fill)",
19
+ "--cg-unhealthy-fill": "#fdf6df",
18
20
  "--cg-danger-fill": "#fceded",
19
21
  "--cg-primary-press": "#4b39db",
20
22
  "--cg-primary-tint": "#eeecfe",
@@ -147,9 +149,11 @@
147
149
  "--cg-offline": "#D9D4FB",
148
150
  "--cg-expired": "#FFE2B8",
149
151
  "--cg-pending": "var(--cg-expired)",
152
+ "--cg-unhealthy": "#F7E20C",
150
153
  "--cg-online-fill": "rgba(255, 255, 255, 0.16)",
151
154
  "--cg-expired-fill": "rgba(255, 255, 255, 0.16)",
152
155
  "--cg-pending-fill": "var(--cg-expired-fill)",
156
+ "--cg-unhealthy-fill": "rgba(255, 255, 255, 0.16)",
153
157
  "--cg-danger-fill": "rgba(255, 255, 255, 0.16)",
154
158
  "--cg-hover": "rgba(255, 255, 255, 0.10)"
155
159
  },
@@ -172,9 +176,11 @@
172
176
  "--cg-offline": "#9A9A9A",
173
177
  "--cg-expired": "#F0B45E",
174
178
  "--cg-pending": "var(--cg-expired)",
179
+ "--cg-unhealthy": "#F1DC03",
175
180
  "--cg-online-fill": "rgba(255, 255, 255, 0.08)",
176
181
  "--cg-expired-fill": "rgba(255, 255, 255, 0.08)",
177
182
  "--cg-pending-fill": "var(--cg-expired-fill)",
183
+ "--cg-unhealthy-fill": "rgba(255, 255, 255, 0.08)",
178
184
  "--cg-danger-fill": "rgba(255, 255, 255, 0.08)",
179
185
  "--cg-hover": "rgba(255, 255, 255, 0.06)"
180
186
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudgrid-io/ui",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "CloudGrid design system: React components, brand design tokens, Tailwind theme, self-hosted fonts.",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/",