@cloudgrid-io/ui 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  The CloudGrid design system: CSS custom properties, a Tailwind v4 theme, self-hosted
4
4
  fonts, and the same values as machine-readable JSON.
5
5
 
6
- 44 React components, built on [Base UI](https://base-ui.com), styled only through tokens
6
+ 47 React components, built on [Base UI](https://base-ui.com), styled only through tokens
7
7
  and carrying no hardcoded colour.
8
8
 
9
9
  ## Install
@@ -0,0 +1,89 @@
1
+ /**
2
+ * The card an entity gets everywhere it is listed: a preview, a name, an
3
+ * address, a line about it, whatever state it is in, and a stat or two.
4
+ *
5
+ * Absorbed out of the console (#117), where the same composition serves the
6
+ * discover list, the discover map, my stuff, home, space pages and the inspired
7
+ * runtimes row. This library previously had `Card`, which is a shell — header,
8
+ * title, description, content, footer — and every CloudGrid-specific thing about
9
+ * an entity card was hand-built downstream and out of the designer's reach.
10
+ *
11
+ * This is the shell, not the console's card. It takes data as props and holds no
12
+ * product rules: it does not build URLs, does not know the wire status
13
+ * vocabulary, does not resolve visibility or permissions, does not fetch, and
14
+ * does not route. All of that stays where it belongs. A design system that
15
+ * learns the product's rules stops being a design system.
16
+ *
17
+ * ## Two tiers of preview, and neither can fail dark
18
+ *
19
+ * `seed` turns the preview region on. With `seed` alone the card shows the
20
+ * generative identity tile (`EntityIdentity`), derived from the id and the title
21
+ * with no network access at all. Add `previewUrl` and a real capture paints over
22
+ * it.
23
+ *
24
+ * The capture is a background image rather than an `<img>`, which is the one
25
+ * deliberate departure from the console's implementation. A background image
26
+ * that 404s paints nothing and the identity underneath simply shows through — so
27
+ * the fallback needs no `onError`, no `useState`, and therefore no client
28
+ * boundary on a card that is otherwise entirely static. The console needs its
29
+ * state only to hide a broken `<img>`.
30
+ *
31
+ * That costs the preview an `alt`. It is the right trade here: the console's own
32
+ * identity card is `aria-hidden` whenever a title sits beside it, because
33
+ * announcing a decorative preview after the title reads the card twice.
34
+ *
35
+ * ## Status is a slot
36
+ *
37
+ * `status` takes a node, not a status name. `EntityStatus` is a separate
38
+ * component with the nine-status CloudGrid vocabulary in it; putting that
39
+ * vocabulary in the card would mean the card knows what an entity can BE, which
40
+ * is product knowledge. The console maps its wire status through its own table
41
+ * and drops the result in here.
42
+ *
43
+ * ## Surfaces
44
+ *
45
+ * The chrome flips: ground, hairlines, title, body text and the stat rule all
46
+ * come from tokens, so `data-cg-surface="panel"` on any ancestor moves them
47
+ * together. The identity tile deliberately does not flip — see its own note; it
48
+ * is a picture, and a picture does not flip.
49
+ */
50
+ export interface EntityCardStat {
51
+ /**
52
+ * A small decorative glyph, 14 to 16px. `label` is what gets announced, so
53
+ * the glyph itself should carry no accessible name.
54
+ */
55
+ icon?: React.ReactNode;
56
+ value: React.ReactNode;
57
+ /** Names the stat, e.g. "Views". Rendered for assistive tech. */
58
+ label: string;
59
+ }
60
+ declare function EntityCard({ title, slug, description, titleLines, descriptionLines, status, stats, seed, previewUrl, href, ariaLabel, meta, footer, className, children, ...props }: Omit<React.ComponentProps<"div">, "ref" | "title"> & {
61
+ title: string;
62
+ /** The entity's address, in the mono utility face. */
63
+ slug?: string;
64
+ description?: string;
65
+ titleLines?: 1 | 2;
66
+ descriptionLines?: 1 | 2;
67
+ /** Top right of the header row. Drop an `EntityStatus` in. */
68
+ status?: React.ReactNode;
69
+ stats?: EntityCardStat[];
70
+ /**
71
+ * The entity's stable id. Its presence is what turns the preview region on,
72
+ * and its value is what picks the identity look. Pass the id, not the title.
73
+ */
74
+ seed?: string;
75
+ /** A real capture, painted over the identity tile. A miss falls back to it. */
76
+ previewUrl?: string;
77
+ /** When set, the whole card is the target. A plain anchor: routing is yours. */
78
+ href?: string;
79
+ /** Accessible name for the whole-card link. Defaults to `title`. */
80
+ ariaLabel?: string;
81
+ /** Between the description and the stats. The console puts its residence line here. */
82
+ meta?: React.ReactNode;
83
+ /** Below everything, under no rule of its own. */
84
+ footer?: React.ReactNode;
85
+ /** Replaces the whole body. The preview and the card chrome still render. */
86
+ children?: React.ReactNode;
87
+ }): import("react").JSX.Element;
88
+ export { EntityCard };
89
+ //# sourceMappingURL=entity-card.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity-card.d.ts","sourceRoot":"","sources":["../../src/blocks/entity-card.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAEH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAA;CACd;AAED,iBAAS,UAAU,CAAC,EAClB,KAAK,EACL,IAAI,EACJ,WAAW,EACX,UAAc,EACd,gBAAoB,EACpB,MAAM,EACN,KAAK,EACL,IAAI,EACJ,UAAU,EACV,IAAI,EACJ,SAAS,EACT,IAAI,EACJ,MAAM,EACN,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,GAAG;IACtD,KAAK,EAAE,MAAM,CAAA;IACb,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;IAClB,gBAAgB,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;IACxB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACxB,KAAK,CAAC,EAAE,cAAc,EAAE,CAAA;IACxB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,gFAAgF;IAChF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,uFAAuF;IACvF,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,kDAAkD;IAClD,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACxB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC3B,+BAwIA;AAED,OAAO,EAAE,UAAU,EAAE,CAAA"}
@@ -0,0 +1,35 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Card } from "../ui/card.js";
3
+ import { EntityIdentity } from "./entity-identity.js";
4
+ import { cn } from "../lib/cloudgrid-cn.js";
5
+ function EntityCard({ title, slug, description, titleLines = 2, descriptionLines = 1, status, stats, seed, previewUrl, href, ariaLabel, meta, footer, className, children, ...props }) {
6
+ const body = children ??
7
+ (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("span", { dir: "auto", "data-slot": "entity-card-title", title: title, className: cn("block text-body-sm font-medium break-words text-ink", titleLines === 1 ? "truncate" : "line-clamp-2"), children: title }), slug ? (_jsx("span", { dir: "auto", "data-slot": "entity-card-slug", title: slug, className: "mt-0.5 block truncate font-mono text-caption text-faint", children: slug })) : null] }), status ? _jsx("div", { className: "shrink-0", children: status }) : null] }), description ? (_jsx("p", { dir: "auto", "data-slot": "entity-card-description", title: description, className: cn("m-0 text-body-sm text-body", descriptionLines === 1 ? "truncate" : "line-clamp-2"), children: description })) : null, meta, _jsx("div", { "aria-hidden": "true", className: "grow" }), stats && stats.length > 0 ? (_jsx("div", { "data-slot": "entity-card-stats", className: "flex flex-wrap items-center gap-4 border-t border-hairline pt-3 text-caption text-body", children: stats.map((stat) => (_jsxs("span", { className: "inline-flex items-center gap-1.5", children: [stat.icon ? (_jsx("span", { "aria-hidden": "true", className: "inline-flex [&>svg]:size-4", children: stat.icon })) : null, stat.value, _jsx("span", { className: "sr-only", children: stat.label })] }, stat.label))) })) : null, footer] }));
8
+ const card = (_jsxs(Card, { "data-slot": "entity-card",
9
+ // gap-0 py-0: `Card` pads itself for a header/content/footer stack, and
10
+ // this card's preview is full bleed to the top edge. The padding moves
11
+ // inside, onto the body, so the preview can touch the corners.
12
+ // No hover lift, shadow, scale or colour change, even when the whole card
13
+ // is a link. That is the console's founder card spec (2026-08-16) carried
14
+ // across rather than re-decided here, and it matches this system's own
15
+ // canon: containment is a hairline, not elevation.
16
+ // `h-full` only inside the link wrapper, where the card has to fill the
17
+ // anchor. As a direct grid item it must NOT be h-full: a grid area has a
18
+ // definite height, so height:100% pins the card to the tallest card in the
19
+ // row even when the grid was told `items-start`.
20
+ className: cn("gap-0 py-0", href && "h-full", className), ...props, children: [seed ? (_jsxs("div", { "data-slot": "entity-card-preview", className: "relative shrink-0 border-b border-hairline", children: [_jsx(EntityIdentity, { seed: seed, name: title }), previewUrl ? (_jsx("div", { "data-slot": "entity-card-capture", "aria-hidden": "true", className: "absolute inset-0 bg-cover bg-center",
21
+ // A background image, not an <img>: a URL that 404s paints
22
+ // nothing and the identity tile below shows through, with no
23
+ // state and no client boundary. encodeURI escapes the quote that
24
+ // would otherwise break out of url("...").
25
+ style: { backgroundImage: `url("${encodeURI(previewUrl)}")` } })) : null] })) : null, _jsx("div", { className: "flex min-h-0 flex-1 flex-col gap-3 p-(--card-spacing)", children: body })] }));
26
+ if (!href)
27
+ return card;
28
+ return (_jsx("a", { href: href, "aria-label": ariaLabel ?? title,
29
+ // The focus ring is the same token every control uses, so it flips with
30
+ // the surface. A card-sized link with no visible focus state is unusable
31
+ // by keyboard, and that failure never shows up in a screenshot.
32
+ className: "group block rounded-xl no-underline focus-visible:ring-3 focus-visible:ring-ring focus-visible:outline-none", children: card }));
33
+ }
34
+ export { EntityCard };
35
+ //# sourceMappingURL=entity-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity-card.js","sourceRoot":"","sources":["../../src/blocks/entity-card.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * The identity tile: what a CloudGrid entity looks like before it looks like
3
+ * anything. A flat tinted field, the CloudGrid motif as a hairline mesh over it,
4
+ * and a monogram tile carrying the entity's initials.
5
+ *
6
+ * Absorbed out of the console's `GenerativePreview` (#117), where it exists for
7
+ * one reason: an entity's preview slot has to be filled by SOMETHING the moment
8
+ * the entity is created, and a screenshot does not exist yet. Everything here is
9
+ * derived from the two strings the caller already has, so this can never fail to
10
+ * a broken image, a dark rectangle or a spinner.
11
+ *
12
+ * ## The same entity always looks the same
13
+ *
14
+ * `seed` picks the look, through FNV-1a. A grid of these reads as a set of
15
+ * stable identities rather than a set of placeholders, which is the whole point:
16
+ * the tile is doing the job a favicon does. Pass the entity's id, not its title,
17
+ * or a rename changes what the entity looks like.
18
+ *
19
+ * ## It does not flip per surface, deliberately
20
+ *
21
+ * Every other component in this library flips inside `data-cg-surface`. This one
22
+ * does not, and the reason is that it is standing in for a photograph: a real
23
+ * screenshot layered over it would not flip either, so a flipping fallback would
24
+ * make the two tiers of one slot behave differently. Its field is mixed against
25
+ * `--cg-neutral-0` and its ink is `--cg-text-on-primary`, both of which are white
26
+ * in all three scopes.
27
+ *
28
+ * The card AROUND it flips normally. Only the picture holds still.
29
+ *
30
+ * ## No colour was invented
31
+ *
32
+ * The console's version carries ten hand-picked hexes. This library's rule is
33
+ * that a component consumes custom properties and never hardcodes a hex, so the
34
+ * looks below are built from the brand's own four values by mixing. Eight looks
35
+ * out of four hues, at two tint depths.
36
+ */
37
+ /**
38
+ * THE LOOK TABLE. Edit this to change what the identity tiles look like.
39
+ *
40
+ * Each entry is a hue token and how strongly the field is tinted with it. The
41
+ * monogram tile is the same hue driven toward ink, so it is always a deeper
42
+ * shade of the field it sits on and the white monogram on it always clears AA.
43
+ *
44
+ * Four hues, because the brand has four: the purple, the lime, the cyan and the
45
+ * ink. Two depths, because eight identities across a listing repeat noticeably
46
+ * less than four and cost nothing. Adding a fifth hue is one row here; nothing
47
+ * else in this file knows how many there are.
48
+ *
49
+ * The `tile` percentages are measured rather than eyeballed. White on the mixed
50
+ * tile: purple 7.99:1, lime 5.08:1, cyan 4.93:1, ink 15.9:1 — all clear AA for
51
+ * normal text, which is more than the monogram needs (it is decorative and set
52
+ * at a large-text size), and leaves room for a designer to lighten a tile
53
+ * without immediately going under.
54
+ */
55
+ export declare const ENTITY_IDENTITY_LOOKS: readonly [{
56
+ readonly name: "brand-soft";
57
+ readonly hue: "--cg-primary";
58
+ readonly field: "12%";
59
+ readonly tile: "72%";
60
+ }, {
61
+ readonly name: "lime-soft";
62
+ readonly hue: "--cg-secondary";
63
+ readonly field: "16%";
64
+ readonly tile: "44%";
65
+ }, {
66
+ readonly name: "cyan-soft";
67
+ readonly hue: "--cg-tertiary";
68
+ readonly field: "16%";
69
+ readonly tile: "44%";
70
+ }, {
71
+ readonly name: "ink-soft";
72
+ readonly hue: "--cg-ink";
73
+ readonly field: "8%";
74
+ readonly tile: "84%";
75
+ }, {
76
+ readonly name: "brand-deep";
77
+ readonly hue: "--cg-primary";
78
+ readonly field: "22%";
79
+ readonly tile: "72%";
80
+ }, {
81
+ readonly name: "lime-deep";
82
+ readonly hue: "--cg-secondary";
83
+ readonly field: "30%";
84
+ readonly tile: "44%";
85
+ }, {
86
+ readonly name: "cyan-deep";
87
+ readonly hue: "--cg-tertiary";
88
+ readonly field: "30%";
89
+ readonly tile: "44%";
90
+ }, {
91
+ readonly name: "ink-deep";
92
+ readonly hue: "--cg-ink";
93
+ readonly field: "16%";
94
+ readonly tile: "84%";
95
+ }];
96
+ export type EntityIdentityLook = (typeof ENTITY_IDENTITY_LOOKS)[number];
97
+ /**
98
+ * FNV-1a over the seed. Deterministic, stable across sessions and across
99
+ * machines, and cheap enough to run for every card in a listing.
100
+ *
101
+ * Taken from the console unchanged, deliberately: an entity that has been the
102
+ * teal one for six months should not become the purple one because the library
103
+ * picked a different hash.
104
+ */
105
+ export declare function entityIdentitySeed(seed: string): number;
106
+ /** The look a seed maps to. Same seed, same look, forever. */
107
+ export declare function entityIdentityLook(seed: string): EntityIdentityLook;
108
+ /**
109
+ * A name to its monogram: the initials of the first two words, or the first two
110
+ * letters of a single word. Punctuation and symbols are dropped first, so
111
+ * "@cloudgrid/cli" reads CL rather than @C.
112
+ *
113
+ * An empty or symbol-only name gives "?" rather than an empty tile, because an
114
+ * empty tile looks like a rendering failure and a question mark looks like an
115
+ * unnamed thing, which is what it is.
116
+ */
117
+ export declare function entityMonogram(name: string): string;
118
+ declare function EntityIdentity({ seed, name, monogram, className, style, ...props }: Omit<React.ComponentProps<"div">, "children"> & {
119
+ /** The entity's stable id. Picks the look; never pass the title. */
120
+ seed: string;
121
+ /** The entity's name. Only used for the monogram. */
122
+ name?: string;
123
+ /** Overrides the derived monogram, for a name the initials read badly from. */
124
+ monogram?: string;
125
+ }): import("react").JSX.Element;
126
+ export { EntityIdentity };
127
+ //# sourceMappingURL=entity-identity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity-identity.d.ts","sourceRoot":"","sources":["../../src/blocks/entity-identity.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EASxB,CAAA;AAEV,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEvE;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOvD;AAED,8DAA8D;AAC9D,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,CAEnE;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQnD;AAED,iBAAS,cAAc,CAAC,EACtB,IAAI,EACJ,IAAI,EACJ,QAAQ,EACR,SAAS,EACT,KAAK,EACL,GAAG,KAAK,EACT,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,GAAG;IACjD,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAA;IACZ,qDAAqD;IACrD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,+BAuDA;AAED,OAAO,EAAE,cAAc,EAAE,CAAA"}
@@ -0,0 +1,127 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../lib/cloudgrid-cn.js";
3
+ /**
4
+ * The identity tile: what a CloudGrid entity looks like before it looks like
5
+ * anything. A flat tinted field, the CloudGrid motif as a hairline mesh over it,
6
+ * and a monogram tile carrying the entity's initials.
7
+ *
8
+ * Absorbed out of the console's `GenerativePreview` (#117), where it exists for
9
+ * one reason: an entity's preview slot has to be filled by SOMETHING the moment
10
+ * the entity is created, and a screenshot does not exist yet. Everything here is
11
+ * derived from the two strings the caller already has, so this can never fail to
12
+ * a broken image, a dark rectangle or a spinner.
13
+ *
14
+ * ## The same entity always looks the same
15
+ *
16
+ * `seed` picks the look, through FNV-1a. A grid of these reads as a set of
17
+ * stable identities rather than a set of placeholders, which is the whole point:
18
+ * the tile is doing the job a favicon does. Pass the entity's id, not its title,
19
+ * or a rename changes what the entity looks like.
20
+ *
21
+ * ## It does not flip per surface, deliberately
22
+ *
23
+ * Every other component in this library flips inside `data-cg-surface`. This one
24
+ * does not, and the reason is that it is standing in for a photograph: a real
25
+ * screenshot layered over it would not flip either, so a flipping fallback would
26
+ * make the two tiers of one slot behave differently. Its field is mixed against
27
+ * `--cg-neutral-0` and its ink is `--cg-text-on-primary`, both of which are white
28
+ * in all three scopes.
29
+ *
30
+ * The card AROUND it flips normally. Only the picture holds still.
31
+ *
32
+ * ## No colour was invented
33
+ *
34
+ * The console's version carries ten hand-picked hexes. This library's rule is
35
+ * that a component consumes custom properties and never hardcodes a hex, so the
36
+ * looks below are built from the brand's own four values by mixing. Eight looks
37
+ * out of four hues, at two tint depths.
38
+ */
39
+ /**
40
+ * THE LOOK TABLE. Edit this to change what the identity tiles look like.
41
+ *
42
+ * Each entry is a hue token and how strongly the field is tinted with it. The
43
+ * monogram tile is the same hue driven toward ink, so it is always a deeper
44
+ * shade of the field it sits on and the white monogram on it always clears AA.
45
+ *
46
+ * Four hues, because the brand has four: the purple, the lime, the cyan and the
47
+ * ink. Two depths, because eight identities across a listing repeat noticeably
48
+ * less than four and cost nothing. Adding a fifth hue is one row here; nothing
49
+ * else in this file knows how many there are.
50
+ *
51
+ * The `tile` percentages are measured rather than eyeballed. White on the mixed
52
+ * tile: purple 7.99:1, lime 5.08:1, cyan 4.93:1, ink 15.9:1 — all clear AA for
53
+ * normal text, which is more than the monogram needs (it is decorative and set
54
+ * at a large-text size), and leaves room for a designer to lighten a tile
55
+ * without immediately going under.
56
+ */
57
+ export const ENTITY_IDENTITY_LOOKS = [
58
+ { name: "brand-soft", hue: "--cg-primary", field: "12%", tile: "72%" },
59
+ { name: "lime-soft", hue: "--cg-secondary", field: "16%", tile: "44%" },
60
+ { name: "cyan-soft", hue: "--cg-tertiary", field: "16%", tile: "44%" },
61
+ { name: "ink-soft", hue: "--cg-ink", field: "8%", tile: "84%" },
62
+ { name: "brand-deep", hue: "--cg-primary", field: "22%", tile: "72%" },
63
+ { name: "lime-deep", hue: "--cg-secondary", field: "30%", tile: "44%" },
64
+ { name: "cyan-deep", hue: "--cg-tertiary", field: "30%", tile: "44%" },
65
+ { name: "ink-deep", hue: "--cg-ink", field: "16%", tile: "84%" },
66
+ ];
67
+ /**
68
+ * FNV-1a over the seed. Deterministic, stable across sessions and across
69
+ * machines, and cheap enough to run for every card in a listing.
70
+ *
71
+ * Taken from the console unchanged, deliberately: an entity that has been the
72
+ * teal one for six months should not become the purple one because the library
73
+ * picked a different hash.
74
+ */
75
+ export function entityIdentitySeed(seed) {
76
+ let h = 0x811c9dc5;
77
+ for (let i = 0; i < seed.length; i++) {
78
+ h ^= seed.charCodeAt(i);
79
+ h = Math.imul(h, 0x01000193);
80
+ }
81
+ return h >>> 0;
82
+ }
83
+ /** The look a seed maps to. Same seed, same look, forever. */
84
+ export function entityIdentityLook(seed) {
85
+ return ENTITY_IDENTITY_LOOKS[entityIdentitySeed(seed) % ENTITY_IDENTITY_LOOKS.length];
86
+ }
87
+ /**
88
+ * A name to its monogram: the initials of the first two words, or the first two
89
+ * letters of a single word. Punctuation and symbols are dropped first, so
90
+ * "@cloudgrid/cli" reads CL rather than @C.
91
+ *
92
+ * An empty or symbol-only name gives "?" rather than an empty tile, because an
93
+ * empty tile looks like a rendering failure and a question mark looks like an
94
+ * unnamed thing, which is what it is.
95
+ */
96
+ export function entityMonogram(name) {
97
+ const words = name
98
+ .replace(/[^\p{L}\p{N} ]/gu, " ")
99
+ .split(/\s+/)
100
+ .filter(Boolean);
101
+ const first = words[0]?.[0] ?? "?";
102
+ const second = words[1]?.[0] ?? words[0]?.[1] ?? "";
103
+ return (first + second).toUpperCase();
104
+ }
105
+ function EntityIdentity({ seed, name, monogram, className, style, ...props }) {
106
+ const look = entityIdentityLook(seed);
107
+ const letters = monogram ?? entityMonogram(name ?? "");
108
+ return (_jsxs("div", { "data-slot": "entity-identity", "data-look": look.name, "aria-hidden": "true", className: cn("relative isolate aspect-[1200/630] w-full select-none overflow-hidden", className), style: {
109
+ // Mixed against --cg-neutral-0 rather than --cg-surface-card: the card
110
+ // ground is `transparent` inside a purple panel, and mixing a hue into
111
+ // transparent gives a half-transparent hue rather than a tint. White is
112
+ // white in all three scopes, which is what "it does not flip" means.
113
+ background: `color-mix(in srgb, var(${look.hue}) ${look.field}, var(--cg-neutral-0))`,
114
+ ...style,
115
+ }, ...props, children: [_jsx("div", { "aria-hidden": "true", "data-slot": "entity-identity-mesh", className: "absolute inset-0", style: {
116
+ backgroundImage: "linear-gradient(to right, var(--cg-identity-mesh) 1px, transparent 1px)," +
117
+ " linear-gradient(to bottom, var(--cg-identity-mesh) 1px, transparent 1px)",
118
+ backgroundSize: "22px 22px",
119
+ backgroundPosition: "-1px -1px",
120
+ "--cg-identity-mesh": "color-mix(in srgb, var(--cg-ink) 8%, transparent)",
121
+ } }), _jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: _jsx("span", { "data-slot": "entity-identity-monogram", className: "flex size-[46px] items-center justify-center rounded-md text-heading-md font-bold", style: {
122
+ background: `color-mix(in srgb, var(${look.hue}) ${look.tile}, var(--cg-ink))`,
123
+ color: "var(--cg-text-on-primary)",
124
+ }, children: letters }) })] }));
125
+ }
126
+ export { EntityIdentity };
127
+ //# sourceMappingURL=entity-identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity-identity.js","sourceRoot":"","sources":["../../src/blocks/entity-identity.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,128 @@
1
+ import * as React from "react";
2
+ import { PageHeading } from "./page-heading.js";
3
+ /**
4
+ * A question screen: a heading, a line saying how to answer it, a set of
5
+ * selectable option cards, and the button that moves on.
6
+ *
7
+ * The shape the console's onboarding asks every question in (#119). Read from
8
+ * Figma, the four channel flows reuse five screens and three of them are this
9
+ * one — "How did you find us", "What you usually build", "Let's know each other
10
+ * better" — differing in exactly one respect: whether you may pick more than
11
+ * one. So this is one component with a `mode`, not three screens and not two
12
+ * components. Everything else about them is identical, and duplicating it would
13
+ * mean a designer retuning the option card in two places and finding one.
14
+ *
15
+ * ## Presentation only
16
+ *
17
+ * The same line #117 drew for the entity card. This component carries no
18
+ * CloudGrid question, no channel logic and no idea what comes next. It knows
19
+ * nothing about "invite link" or "website"; it renders the options it is handed
20
+ * and reports what was picked. The strings, the sequence, the persistence and
21
+ * the meaning all stay in the console.
22
+ *
23
+ * ## One `mode`, two ARIA patterns
24
+ *
25
+ * These are not the same control with a different limit, and that is why they
26
+ * are Base UI's rather than a `role` attribute and a `keydown` handler:
27
+ *
28
+ * | | `single` | `multi` |
29
+ * |---|---|---|
30
+ * | container | `role="radiogroup"` | `role="group"` |
31
+ * | option | `role="radio"` | `role="checkbox"` |
32
+ * | tab stops | ONE for the group | one per option |
33
+ * | keys | arrows move and select, Space selects | Space toggles the focused one |
34
+ *
35
+ * A radio group has a single tab stop and roving focus; a checkbox group does
36
+ * not. Hand-rolling that reads correct in review and ships a group a keyboard
37
+ * cannot traverse, with nothing anywhere going red.
38
+ *
39
+ * ## The option IS the library's OptionCard
40
+ *
41
+ * Each option renders through `OptionCard` as Base UI's `render` element, so the
42
+ * card a designer tunes in `option-card.tsx` is the card this screen shows, and
43
+ * the selection behaviour is layered on top rather than a second card being
44
+ * grown here. `OptionCard` itself stays what it is — a static card or a link —
45
+ * because "a card that navigates" and "a card that holds an answer" are
46
+ * different controls, and a `selected` prop on the former would mean nothing
47
+ * most of the time.
48
+ *
49
+ * ## Selection is never colour alone
50
+ *
51
+ * Three independent signals, because the palette alone cannot carry it. Each
52
+ * option has an indicator that changes SHAPE when picked — an empty ring gains a
53
+ * dot, an empty box gains a tick — drawn in `currentColor`, which is `text-ink`
54
+ * and therefore legible on every surface. The card's border goes from hairline
55
+ * to strong. `aria-checked` carries the same fact to assistive tech.
56
+ *
57
+ * ## Surfaces, and the token that would have broken this
58
+ *
59
+ * On the panel scope `tokens.css` declares text, three line colours, the focus
60
+ * ring, a transparent card, the link pair and the state palette, and nothing
61
+ * else. `--cg-border-accent` is NOT among them, so it inherits `:root`, which is
62
+ * `var(--cg-primary)` — the panel's own purple. A selected card outlined in the
63
+ * accent is invisible on the panel, and looks perfect everywhere anyone would
64
+ * check. `--cg-primary-tint` is base-only and would paint near-white on purple.
65
+ *
66
+ * So the selected state is `--cg-border-strong` (ink on the page, white on both
67
+ * dark surfaces) plus the drawn indicator. No accent border, no tint, no fill.
68
+ */
69
+ export interface OnboardingOption {
70
+ /** What `onValueChange` reports. Stable, and never shown. */
71
+ value: string;
72
+ /** The words on the card. */
73
+ label: string;
74
+ /**
75
+ * An outline SVG at 26 to 30px — `OptionCard`'s glyph contract, unchanged.
76
+ * Icons in this system are monoline: never filled, never on a chip.
77
+ */
78
+ icon?: React.ReactNode;
79
+ /** A line under the label, when the label alone is not enough. */
80
+ description?: string;
81
+ disabled?: boolean;
82
+ }
83
+ /** Two columns is the Figma arrangement. One reads as a list, three as a picker. */
84
+ declare const COLUMNS: {
85
+ readonly 1: "grid-cols-1";
86
+ readonly 2: "grid-cols-1 sm:grid-cols-2";
87
+ readonly 3: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3";
88
+ };
89
+ /** Everything both modes share. The mode-specific half is the union below. */
90
+ type OnboardingQuestionBaseProps = Omit<React.ComponentProps<"section">, "ref" | "title" | "defaultValue" | "onChange"> & {
91
+ title: string;
92
+ /** How to answer it: "Pick one", "Pick all that apply". */
93
+ sub?: string;
94
+ eyebrow?: string;
95
+ options: OnboardingOption[];
96
+ columns?: keyof typeof COLUMNS;
97
+ /** Forwarded to `PageHeading`. `h2` for a question inside a larger page. */
98
+ level?: 1 | 2;
99
+ headingSize?: React.ComponentProps<typeof PageHeading>["size"];
100
+ continueLabel?: string;
101
+ onContinue?: () => void;
102
+ /**
103
+ * Overrides when the button is live. Left alone it enables as soon as there is
104
+ * an answer, which is the only rule this component can know. Anything else —
105
+ * a minimum, a question that may be skipped — is the console's to decide.
106
+ */
107
+ canContinue?: boolean;
108
+ /** Under the button. The console puts "Skip for now" here. */
109
+ footer?: React.ReactNode;
110
+ /** Submitted with a surrounding form, when there is one. */
111
+ name?: string;
112
+ };
113
+ type OnboardingQuestionProps = OnboardingQuestionBaseProps & ({
114
+ /** One answer. A radio group: one tab stop, arrows move and select. */
115
+ mode?: "single";
116
+ value?: string | null;
117
+ defaultValue?: string | null;
118
+ onValueChange?: (value: string | null) => void;
119
+ } | {
120
+ /** Any number of answers. A checkbox group: each option is a tab stop. */
121
+ mode: "multi";
122
+ value?: string[];
123
+ defaultValue?: string[];
124
+ onValueChange?: (value: string[]) => void;
125
+ });
126
+ declare function OnboardingQuestion(props: OnboardingQuestionProps): React.JSX.Element;
127
+ export { OnboardingQuestion };
128
+ //# sourceMappingURL=onboarding-question.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,86 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Checkbox } from "@base-ui/react/checkbox";
5
+ import { CheckboxGroup } from "@base-ui/react/checkbox-group";
6
+ import { Radio } from "@base-ui/react/radio";
7
+ import { RadioGroup } from "@base-ui/react/radio-group";
8
+ import { CheckIcon } from "lucide-react";
9
+ import { Button } from "../ui/button.js";
10
+ import { OptionCard } from "./option-card.js";
11
+ import { PageHeading } from "./page-heading.js";
12
+ import { cn } from "../lib/cloudgrid-cn.js";
13
+ /** Two columns is the Figma arrangement. One reads as a list, three as a picker. */
14
+ const COLUMNS = {
15
+ 1: "grid-cols-1",
16
+ 2: "grid-cols-1 sm:grid-cols-2",
17
+ 3: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3",
18
+ };
19
+ /**
20
+ * The card, in both modes. Every class here is a utility over a `--cg-*` token,
21
+ * so the whole thing flips with the surface.
22
+ *
23
+ * `group` is what lets the indicator inside react to the root's `data-checked`.
24
+ * `pe-11` reserves the indicator's corner so a long label cannot run under it;
25
+ * tailwind-merge resolves it against `OptionCard`'s own `p-5` rather than
26
+ * fighting it.
27
+ */
28
+ const OPTION_CLASS = cn("group relative cursor-pointer pe-11 text-start transition-colors select-none", "hover:border-line", "focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring focus-visible:outline-none", "data-checked:border-line-strong data-checked:ring-1 data-checked:ring-(--cg-border-strong)", "data-disabled:pointer-events-none data-disabled:opacity-50");
29
+ /** The indicator's frame. Round for one-of, square for many-of: shape says which. */
30
+ const INDICATOR_CLASS = cn("pointer-events-none absolute end-4 top-4 flex size-4 shrink-0 items-center justify-center", "border border-line text-ink transition-colors group-data-checked:border-line-strong");
31
+ function OnboardingQuestionOption({ option, mode, name, }) {
32
+ // `render` rather than children: Base UI merges its own role, aria-checked,
33
+ // data-checked, tabIndex, handlers and ref INTO this element, so the option is
34
+ // literally an OptionCard with radio or checkbox semantics rather than a
35
+ // lookalike. className is concatenated, and OptionCard runs the result through
36
+ // cn() last, so these utilities win the conflicts they are meant to win.
37
+ const card = (_jsx(OptionCard, { title: option.label, body: option.description, glyph: option.icon }));
38
+ if (mode === "multi") {
39
+ return (_jsx(Checkbox.Root, { value: option.value, name: name, disabled: option.disabled, "data-slot": "onboarding-question-option", className: OPTION_CLASS, render: card, children: _jsx("span", { "aria-hidden": "true", "data-slot": "onboarding-question-indicator", className: cn(INDICATOR_CLASS, "rounded-[4px]"), children: _jsx(Checkbox.Indicator, { className: "flex [&>svg]:size-3", children: _jsx(CheckIcon, { strokeWidth: 3 }) }) }) }));
40
+ }
41
+ return (_jsx(Radio.Root, { value: option.value, disabled: option.disabled, "data-slot": "onboarding-question-option", className: OPTION_CLASS, render: card, children: _jsx("span", { "aria-hidden": "true", "data-slot": "onboarding-question-indicator", className: cn(INDICATOR_CLASS, "rounded-pill"), children: _jsx(Radio.Indicator, { className: "size-2 rounded-pill bg-current" }) }) }));
42
+ }
43
+ function OnboardingQuestion(props) {
44
+ // Narrowed on the discriminant BEFORE destructuring, which is what keeps the
45
+ // value types honest: `single` answers with `string | null` and `multi` with
46
+ // `string[]`, and a consumer never has to narrow what came back. Destructuring
47
+ // first would collapse both into one union and hand the console a value it has
48
+ // to unpick at every call site.
49
+ const isMulti = props.mode === "multi";
50
+ const { title, sub, eyebrow, options, columns = 2, level = 1, headingSize = "md", continueLabel = "Continue", onContinue, canContinue, footer, name, className,
51
+ // Held back so they cannot reach the DOM as attributes.
52
+ mode: _mode, value: _value, defaultValue: _defaultValue, onValueChange: _onValueChange, ...rest } = props;
53
+ // A mirror, kept for ONE reason: to know whether the button should be live.
54
+ // The group below stays the source of truth in both directions — controlled
55
+ // through `value`, uncontrolled through `defaultValue` — so this never becomes
56
+ // a second place the answer lives. `canContinue` overrides it outright.
57
+ const [picked, setPicked] = React.useState(() => {
58
+ const initial = props.value ?? props.defaultValue;
59
+ if (initial == null)
60
+ return [];
61
+ return Array.isArray(initial) ? initial : [initial];
62
+ });
63
+ const answered = (props.value !== undefined
64
+ ? Array.isArray(props.value)
65
+ ? props.value
66
+ : props.value == null
67
+ ? []
68
+ : [props.value]
69
+ : picked).length > 0;
70
+ // The group is labelled by the whole lockup, deliberately. The subline is the
71
+ // instruction — "Pick all that apply" — and a group's label is exactly where a
72
+ // screen reader user needs to hear it. Labelling by the title alone would drop
73
+ // the one sentence that says how many answers are allowed.
74
+ const headingId = React.useId();
75
+ const grid = cn("grid gap-4", COLUMNS[columns]);
76
+ return (_jsxs("section", { "data-slot": "onboarding-question", className: cn("flex flex-col gap-8", className), ...rest, children: [_jsx(PageHeading, { id: headingId, eyebrow: eyebrow, title: title, sub: sub, size: headingSize, level: level }), isMulti ? (_jsx(CheckboxGroup, { "aria-labelledby": headingId, "data-slot": "onboarding-question-options", className: grid, defaultValue: props.defaultValue ?? undefined, value: props.value, onValueChange: (next) => {
77
+ setPicked(next);
78
+ props.onValueChange?.(next);
79
+ }, children: options.map((option) => (_jsx(OnboardingQuestionOption, { option: option, mode: "multi", name: name }, option.value))) })) : (_jsx(RadioGroup, { "aria-labelledby": headingId, "data-slot": "onboarding-question-options", className: grid, name: name, defaultValue: props.defaultValue ?? undefined, value: props.value === null ? "" : props.value, onValueChange: (next) => {
80
+ const answer = typeof next === "string" && next !== "" ? next : null;
81
+ setPicked(answer == null ? [] : [answer]);
82
+ props.onValueChange?.(answer);
83
+ }, children: options.map((option) => (_jsx(OnboardingQuestionOption, { option: option, mode: "single" }, option.value))) })), _jsxs("div", { "data-slot": "onboarding-question-actions", className: "flex flex-col items-start gap-4", children: [_jsx(Button, { type: "button", size: "lg", "data-slot": "onboarding-question-continue", onClick: onContinue, disabled: !(canContinue ?? answered), children: continueLabel }), footer] })] }));
84
+ }
85
+ export { OnboardingQuestion };
86
+ //# sourceMappingURL=onboarding-question.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"onboarding-question.js","sourceRoot":"","sources":["../../src/blocks/onboarding-question.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAA;AAC7D,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAA;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAExC,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAA;AACxD,OAAO,EAAE,EAAE,EAAE,MAAM,oBAAoB,CAAA;AAoFvC,oFAAoF;AACpF,MAAM,OAAO,GAAG;IACd,CAAC,EAAE,aAAa;IAChB,CAAC,EAAE,4BAA4B;IAC/B,CAAC,EAAE,2CAA2C;CACtC,CAAA;AAEV;;;;;;;;GAQG;AACH,MAAM,YAAY,GAAG,EAAE,CACrB,8EAA8E,EAC9E,mBAAmB,EACnB,mGAAmG,EACnG,4FAA4F,EAC5F,4DAA4D,CAC7D,CAAA;AAED,qFAAqF;AACrF,MAAM,eAAe,GAAG,EAAE,CACxB,2FAA2F,EAC3F,qFAAqF,CACtF,CAAA;AAED,SAAS,wBAAwB,CAAC,EAChC,MAAM,EACN,IAAI,EACJ,IAAI,GAML;IACC,4EAA4E;IAC5E,+EAA+E;IAC/E,yEAAyE;IACzE,+EAA+E;IAC/E,yEAAyE;IACzE,MAAM,IAAI,GAAG,CACX,KAAC,UAAU,IAAC,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC,WAAW,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,GAAI,CAClF,CAAA;IAED,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,OAAO,CACL,KAAC,QAAQ,CAAC,IAAI,IACZ,KAAK,EAAE,MAAM,CAAC,KAAK,EACnB,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,CAAC,QAAQ,eACf,4BAA4B,EACtC,SAAS,EAAE,YAAY,EACvB,MAAM,EAAE,IAAI,YAEZ,8BACc,MAAM,eACR,+BAA+B,EACzC,SAAS,EAAE,EAAE,CAAC,eAAe,EAAE,eAAe,CAAC,YAE/C,KAAC,QAAQ,CAAC,SAAS,IAAC,SAAS,EAAC,qBAAqB,YACjD,KAAC,SAAS,IAAC,WAAW,EAAE,CAAC,GAAI,GACV,GAChB,GACO,CACjB,CAAA;IACH,CAAC;IAED,OAAO,CACL,KAAC,KAAK,CAAC,IAAI,IACT,KAAK,EAAE,MAAM,CAAC,KAAK,EACnB,QAAQ,EAAE,MAAM,CAAC,QAAQ,eACf,4BAA4B,EACtC,SAAS,EAAE,YAAY,EACvB,MAAM,EAAE,IAAI,YAEZ,8BACc,MAAM,eACR,+BAA+B,EACzC,SAAS,EAAE,EAAE,CAAC,eAAe,EAAE,cAAc,CAAC,YAE9C,KAAC,KAAK,CAAC,SAAS,IAAC,SAAS,EAAC,gCAAgC,GAAG,GACzD,GACI,CACd,CAAA;AACH,CAAC;AAgDD,SAAS,kBAAkB,CAAC,KAA8B;IACxD,6EAA6E;IAC7E,6EAA6E;IAC7E,+EAA+E;IAC/E,+EAA+E;IAC/E,gCAAgC;IAChC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,KAAK,OAAO,CAAA;IAEtC,MAAM,EACJ,KAAK,EACL,GAAG,EACH,OAAO,EACP,OAAO,EACP,OAAO,GAAG,CAAC,EACX,KAAK,GAAG,CAAC,EACT,WAAW,GAAG,IAAI,EAClB,aAAa,GAAG,UAAU,EAC1B,UAAU,EACV,WAAW,EACX,MAAM,EACN,IAAI,EACJ,SAAS;IACT,wDAAwD;IACxD,IAAI,EAAE,KAAK,EACX,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,aAAa,EAC3B,aAAa,EAAE,cAAc,EAC7B,GAAG,IAAI,EACR,GAAG,KAAK,CAAA;IAET,4EAA4E;IAC5E,4EAA4E;IAC5E,+EAA+E;IAC/E,wEAAwE;IACxE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAW,GAAG,EAAE;QACxD,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,YAAY,CAAA;QACjD,IAAI,OAAO,IAAI,IAAI;YAAE,OAAO,EAAE,CAAA;QAC9B,OAAO,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;IACrD,CAAC,CAAC,CAAA;IACF,MAAM,QAAQ,GAAG,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS;QACzC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;YAC1B,CAAC,CAAC,KAAK,CAAC,KAAK;YACb,CAAC,CAAC,KAAK,CAAC,KAAK,IAAI,IAAI;gBACnB,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC;QACnB,CAAC,CAAC,MAAM,CACT,CAAC,MAAM,GAAG,CAAC,CAAA;IAEZ,8EAA8E;IAC9E,+EAA+E;IAC/E,+EAA+E;IAC/E,2DAA2D;IAC3D,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,EAAE,CAAA;IAE/B,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAA;IAE/C,OAAO,CACL,gCAAmB,qBAAqB,EAAC,SAAS,EAAE,EAAE,CAAC,qBAAqB,EAAE,SAAS,CAAC,KAAM,IAAI,aAChG,KAAC,WAAW,IAAC,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,GAAI,EAExG,OAAO,CAAC,CAAC,CAAC,CACT,KAAC,aAAa,uBACK,SAAS,eAChB,6BAA6B,EACvC,SAAS,EAAE,IAAI,EACf,YAAY,EAAE,KAAK,CAAC,YAAY,IAAI,SAAS,EAC7C,KAAK,EAAE,KAAK,CAAC,KAAK,EAClB,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE;oBACtB,SAAS,CAAC,IAAI,CAAC,CAAA;oBACf,KAAK,CAAC,aAAa,EAAE,CAAC,IAAI,CAAC,CAAA;gBAC7B,CAAC,YAEA,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CACvB,KAAC,wBAAwB,IAAoB,MAAM,EAAE,MAAM,EAAE,IAAI,EAAC,OAAO,EAAC,IAAI,EAAE,IAAI,IAArD,MAAM,CAAC,KAAK,CAA6C,CACzF,CAAC,GACY,CACjB,CAAC,CAAC,CAAC,CACF,KAAC,UAAU,uBACQ,SAAS,eAChB,6BAA6B,EACvC,SAAS,EAAE,IAAI,EACf,IAAI,EAAE,IAAI,EACV,YAAY,EAAE,KAAK,CAAC,YAAY,IAAI,SAAS,EAC7C,KAAK,EAAE,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,EAC9C,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE;oBACtB,MAAM,MAAM,GAAG,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;oBACpE,SAAS,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA;oBACzC,KAAK,CAAC,aAAa,EAAE,CAAC,MAAM,CAAC,CAAA;gBAC/B,CAAC,YAEA,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CACvB,KAAC,wBAAwB,IAAoB,MAAM,EAAE,MAAM,EAAE,IAAI,EAAC,QAAQ,IAA3C,MAAM,CAAC,KAAK,CAAkC,CAC9E,CAAC,GACS,CACd,EAED,4BAAe,6BAA6B,EAAC,SAAS,EAAC,iCAAiC,aACtF,KAAC,MAAM,IACL,IAAI,EAAC,QAAQ,EACb,IAAI,EAAC,IAAI,eACC,8BAA8B,EACxC,OAAO,EAAE,UAAU,EACnB,QAAQ,EAAE,CAAC,CAAC,WAAW,IAAI,QAAQ,CAAC,YAEnC,aAAa,GACP,EACR,MAAM,IACH,IACE,CACX,CAAA;AACH,CAAC;AAED,OAAO,EAAE,kBAAkB,EAAE,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"page-heading.d.ts","sourceRoot":"","sources":["../../src/blocks/page-heading.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AACH,QAAA,MAAM,KAAK;;;;;CAKD,CAAA;AAEV,iBAAS,WAAW,CAAC,EACnB,OAAO,EACP,KAAK,EACL,GAAG,EACH,IAAW,EACX,KAAe,EACf,KAAS,EACT,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,GAAG;IAClC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,OAAO,KAAK,CAAA;IACzB,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAA;IAC1B,8EAA8E;IAC9E,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;CACd,+BA8BA;AAED,OAAO,EAAE,WAAW,EAAE,CAAA"}
1
+ {"version":3,"file":"page-heading.d.ts","sourceRoot":"","sources":["../../src/blocks/page-heading.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AACH,QAAA,MAAM,KAAK;;;;;CAKD,CAAA;AAEV,iBAAS,WAAW,CAAC,EACnB,OAAO,EACP,KAAK,EACL,GAAG,EACH,IAAW,EACX,KAAe,EACf,KAAS,EACT,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,GAAG;IAClC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,OAAO,KAAK,CAAA;IACzB,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAA;IAC1B,8EAA8E;IAC9E,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;CACd,+BAuCA;AAED,OAAO,EAAE,WAAW,EAAE,CAAA"}
@@ -24,7 +24,7 @@ const SIZES = {
24
24
  };
25
25
  function PageHeading({ eyebrow, title, sub, size = "lg", align = "start", level = 1, className, ...props }) {
26
26
  const Heading = level === 1 ? "h1" : "h2";
27
- return (_jsxs("header", { "data-slot": "page-heading", className: cn("flex flex-col gap-3", align === "center" && "items-center text-center", className), ...props, children: [eyebrow ? (_jsx("p", { className: "text-label font-medium text-faint", children: eyebrow })) : null, _jsx(Heading, { className: cn("font-display font-heavy tracking-display uppercase leading-display text-balance text-brand", SIZES[size]), children: title }), sub ? _jsx("p", { className: "text-body-lg text-body", children: sub }) : null] }));
27
+ return (_jsxs("header", { "data-slot": "page-heading", className: cn("flex flex-col gap-3", align === "center" && "items-center text-center", className), ...props, children: [eyebrow ? (_jsx("p", { className: "text-label font-medium text-faint", children: eyebrow })) : null, _jsx(Heading, { className: cn("font-display font-heavy tracking-display uppercase leading-display text-balance text-(--cg-text-accent)", SIZES[size]), children: title }), sub ? _jsx("p", { className: "text-body-lg text-body", children: sub }) : null] }));
28
28
  }
29
29
  export { PageHeading };
30
30
  //# sourceMappingURL=page-heading.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"page-heading.js","sourceRoot":"","sources":["../../src/blocks/page-heading.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,oBAAoB,CAAA;AAEvC;;;;;;;;;;;;;;;GAeG;AACH,MAAM,KAAK,GAAG;IACZ,EAAE,EAAE,iBAAiB;IACrB,EAAE,EAAE,oCAAoC;IACxC,EAAE,EAAE,oCAAoC;IACxC,EAAE,EAAE,oCAAoC;CAChC,CAAA;AAEV,SAAS,WAAW,CAAC,EACnB,OAAO,EACP,KAAK,EACL,GAAG,EACH,IAAI,GAAG,IAAI,EACX,KAAK,GAAG,OAAO,EACf,KAAK,GAAG,CAAC,EACT,SAAS,EACT,GAAG,KAAK,EAST;IACC,MAAM,OAAO,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;IAEzC,OAAO,CACL,+BACY,cAAc,EACxB,SAAS,EAAE,EAAE,CACX,qBAAqB,EACrB,KAAK,KAAK,QAAQ,IAAI,0BAA0B,EAChD,SAAS,CACV,KACG,KAAK,aAER,OAAO,CAAC,CAAC,CAAC,CACT,YAAG,SAAS,EAAC,mCAAmC,YAAE,OAAO,GAAK,CAC/D,CAAC,CAAC,CAAC,IAAI,EAIR,KAAC,OAAO,IACN,SAAS,EAAE,EAAE,CACX,4FAA4F,EAC5F,KAAK,CAAC,IAAI,CAAC,CACZ,YAEA,KAAK,GACE,EACT,GAAG,CAAC,CAAC,CAAC,YAAG,SAAS,EAAC,wBAAwB,YAAE,GAAG,GAAK,CAAC,CAAC,CAAC,IAAI,IACtD,CACV,CAAA;AACH,CAAC;AAED,OAAO,EAAE,WAAW,EAAE,CAAA"}
1
+ {"version":3,"file":"page-heading.js","sourceRoot":"","sources":["../../src/blocks/page-heading.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,oBAAoB,CAAA;AAEvC;;;;;;;;;;;;;;;GAeG;AACH,MAAM,KAAK,GAAG;IACZ,EAAE,EAAE,iBAAiB;IACrB,EAAE,EAAE,oCAAoC;IACxC,EAAE,EAAE,oCAAoC;IACxC,EAAE,EAAE,oCAAoC;CAChC,CAAA;AAEV,SAAS,WAAW,CAAC,EACnB,OAAO,EACP,KAAK,EACL,GAAG,EACH,IAAI,GAAG,IAAI,EACX,KAAK,GAAG,OAAO,EACf,KAAK,GAAG,CAAC,EACT,SAAS,EACT,GAAG,KAAK,EAST;IACC,MAAM,OAAO,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;IAEzC,OAAO,CACL,+BACY,cAAc,EACxB,SAAS,EAAE,EAAE,CACX,qBAAqB,EACrB,KAAK,KAAK,QAAQ,IAAI,0BAA0B,EAChD,SAAS,CACV,KACG,KAAK,aAER,OAAO,CAAC,CAAC,CAAC,CACT,YAAG,SAAS,EAAC,mCAAmC,YAAE,OAAO,GAAK,CAC/D,CAAC,CAAC,CAAC,IAAI,EAaR,KAAC,OAAO,IACN,SAAS,EAAE,EAAE,CACX,yGAAyG,EACzG,KAAK,CAAC,IAAI,CAAC,CACZ,YAEA,KAAK,GACE,EACT,GAAG,CAAC,CAAC,CAAC,YAAG,SAAS,EAAC,wBAAwB,YAAE,GAAG,GAAK,CAAC,CAAC,CAAC,IAAI,IACtD,CACV,CAAA;AACH,CAAC;AAED,OAAO,EAAE,WAAW,EAAE,CAAA"}
package/dist/index.d.ts CHANGED
@@ -27,10 +27,13 @@ export * from "./blocks/compare-table.js";
27
27
  export * from "./blocks/creation-tile.js";
28
28
  export * from "./blocks/cta-banner.js";
29
29
  export * from "./blocks/drop-zone.js";
30
+ export * from "./blocks/entity-card.js";
31
+ export * from "./blocks/entity-identity.js";
30
32
  export * from "./blocks/entity-status.js";
31
33
  export * from "./blocks/feature-cells.js";
32
34
  export * from "./blocks/hub-index.js";
33
35
  export * from "./blocks/logo.js";
36
+ export * from "./blocks/onboarding-question.js";
34
37
  export * from "./blocks/option-card.js";
35
38
  export * from "./blocks/page-heading.js";
36
39
  export * from "./blocks/plug-method.js";
@@ -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,2BAA2B,CAAA;AACzC,cAAc,2BAA2B,CAAA;AACzC,cAAc,uBAAuB,CAAA;AACrC,cAAc,kBAAkB,CAAA;AAChC,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;AAGtC,cAAc,uBAAuB,CAAA"}
package/dist/index.js CHANGED
@@ -1,14 +1,14 @@
1
1
  // The public surface of @cloudgrid-io/ui.
2
2
  //
3
3
  // GENERATED shape, hand-committed: one `export *` per component module. Flat is
4
- // safe here — the exported names across all 44 components collide zero times,
4
+ // safe here — the exported names across all 47 components collide zero times,
5
5
  // asserted by a test so a future addition cannot break it silently.
6
6
  //
7
7
  // Per-module re-exports rather than a bundle, deliberately. 20 of these carry a
8
8
  // "use client" directive, and the client boundary is a property of the MODULE.
9
9
  // tsc emits file-per-file and preserves the directive, so a React Server
10
10
  // Component consumer keeps exactly the boundaries the source declares. Bundling
11
- // them into one file would collapse 44 modules into a single client island.
11
+ // them into one file would collapse 47 modules into a single client island.
12
12
  //
13
13
  // The CSS is NOT imported here. It ships as separate entry points
14
14
  // (@cloudgrid-io/ui/tokens.css and friends) so a consumer with no React — the
@@ -44,10 +44,13 @@ export * from "./blocks/compare-table.js";
44
44
  export * from "./blocks/creation-tile.js";
45
45
  export * from "./blocks/cta-banner.js";
46
46
  export * from "./blocks/drop-zone.js";
47
+ export * from "./blocks/entity-card.js";
48
+ export * from "./blocks/entity-identity.js";
47
49
  export * from "./blocks/entity-status.js";
48
50
  export * from "./blocks/feature-cells.js";
49
51
  export * from "./blocks/hub-index.js";
50
52
  export * from "./blocks/logo.js";
53
+ export * from "./blocks/onboarding-question.js";
51
54
  export * from "./blocks/option-card.js";
52
55
  export * from "./blocks/page-heading.js";
53
56
  export * from "./blocks/plug-method.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,2BAA2B,CAAA;AACzC,cAAc,2BAA2B,CAAA;AACzC,cAAc,uBAAuB,CAAA;AACrC,cAAc,kBAAkB,CAAA;AAChC,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;AAEtC,0EAA0E;AAC1E,cAAc,uBAAuB,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudgrid-io/ui",
3
- "version": "0.3.0",
3
+ "version": "0.5.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/",