@cloudgrid-io/ui 0.11.0 → 0.13.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/dist/blocks/sign-in-options.d.ts +140 -0
- package/dist/blocks/sign-in-options.d.ts.map +1 -0
- package/dist/blocks/sign-in-options.js +57 -0
- package/dist/blocks/sign-in-options.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/theme.css +22 -0
- package/dist/tokens/base.json +99 -12
- package/dist/tokens/surface-ink.json +65 -1
- package/dist/tokens/surface-panel.json +65 -1
- package/dist/tokens.css +164 -7
- package/dist/tokens.json +26 -1
- package/package.json +1 -1
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Button } from "../ui/button.js";
|
|
3
|
+
/**
|
|
4
|
+
* The sign-in methods, in one place.
|
|
5
|
+
*
|
|
6
|
+
* ## Why this exists (#129)
|
|
7
|
+
*
|
|
8
|
+
* The console rendered its sign-in options in four separate places and they
|
|
9
|
+
* diverged: the invitation-accept screen offered ONLY Google, so an invitee
|
|
10
|
+
* without a Google account could not accept an invitation at all — no error, no
|
|
11
|
+
* fallback, on the one screen whose whole purpose is to let an invited person
|
|
12
|
+
* in. A fifth hand-written list would be the same mistake with a larger blast
|
|
13
|
+
* radius. So the list renders through one component, and adding a provider is
|
|
14
|
+
* one edit at the call site that owns the list rather than four that can
|
|
15
|
+
* silently disagree.
|
|
16
|
+
*
|
|
17
|
+
* ## Presentation only
|
|
18
|
+
*
|
|
19
|
+
* The same line #117 / #119 / #121 / #126 drew. This block holds no auth logic,
|
|
20
|
+
* no provider SDK, no redirect and no token; it renders the methods it is
|
|
21
|
+
* handed and never implies a provider that does not exist. Starting Google
|
|
22
|
+
* OAuth needs a session code the page mints — so that is a callback the page
|
|
23
|
+
* owns. The email option is a LINK, deliberately: middle-click and "open in new
|
|
24
|
+
* tab" work, and the target is visible in the status bar.
|
|
25
|
+
*
|
|
26
|
+
* ## The one thing it does own: `carry`
|
|
27
|
+
*
|
|
28
|
+
* Sign-in must not lose state that rides the URL — where to land afterwards
|
|
29
|
+
* (`return_to`), the onboarding path a NEW user should walk (`arrival`), and
|
|
30
|
+
* for the device flow the code being authorized (`code`) and the client that
|
|
31
|
+
* started it (`source`). Forgetting one is not a visual bug and shows up
|
|
32
|
+
* nowhere: sign-in succeeds and the person silently lands somewhere generic or
|
|
33
|
+
* walks the wrong flow. That has happened three times (`ai_tool`, `invite`,
|
|
34
|
+
* `watermark`).
|
|
35
|
+
*
|
|
36
|
+
* So the page declares what must survive ONCE, in `carry`, and the block
|
|
37
|
+
* applies it to every method uniformly: appended to each link method's `href`,
|
|
38
|
+
* handed to each action method's `onSelect`. The block never knows what the
|
|
39
|
+
* keys mean — the page wires, the block carries.
|
|
40
|
+
*
|
|
41
|
+
* One obligation stays with the caller, loudly: **validate `return_to` before
|
|
42
|
+
* handing it in.** The console keeps its `isSafePostAuthReturn` allowlist and
|
|
43
|
+
* this package cannot import it; an unvalidated `return_to` is an open
|
|
44
|
+
* redirect. Pass the value already validated with the same allowlist the
|
|
45
|
+
* post-auth resolver uses, so there is one thing to audit rather than two.
|
|
46
|
+
*
|
|
47
|
+
* ## Methods are groups
|
|
48
|
+
*
|
|
49
|
+
* `methods` is an array of GROUPS: buttons within a group stack tight, and the
|
|
50
|
+
* divider — "OR" unless `divider` says otherwise — renders between groups.
|
|
51
|
+
* `[[google], [email]]` is the console's current screen; a second OAuth
|
|
52
|
+
* provider joins the first group with no API change. The grouping is what lets
|
|
53
|
+
* the divider sit "between the providers and email" without the block knowing
|
|
54
|
+
* which method is which.
|
|
55
|
+
*
|
|
56
|
+
* ## Icons are handed in
|
|
57
|
+
*
|
|
58
|
+
* Every mark in this package is flattened to `currentColor` and the literal
|
|
59
|
+
* gate keeps it that way, so the four-colour Google G cannot live here without
|
|
60
|
+
* a brand decision. Consumers pass their own marks (`icon`), which the block
|
|
61
|
+
* hides from assistive tech: the accessible name is exactly `label`.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```tsx
|
|
65
|
+
* <SignInOptions
|
|
66
|
+
* methods={[
|
|
67
|
+
* [{ id: "google", label: "Continue with Google", icon: <GoogleIcon />, onSelect: startGoogle }],
|
|
68
|
+
* [{ id: "email", label: "Continue with Email", icon: <Mail />, href: "/auth/email" }],
|
|
69
|
+
* ]}
|
|
70
|
+
* carry={{ return_to: safeReturn, arrival, code, source }}
|
|
71
|
+
* renderLink={(p) => <Link {...p} />}
|
|
72
|
+
* />
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
interface SignInMethodBase {
|
|
76
|
+
/** Stable, never shown. Lands on the button as `data-method`. */
|
|
77
|
+
id: string;
|
|
78
|
+
/** The words on the button, and its exact accessible name. */
|
|
79
|
+
label: string;
|
|
80
|
+
/**
|
|
81
|
+
* The method's mark, ~20px inside a fixed 5-unit slot at the inline start.
|
|
82
|
+
* Hidden from assistive tech here, so any mark keeps the name = `label`.
|
|
83
|
+
*/
|
|
84
|
+
icon?: React.ReactNode;
|
|
85
|
+
disabled?: boolean;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Exactly one of `href` (a link method: a real anchor, `carry` appended) or
|
|
89
|
+
* `onSelect` (an action method: the page owns what selecting it starts, and
|
|
90
|
+
* receives the carried entries). The union is enforced in the types because a
|
|
91
|
+
* method with both would hide which one wins, and a method with neither is a
|
|
92
|
+
* button that cannot sign anyone in — worse than an absent one.
|
|
93
|
+
*/
|
|
94
|
+
export type SignInMethod = (SignInMethodBase & {
|
|
95
|
+
href: string;
|
|
96
|
+
onSelect?: never;
|
|
97
|
+
}) | (SignInMethodBase & {
|
|
98
|
+
href?: never;
|
|
99
|
+
onSelect: (carried: Readonly<Record<string, string>>) => void;
|
|
100
|
+
});
|
|
101
|
+
export interface SignInOptionsProps {
|
|
102
|
+
/** Groups of methods. The divider renders between groups, never inside one. */
|
|
103
|
+
methods: ReadonlyArray<readonly SignInMethod[]>;
|
|
104
|
+
/**
|
|
105
|
+
* What every method must carry through sign-in — `return_to`, `arrival`,
|
|
106
|
+
* device `code`, `source`, whatever the page cannot afford to lose. Entries
|
|
107
|
+
* that are `null`, `undefined` or empty are dropped, so query params can be
|
|
108
|
+
* passed straight through. Appended to link methods' `href`; handed to
|
|
109
|
+
* action methods' `onSelect`.
|
|
110
|
+
*/
|
|
111
|
+
carry?: Readonly<Record<string, string | null | undefined>>;
|
|
112
|
+
/** Replaces the "OR" between groups. The rule itself stays. */
|
|
113
|
+
divider?: React.ReactNode;
|
|
114
|
+
/**
|
|
115
|
+
* Renders the anchor for link methods, for consumers with a framework link —
|
|
116
|
+
* `(p) => <Link {...p} />`. Defaults to a plain `<a>`, which works
|
|
117
|
+
* everywhere; the framework link is an optimisation, not a requirement.
|
|
118
|
+
*/
|
|
119
|
+
renderLink?: (props: {
|
|
120
|
+
href: string;
|
|
121
|
+
}) => React.ReactElement;
|
|
122
|
+
className?: string;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* One full-width method row: the library `Button` (`variant="outline"` — white
|
|
126
|
+
* fill, hairline border, ink label) sized up to the ~52px onboarding rhythm.
|
|
127
|
+
* The icon sits at the inline start, positioned absolute so the label stays
|
|
128
|
+
* centered rather than being pushed off-center.
|
|
129
|
+
*
|
|
130
|
+
* Exported so a surface composing its own screen (the console's styleguide
|
|
131
|
+
* pilot) renders the same row `SignInOptions` does, instead of keeping a
|
|
132
|
+
* private twin. Every remaining prop passes through to `Button`.
|
|
133
|
+
*/
|
|
134
|
+
export declare function SignInMethodButton({ icon, label, className, ...props }: Omit<React.ComponentProps<typeof Button>, "children"> & {
|
|
135
|
+
icon?: React.ReactNode;
|
|
136
|
+
label: string;
|
|
137
|
+
}): React.JSX.Element;
|
|
138
|
+
export declare function SignInOptions({ methods, carry, divider, renderLink, className, }: SignInOptionsProps): React.JSX.Element;
|
|
139
|
+
export {};
|
|
140
|
+
//# sourceMappingURL=sign-in-options.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sign-in-options.d.ts","sourceRoot":"","sources":["../../src/blocks/sign-in-options.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAI7C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuEG;AAEH,UAAU,gBAAgB;IACxB,iEAAiE;IACjE,EAAE,EAAE,MAAM,CAAA;IACV,8DAA8D;IAC9D,KAAK,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GACpB,CAAC,gBAAgB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC,GACvD,CAAC,gBAAgB,GAAG;IAClB,IAAI,CAAC,EAAE,KAAK,CAAA;IACZ,QAAQ,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,KAAK,IAAI,CAAA;CAC9D,CAAC,CAAA;AAEN,MAAM,WAAW,kBAAkB;IACjC,+EAA+E;IAC/E,OAAO,EAAE,aAAa,CAAC,SAAS,YAAY,EAAE,CAAC,CAAA;IAC/C;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC,CAAA;IAC3D,+DAA+D;IAC/D,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,KAAK,CAAC,YAAY,CAAA;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAuBD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,KAAK,EACL,SAAS,EACT,GAAG,KAAK,EACT,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,EAAE,UAAU,CAAC,GAAG;IACzD,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,KAAK,EAAE,MAAM,CAAA;CACd,qBAsBA;AAmBD,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,KAAK,EACL,OAAO,EACP,UAAU,EACV,SAAS,GACV,EAAE,kBAAkB,qBAoDpB"}
|
|
@@ -0,0 +1,57 @@
|
|
|
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 { Separator } from "../ui/separator.js";
|
|
6
|
+
import { cn } from "../lib/cloudgrid-cn.js";
|
|
7
|
+
/** `carry` with the empty entries dropped, in declaration order. */
|
|
8
|
+
function carriedEntries(carry) {
|
|
9
|
+
if (!carry)
|
|
10
|
+
return [];
|
|
11
|
+
return Object.entries(carry).filter((entry) => typeof entry[1] === "string" && entry[1] !== "");
|
|
12
|
+
}
|
|
13
|
+
/** `href` with the carried entries appended, hash kept at the end. */
|
|
14
|
+
function withCarried(href, entries) {
|
|
15
|
+
if (entries.length === 0)
|
|
16
|
+
return href;
|
|
17
|
+
const hashAt = href.indexOf("#");
|
|
18
|
+
const base = hashAt === -1 ? href : href.slice(0, hashAt);
|
|
19
|
+
const hash = hashAt === -1 ? "" : href.slice(hashAt);
|
|
20
|
+
const joined = new URLSearchParams(entries).toString();
|
|
21
|
+
return `${base}${base.includes("?") ? "&" : "?"}${joined}${hash}`;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* One full-width method row: the library `Button` (`variant="outline"` — white
|
|
25
|
+
* fill, hairline border, ink label) sized up to the ~52px onboarding rhythm.
|
|
26
|
+
* The icon sits at the inline start, positioned absolute so the label stays
|
|
27
|
+
* centered rather than being pushed off-center.
|
|
28
|
+
*
|
|
29
|
+
* Exported so a surface composing its own screen (the console's styleguide
|
|
30
|
+
* pilot) renders the same row `SignInOptions` does, instead of keeping a
|
|
31
|
+
* private twin. Every remaining prop passes through to `Button`.
|
|
32
|
+
*/
|
|
33
|
+
export function SignInMethodButton({ icon, label, className, ...props }) {
|
|
34
|
+
return (_jsxs(Button, { variant: "outline", "data-slot": "sign-in-method", className: cn("relative h-[52px] w-full justify-center rounded-xl text-body-md font-medium", className), ...props, children: [icon == null ? null : (_jsx("span", { "aria-hidden": "true", className: "absolute left-4 flex size-5 items-center justify-center [&_svg]:size-5", children: icon })), label] }));
|
|
35
|
+
}
|
|
36
|
+
/** The "OR" rule between method groups. Decorative, and hidden as such. */
|
|
37
|
+
function SignInOptionsDivider({ children }) {
|
|
38
|
+
return (_jsxs("div", { "data-slot": "sign-in-options-divider", "aria-hidden": "true", className: "my-5 flex items-center gap-3", children: [_jsx(Separator, { className: "flex-1" }), _jsx("span", { className: "text-caption font-medium text-faint", children: children ?? "OR" }), _jsx(Separator, { className: "flex-1" })] }));
|
|
39
|
+
}
|
|
40
|
+
export function SignInOptions({ methods, carry, divider, renderLink, className, }) {
|
|
41
|
+
const entries = carriedEntries(carry);
|
|
42
|
+
const carried = Object.fromEntries(entries);
|
|
43
|
+
const groups = methods.filter((group) => group.length > 0);
|
|
44
|
+
return (_jsx("div", { "data-slot": "sign-in-options", className: className, children: groups.map((group, index) => (
|
|
45
|
+
// Groups have no identity of their own — position is what they mean.
|
|
46
|
+
// eslint-disable-next-line react/no-array-index-key
|
|
47
|
+
_jsxs(React.Fragment, { children: [index > 0 && _jsx(SignInOptionsDivider, { children: divider }), _jsx("div", { className: "grid gap-3", children: group.map((method) => method.href !== undefined ? (
|
|
48
|
+
/*
|
|
49
|
+
A real anchor rather than an onClick. Base UI's Button warns
|
|
50
|
+
when the rendered element is not a <button>, hence
|
|
51
|
+
`nativeButton={false}`, and it stamps `role="button"` on a
|
|
52
|
+
non-native target — put back to `link`, because this one
|
|
53
|
+
really does navigate.
|
|
54
|
+
*/
|
|
55
|
+
_jsx(SignInMethodButton, { "data-method": method.id, render: renderLink?.({ href: withCarried(method.href, entries) }) ?? (_jsx("a", { href: withCarried(method.href, entries) })), nativeButton: false, role: "link", icon: method.icon, label: method.label, disabled: method.disabled }, method.id)) : (_jsx(SignInMethodButton, { "data-method": method.id, icon: method.icon, label: method.label, disabled: method.disabled, onClick: () => method.onSelect(carried) }, method.id))) })] }, index))) }));
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=sign-in-options.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sign-in-options.js","sourceRoot":"","sources":["../../src/blocks/sign-in-options.tsx"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -42,6 +42,7 @@ export * from "./blocks/pricing-tier-cards.js";
|
|
|
42
42
|
export * from "./blocks/pricing-tiers.js";
|
|
43
43
|
export * from "./blocks/quote.js";
|
|
44
44
|
export * from "./blocks/rich-text.js";
|
|
45
|
+
export * from "./blocks/sign-in-options.js";
|
|
45
46
|
export * from "./blocks/stage-panel.js";
|
|
46
47
|
export * from "./blocks/step-item.js";
|
|
47
48
|
export * from "./blocks/tool-logo.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,4BAA4B,CAAA;AAC1C,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"}
|
|
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,4BAA4B,CAAA;AAC1C,cAAc,yBAAyB,CAAA;AACvC,cAAc,gCAAgC,CAAA;AAC9C,cAAc,2BAA2B,CAAA;AACzC,cAAc,mBAAmB,CAAA;AACjC,cAAc,uBAAuB,CAAA;AACrC,cAAc,6BAA6B,CAAA;AAC3C,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
|
@@ -59,6 +59,7 @@ export * from "./blocks/pricing-tier-cards.js";
|
|
|
59
59
|
export * from "./blocks/pricing-tiers.js";
|
|
60
60
|
export * from "./blocks/quote.js";
|
|
61
61
|
export * from "./blocks/rich-text.js";
|
|
62
|
+
export * from "./blocks/sign-in-options.js";
|
|
62
63
|
export * from "./blocks/stage-panel.js";
|
|
63
64
|
export * from "./blocks/step-item.js";
|
|
64
65
|
export * from "./blocks/tool-logo.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,4BAA4B,CAAA;AAC1C,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"}
|
|
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,4BAA4B,CAAA;AAC1C,cAAc,yBAAyB,CAAA;AACvC,cAAc,gCAAgC,CAAA;AAC9C,cAAc,2BAA2B,CAAA;AACzC,cAAc,mBAAmB,CAAA;AACjC,cAAc,uBAAuB,CAAA;AACrC,cAAc,6BAA6B,CAAA;AAC3C,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
|
@@ -66,6 +66,10 @@
|
|
|
66
66
|
--color-page: var(--cg-surface-page);
|
|
67
67
|
--color-page-alt: var(--cg-surface-page-alt);
|
|
68
68
|
--color-subtle: var(--cg-surface-subtle);
|
|
69
|
+
/* The neutral raised ground (#151). `subtle` is the lavender tint and `card`
|
|
70
|
+
is white, so before this there was no name for "one step off white, no
|
|
71
|
+
hue" and a raised panel had to flatten or take a cast. */
|
|
72
|
+
--color-raised: var(--cg-surface-raised);
|
|
69
73
|
--color-control: var(--cg-surface-control);
|
|
70
74
|
--color-panel: var(--cg-surface-panel);
|
|
71
75
|
--color-code: var(--cg-surface-code);
|
|
@@ -137,6 +141,24 @@
|
|
|
137
141
|
--color-expired-fill: var(--cg-expired-fill);
|
|
138
142
|
--color-danger-fill: var(--cg-danger-fill);
|
|
139
143
|
|
|
144
|
+
/* The other two rungs of the chip (#149, #150). `-fill` is the ground, the
|
|
145
|
+
bare name is the dot, `-border` is the hairline and `-strong` is the word.
|
|
146
|
+
Mapped in the same pass as the tokens, for the reason the block above
|
|
147
|
+
gives: a token whose utility lands a release later is a token the first
|
|
148
|
+
consumer reaches for and does not find.
|
|
149
|
+
|
|
150
|
+
There is no --color-danger-border or --color-unhealthy-border, because
|
|
151
|
+
there is no such token — the same shape as --color-offline-fill, and the
|
|
152
|
+
`state` group's own note carries the reason. */
|
|
153
|
+
--color-online-border: var(--cg-online-border);
|
|
154
|
+
--color-online-strong: var(--cg-online-strong);
|
|
155
|
+
--color-expired-border: var(--cg-expired-border);
|
|
156
|
+
--color-expired-strong: var(--cg-expired-strong);
|
|
157
|
+
--color-pending-border: var(--cg-pending-border);
|
|
158
|
+
--color-pending-strong: var(--cg-pending-strong);
|
|
159
|
+
--color-unhealthy-strong: var(--cg-unhealthy-strong);
|
|
160
|
+
--color-danger-strong: var(--cg-danger-strong);
|
|
161
|
+
|
|
140
162
|
/* ---- lines ---- */
|
|
141
163
|
--color-hairline: var(--cg-border-hairline);
|
|
142
164
|
--color-line: var(--cg-border-control);
|
package/dist/tokens/base.json
CHANGED
|
@@ -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.\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.",
|
|
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.\n\nTHREE RUNGS PER STATUS, from 2026-09-04 (#149, #150). A chip is a fill, a\nhairline around it, a dot and a word, and until now the set carried only the\nfirst two of those four. -border is the hairline and -strong is the word; the\nbare name stays the dot it has always been.\n\n <status>-fill the chip ground\n <status>-border the hairline around it, a pale tint\n <status> the dot. An indicator, so 3:1, never text\n <status>-strong the word on the fill. Text, so 4.5:1\n\nThe values are the Console's, measured and shipped, not new ones: #149 asked\nfor the rung and console#579 had already been forced to leave two bands as\nliterals for the want of it. Only unhealthy-strong and danger-strong are\nderived, each for a stated reason in its own note.\n\nNo --cg-danger-border and no --cg-unhealthy-border. Neither has ever been drawn\nby any surface — StatusChip's error state has no edge and unhealthy has no band\nat all — so a value here would be invented to complete a grid rather than to\nserve a chip. Same call as --cg-offline-fill above, and the same remedy: add\nthem when something needs them, with the number that made the choice.\n\noffline has no -border and no -strong either, and for the reason it has no\nfill: an unplugged chip sits on the card ground with border-control, and its\nword is --cg-text-primary. Absence of state still does not need a colour.\n\nOn the panel and on ink, -border resolves to the surface's own hairline and\n-strong to its own primary text. A dark ground has no darker rung to reach for,\nand the light tints these scopes carry measure 3.03:1 to 3.60:1 on the panel's\nchip fill, which is a dot and not a word. Worth knowing before retuning: on ink\nthe tints DO clear 4.5:1 on the ink fill (5.48:1 to 8.92:1), so a designer who\nwants the word tinted there changes five entries in surface-ink.json. On the\npanel it is not available at any value — white is 4.31:1 on that fill and white\nis the lightest colour there is, so AA on a panel chip needs the FILL to move.",
|
|
56
56
|
"online": {
|
|
57
57
|
"$value": "#00a05c",
|
|
58
58
|
"$type": "color",
|
|
@@ -149,6 +149,84 @@
|
|
|
149
149
|
"css": "--cg-danger-fill"
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
|
+
},
|
|
153
|
+
"online-border": {
|
|
154
|
+
"$value": "#abefc6",
|
|
155
|
+
"$type": "color",
|
|
156
|
+
"$description": "The hairline around an online chip. From the Console's OK band, where it has\nshipped as #abefc6 on #ecfdf3 (console#579).\n\nIts own rung rather than the tone, and console#579 is why. Both systems carried\n{tone, fill} pairs and no third value, so converting the band meant sending the\nborder AND the word to --cg-online. Measured, that is not a repaint: the word\nfell from 7.55:1 to 3.06:1 and the border went from this pale tint to full\nsaturation. The band stayed a literal instead. This token is what lets it move.\n\n1.19:1 against --cg-online-fill. A hairline is not text and not an indicator, so\nthere is no floor it has to clear; what it has to do is stay pale, which the\ncontrast guardrail holds with a ceiling rather than a floor.",
|
|
157
|
+
"$extensions": {
|
|
158
|
+
"io.cloudgrid": {
|
|
159
|
+
"css": "--cg-online-border"
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
"expired-border": {
|
|
164
|
+
"$value": "#fedf89",
|
|
165
|
+
"$type": "color",
|
|
166
|
+
"$description": "The hairline around an expired or pending chip. From the Console's WARN band,\nwhere it has shipped as #fedf89 on #fffaeb (console#579).\n\n1.18:1 against --cg-expired-fill. Same rung and same reasoning as\n--cg-online-border: collapsing it onto --cg-expired measured 3.99:1, which is\nthe tone doing the border's job.",
|
|
167
|
+
"$extensions": {
|
|
168
|
+
"io.cloudgrid": {
|
|
169
|
+
"css": "--cg-expired-border"
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
"pending-border": {
|
|
174
|
+
"$value": "{state.expired-border}",
|
|
175
|
+
"$description": "The hairline around a pending chip, aliased to expired's for the same reason the\ntone and the fill are. Splitting pending from expired later means editing the\nfour pending entries in this file and nothing else.",
|
|
176
|
+
"$extensions": {
|
|
177
|
+
"io.cloudgrid": {
|
|
178
|
+
"css": "--cg-pending-border"
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
"online-strong": {
|
|
183
|
+
"$value": "#085d3a",
|
|
184
|
+
"$type": "color",
|
|
185
|
+
"$description": "The WORD on an online chip, and online as running text anywhere. From the\nConsole's OK band. 7.17:1 on --cg-online-fill and 7.96:1 on white.\n\nThis is the rung EntityStatus said the library lacked. --cg-online is 3.40:1 on\nwhite and 3.06:1 on its own fill, which is an indicator and not a foreground, so\nthe component paints the dot with the tone and the word with --cg-text-primary,\nand its own note says a state foreground is a brand decision rather than a\ncomponent's to take. This is that decision, made with a value the Console has\nalready shipped rather than a new one.",
|
|
186
|
+
"$extensions": {
|
|
187
|
+
"io.cloudgrid": {
|
|
188
|
+
"css": "--cg-online-strong"
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
"expired-strong": {
|
|
193
|
+
"$value": "#7a3d0a",
|
|
194
|
+
"$type": "color",
|
|
195
|
+
"$description": "The word on an expired chip, and expired as running text. From the Console's\nWARN band. 7.63:1 on --cg-expired-fill and 8.40:1 on white.",
|
|
196
|
+
"$extensions": {
|
|
197
|
+
"io.cloudgrid": {
|
|
198
|
+
"css": "--cg-expired-strong"
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
"pending-strong": {
|
|
203
|
+
"$value": "{state.expired-strong}",
|
|
204
|
+
"$description": "The word on a pending chip. Aliased to expired's, like every other pending rung.",
|
|
205
|
+
"$extensions": {
|
|
206
|
+
"io.cloudgrid": {
|
|
207
|
+
"css": "--cg-pending-strong"
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
"unhealthy-strong": {
|
|
212
|
+
"$value": "#5d5401",
|
|
213
|
+
"$type": "color",
|
|
214
|
+
"$description": "The word on an unhealthy chip. 7.10:1 on --cg-unhealthy-fill and 7.68:1 on\nwhite.\n\nDERIVED, not adopted. unhealthy is the one tone here the Console never had, so\nthere is no shipped literal to take. This is --cg-unhealthy darkened along its\nown hue and saturation until it reaches the band the two adopted words already\noccupy — 7.17:1 for online and 7.63:1 for expired on their own fills — rather\nthan stopped at the first value that clears 4.5:1, so the words read as one set\ninstead of three matched and one nearly failing.\n\nThe tone is not simply reused because it does not clear the bar: --cg-unhealthy\nmeasures 4.46:1 on this fill. Four hundredths under the floor is under it.\n\ndE2000 to its neighbours in this rung: 20.6 from expired-strong, 23.1 from\nonline-strong. The tightest pair in the whole rung is expired-strong against\ndanger-strong at 19.8, above the 16.5 floor this palette was measured against.",
|
|
215
|
+
"$extensions": {
|
|
216
|
+
"io.cloudgrid": {
|
|
217
|
+
"css": "--cg-unhealthy-strong"
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
"danger-strong": {
|
|
222
|
+
"$value": "#c42828",
|
|
223
|
+
"$type": "color",
|
|
224
|
+
"$description": "The word on a danger chip, and danger as running text. 5.02:1 on\n--cg-danger-fill and 5.70:1 on white.\n\n--cg-danger itself is #E53935, which is 4.23:1 on white and 3.72:1 on its own\nfill. It passes as a fill, a border or an icon, where the bar is 3:1, and fails\nas a sentence. The token is deliberately NOT moved to fix that: every fill,\nborder and icon already consuming it would repaint, and the error is that danger\nhad no word rung, not that its tone is wrong.\n\nThe Console's retired --color-error #d42b2b is where this starts and is not\nwhere it lands. It is 5.02:1 on white, but 4.42:1 on --cg-danger-fill — still\nunder the floor on the exact chip that needs it. This is that red darkened along\nits own hue and saturation until it clears 5:1 on the fill, which is the\nsmallest move that holds on both grounds.\n\nKept lighter than the other three words on purpose. It is already the closest\npair in the rung, dE2000 19.8 from expired-strong, and darkening it into their\n7:1 band closes that to 16.6 — a failed chip and an expiring one should not\nconverge.",
|
|
225
|
+
"$extensions": {
|
|
226
|
+
"io.cloudgrid": {
|
|
227
|
+
"css": "--cg-danger-strong"
|
|
228
|
+
}
|
|
229
|
+
}
|
|
152
230
|
}
|
|
153
231
|
},
|
|
154
232
|
"brand-derived": {
|
|
@@ -210,6 +288,16 @@
|
|
|
210
288
|
}
|
|
211
289
|
}
|
|
212
290
|
},
|
|
291
|
+
"neutral-50": {
|
|
292
|
+
"$value": "#f4f6f8",
|
|
293
|
+
"$type": "color",
|
|
294
|
+
"$description": "SOURCE: the Console's --color-canvas, the recessed ground behind a card.\n\nNot a pure grey. It carries a slight blue cast where the rest of this ramp is\nneutral, and that is recorded rather than corrected because the value is what\nlegacy raised panels already render and matching them is the point (#151).",
|
|
295
|
+
"$extensions": {
|
|
296
|
+
"io.cloudgrid": {
|
|
297
|
+
"css": "--cg-neutral-50"
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
},
|
|
213
301
|
"neutral-100": {
|
|
214
302
|
"$value": "#f1f1f1",
|
|
215
303
|
"$type": "color",
|
|
@@ -304,6 +392,15 @@
|
|
|
304
392
|
}
|
|
305
393
|
}
|
|
306
394
|
},
|
|
395
|
+
"surface-raised": {
|
|
396
|
+
"$value": "{neutrals.neutral-50}",
|
|
397
|
+
"$description": "A neutral ground one step off white, for a panel that should read as lifted off\nthe page rather than flat on it.\n\n#151: --cg-surface-card is #ffffff and --cg-surface-subtle is the lavender tint,\nso a raised panel had no value-preserving target and every consumer faced the\nsame three-way choice with no right answer — flatten it to white, tint it\nlavender, or pin a literal. This is the fourth answer, and it is the Console's\nown #f4f6f8 rather than a new value.\n\nDeclared at :root only, like --cg-surface-subtle and --cg-surface-control. The\nneutral grounds do not flip: inside a purple panel or on ink, depth is carried\nby --cg-surface-card and the hairline instead. Making it flip is one entry in\neach of the two surface sets.",
|
|
398
|
+
"$extensions": {
|
|
399
|
+
"io.cloudgrid": {
|
|
400
|
+
"css": "--cg-surface-raised"
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
},
|
|
307
404
|
"surface-control": {
|
|
308
405
|
"$value": "{neutrals.neutral-200}",
|
|
309
406
|
"$extensions": {
|
|
@@ -1164,23 +1261,13 @@
|
|
|
1164
1261
|
"volt": {
|
|
1165
1262
|
"$value": "#b6ff2e",
|
|
1166
1263
|
"$type": "color",
|
|
1167
|
-
"$description": "The live/current INDICATOR — the dot in its halo, and the brand mark. Never\ndecorative, never a fill.\n\nMEASURED 1.21:1 on white, which is below every bar there is. It works on the\npanel (4.82:1) and on ink (13.13:1); on the page ground it reads only because\nit is paired with
|
|
1264
|
+
"$description": "The live/current INDICATOR — the dot in its halo, and the brand mark. Never\ndecorative, never a fill.\n\nMEASURED 1.21:1 on white, which is below every bar there is. It works on the\npanel (4.82:1) and on ink (13.13:1); on the page ground it reads only because\nit is paired with volt-ink in a pill. Recorded rather than\nsilently corrected: darkening it would change the Console's appearance, which\nis a design decision and not a contrast fix.\n\nNote it is a SECOND lime beside --cg-secondary (#b6de68), 36 dE away. Which one\nthe brand owns is the open question here.\n\nIt is also a second LIVE. state.online (#00a05c) is the same idea in the state\nscale, and EntityStatus (#116) already paints the live dot with it. They are not\ninterchangeable at any bar — online is 3.40:1 on white, volt is 1.21:1 — so a\nsurface must not swap one for the other on the assumption that both mean\n'serving'. Which name the brand keeps for live belongs to the same open question,\nand until it is answered volt is the Console's brand mark and online is the\nentity's state.",
|
|
1168
1265
|
"$extensions": {
|
|
1169
1266
|
"io.cloudgrid": {
|
|
1170
1267
|
"css": "--cg-volt"
|
|
1171
1268
|
}
|
|
1172
1269
|
}
|
|
1173
1270
|
},
|
|
1174
|
-
"volt-ring": {
|
|
1175
|
-
"$value": "rgba(182, 255, 46, 0.32)",
|
|
1176
|
-
"$type": "color",
|
|
1177
|
-
"$description": "The halo behind a volt dot. Its job is to make the dot findable on a light ground, which is what carries the 1.21:1 above.",
|
|
1178
|
-
"$extensions": {
|
|
1179
|
-
"io.cloudgrid": {
|
|
1180
|
-
"css": "--cg-volt-ring"
|
|
1181
|
-
}
|
|
1182
|
-
}
|
|
1183
|
-
},
|
|
1184
1271
|
"volt-ink": {
|
|
1185
1272
|
"$value": "#243d00",
|
|
1186
1273
|
"$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. 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.",
|
|
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.\n\nThe -border and -strong rungs (#149, #150) are the exception to \"measured per surface\": they resolve to this surface's own hairline and its own primary text rather than to a tint of the tone. A dark ground has no darker rung, and the tints above are tuned to be dots — on the panel's chip fill they measure 3.03:1 to 3.60:1, which is an indicator and not a word. On ink they DO clear 4.5:1 on the ink fill (5.48:1 to 8.92:1), so tinting the word there is available and is five edits in this file; on the panel it is not available at any value, because white measures 4.31:1 on that fill and white is the lightest colour there is.",
|
|
138
138
|
"online": {
|
|
139
139
|
"$value": "#5FD99A",
|
|
140
140
|
"$type": "color",
|
|
@@ -232,6 +232,70 @@
|
|
|
232
232
|
"css": "--cg-danger-fill"
|
|
233
233
|
}
|
|
234
234
|
}
|
|
235
|
+
},
|
|
236
|
+
"online-border": {
|
|
237
|
+
"$value": "{semantic-lines.border-hairline}",
|
|
238
|
+
"$extensions": {
|
|
239
|
+
"io.cloudgrid": {
|
|
240
|
+
"css": "--cg-online-border"
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
"expired-border": {
|
|
245
|
+
"$value": "{semantic-lines.border-hairline}",
|
|
246
|
+
"$extensions": {
|
|
247
|
+
"io.cloudgrid": {
|
|
248
|
+
"css": "--cg-expired-border"
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
"pending-border": {
|
|
253
|
+
"$value": "{state.expired-border}",
|
|
254
|
+
"$extensions": {
|
|
255
|
+
"io.cloudgrid": {
|
|
256
|
+
"css": "--cg-pending-border"
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
"online-strong": {
|
|
261
|
+
"$value": "{semantic-text.text-primary}",
|
|
262
|
+
"$extensions": {
|
|
263
|
+
"io.cloudgrid": {
|
|
264
|
+
"css": "--cg-online-strong"
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
},
|
|
268
|
+
"expired-strong": {
|
|
269
|
+
"$value": "{semantic-text.text-primary}",
|
|
270
|
+
"$extensions": {
|
|
271
|
+
"io.cloudgrid": {
|
|
272
|
+
"css": "--cg-expired-strong"
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
"pending-strong": {
|
|
277
|
+
"$value": "{state.expired-strong}",
|
|
278
|
+
"$extensions": {
|
|
279
|
+
"io.cloudgrid": {
|
|
280
|
+
"css": "--cg-pending-strong"
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
"unhealthy-strong": {
|
|
285
|
+
"$value": "{semantic-text.text-primary}",
|
|
286
|
+
"$extensions": {
|
|
287
|
+
"io.cloudgrid": {
|
|
288
|
+
"css": "--cg-unhealthy-strong"
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
"danger-strong": {
|
|
293
|
+
"$value": "{semantic-text.text-primary}",
|
|
294
|
+
"$extensions": {
|
|
295
|
+
"io.cloudgrid": {
|
|
296
|
+
"css": "--cg-danger-strong"
|
|
297
|
+
}
|
|
298
|
+
}
|
|
235
299
|
}
|
|
236
300
|
},
|
|
237
301
|
"indicator": {
|
|
@@ -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. 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.",
|
|
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.\n\nThe -border and -strong rungs (#149, #150) are the exception to \"measured per surface\": they resolve to this surface's own hairline and its own primary text rather than to a tint of the tone. A dark ground has no darker rung, and the tints above are tuned to be dots — on the panel's chip fill they measure 3.03:1 to 3.60:1, which is an indicator and not a word. On ink they DO clear 4.5:1 on the ink fill (5.48:1 to 8.92:1), so tinting the word there is available and is five edits in this file; on the panel it is not available at any value, because white measures 4.31:1 on that fill and white is the lightest colour there is.",
|
|
122
122
|
"online": {
|
|
123
123
|
"$value": "#C8F5DC",
|
|
124
124
|
"$type": "color",
|
|
@@ -216,6 +216,70 @@
|
|
|
216
216
|
"css": "--cg-danger-fill"
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
|
+
},
|
|
220
|
+
"online-border": {
|
|
221
|
+
"$value": "{semantic-lines.border-hairline}",
|
|
222
|
+
"$extensions": {
|
|
223
|
+
"io.cloudgrid": {
|
|
224
|
+
"css": "--cg-online-border"
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
"expired-border": {
|
|
229
|
+
"$value": "{semantic-lines.border-hairline}",
|
|
230
|
+
"$extensions": {
|
|
231
|
+
"io.cloudgrid": {
|
|
232
|
+
"css": "--cg-expired-border"
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
"pending-border": {
|
|
237
|
+
"$value": "{state.expired-border}",
|
|
238
|
+
"$extensions": {
|
|
239
|
+
"io.cloudgrid": {
|
|
240
|
+
"css": "--cg-pending-border"
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
"online-strong": {
|
|
245
|
+
"$value": "{semantic-text.text-primary}",
|
|
246
|
+
"$extensions": {
|
|
247
|
+
"io.cloudgrid": {
|
|
248
|
+
"css": "--cg-online-strong"
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
"expired-strong": {
|
|
253
|
+
"$value": "{semantic-text.text-primary}",
|
|
254
|
+
"$extensions": {
|
|
255
|
+
"io.cloudgrid": {
|
|
256
|
+
"css": "--cg-expired-strong"
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
"pending-strong": {
|
|
261
|
+
"$value": "{state.expired-strong}",
|
|
262
|
+
"$extensions": {
|
|
263
|
+
"io.cloudgrid": {
|
|
264
|
+
"css": "--cg-pending-strong"
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
},
|
|
268
|
+
"unhealthy-strong": {
|
|
269
|
+
"$value": "{semantic-text.text-primary}",
|
|
270
|
+
"$extensions": {
|
|
271
|
+
"io.cloudgrid": {
|
|
272
|
+
"css": "--cg-unhealthy-strong"
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
"danger-strong": {
|
|
277
|
+
"$value": "{semantic-text.text-primary}",
|
|
278
|
+
"$extensions": {
|
|
279
|
+
"io.cloudgrid": {
|
|
280
|
+
"css": "--cg-danger-strong"
|
|
281
|
+
}
|
|
282
|
+
}
|
|
219
283
|
}
|
|
220
284
|
},
|
|
221
285
|
"indicator": {
|
package/dist/tokens.css
CHANGED
|
@@ -107,7 +107,41 @@
|
|
|
107
107
|
brighter gold on the same ramp as the amber rather than a new hue family,
|
|
108
108
|
because green/amber/red is the traffic light an operator already reads and
|
|
109
109
|
'serving but degraded' is its middle rung. Its own note carries the numbers
|
|
110
|
-
and the three families that were measured and rejected.
|
|
110
|
+
and the three families that were measured and rejected.
|
|
111
|
+
|
|
112
|
+
THREE RUNGS PER STATUS, from 2026-09-04 (#149, #150). A chip is a fill, a
|
|
113
|
+
hairline around it, a dot and a word, and until now the set carried only the
|
|
114
|
+
first two of those four. -border is the hairline and -strong is the word; the
|
|
115
|
+
bare name stays the dot it has always been.
|
|
116
|
+
|
|
117
|
+
<status>-fill the chip ground
|
|
118
|
+
<status>-border the hairline around it, a pale tint
|
|
119
|
+
<status> the dot. An indicator, so 3:1, never text
|
|
120
|
+
<status>-strong the word on the fill. Text, so 4.5:1
|
|
121
|
+
|
|
122
|
+
The values are the Console's, measured and shipped, not new ones: #149 asked
|
|
123
|
+
for the rung and console#579 had already been forced to leave two bands as
|
|
124
|
+
literals for the want of it. Only unhealthy-strong and danger-strong are
|
|
125
|
+
derived, each for a stated reason in its own note.
|
|
126
|
+
|
|
127
|
+
No --cg-danger-border and no --cg-unhealthy-border. Neither has ever been drawn
|
|
128
|
+
by any surface — StatusChip's error state has no edge and unhealthy has no band
|
|
129
|
+
at all — so a value here would be invented to complete a grid rather than to
|
|
130
|
+
serve a chip. Same call as --cg-offline-fill above, and the same remedy: add
|
|
131
|
+
them when something needs them, with the number that made the choice.
|
|
132
|
+
|
|
133
|
+
offline has no -border and no -strong either, and for the reason it has no
|
|
134
|
+
fill: an unplugged chip sits on the card ground with border-control, and its
|
|
135
|
+
word is --cg-text-primary. Absence of state still does not need a colour.
|
|
136
|
+
|
|
137
|
+
On the panel and on ink, -border resolves to the surface's own hairline and
|
|
138
|
+
-strong to its own primary text. A dark ground has no darker rung to reach for,
|
|
139
|
+
and the light tints these scopes carry measure 3.03:1 to 3.60:1 on the panel's
|
|
140
|
+
chip fill, which is a dot and not a word. Worth knowing before retuning: on ink
|
|
141
|
+
the tints DO clear 4.5:1 on the ink fill (5.48:1 to 8.92:1), so a designer who
|
|
142
|
+
wants the word tinted there changes five entries in surface-ink.json. On the
|
|
143
|
+
panel it is not available at any value — white is 4.31:1 on that fill and white
|
|
144
|
+
is the lightest colour there is, so AA on a panel chip needs the FILL to move. */
|
|
111
145
|
/*
|
|
112
146
|
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. */
|
|
113
147
|
--cg-online: #00a05c;
|
|
@@ -162,6 +196,90 @@
|
|
|
162
196
|
/*
|
|
163
197
|
From the Console's --color-error-fill. --cg-danger measures 3.72:1 on it. danger had no fill before; a chip needs one. */
|
|
164
198
|
--cg-danger-fill: #fceded;
|
|
199
|
+
/*
|
|
200
|
+
The hairline around an online chip. From the Console's OK band, where it has
|
|
201
|
+
shipped as #abefc6 on #ecfdf3 (console#579).
|
|
202
|
+
|
|
203
|
+
Its own rung rather than the tone, and console#579 is why. Both systems carried
|
|
204
|
+
{tone, fill} pairs and no third value, so converting the band meant sending the
|
|
205
|
+
border AND the word to --cg-online. Measured, that is not a repaint: the word
|
|
206
|
+
fell from 7.55:1 to 3.06:1 and the border went from this pale tint to full
|
|
207
|
+
saturation. The band stayed a literal instead. This token is what lets it move.
|
|
208
|
+
|
|
209
|
+
1.19:1 against --cg-online-fill. A hairline is not text and not an indicator, so
|
|
210
|
+
there is no floor it has to clear; what it has to do is stay pale, which the
|
|
211
|
+
contrast guardrail holds with a ceiling rather than a floor. */
|
|
212
|
+
--cg-online-border: #abefc6;
|
|
213
|
+
/*
|
|
214
|
+
The hairline around an expired or pending chip. From the Console's WARN band,
|
|
215
|
+
where it has shipped as #fedf89 on #fffaeb (console#579).
|
|
216
|
+
|
|
217
|
+
1.18:1 against --cg-expired-fill. Same rung and same reasoning as
|
|
218
|
+
--cg-online-border: collapsing it onto --cg-expired measured 3.99:1, which is
|
|
219
|
+
the tone doing the border's job. */
|
|
220
|
+
--cg-expired-border: #fedf89;
|
|
221
|
+
/*
|
|
222
|
+
The hairline around a pending chip, aliased to expired's for the same reason the
|
|
223
|
+
tone and the fill are. Splitting pending from expired later means editing the
|
|
224
|
+
four pending entries in this file and nothing else. */
|
|
225
|
+
--cg-pending-border: var(--cg-expired-border);
|
|
226
|
+
/*
|
|
227
|
+
The WORD on an online chip, and online as running text anywhere. From the
|
|
228
|
+
Console's OK band. 7.17:1 on --cg-online-fill and 7.96:1 on white.
|
|
229
|
+
|
|
230
|
+
This is the rung EntityStatus said the library lacked. --cg-online is 3.40:1 on
|
|
231
|
+
white and 3.06:1 on its own fill, which is an indicator and not a foreground, so
|
|
232
|
+
the component paints the dot with the tone and the word with --cg-text-primary,
|
|
233
|
+
and its own note says a state foreground is a brand decision rather than a
|
|
234
|
+
component's to take. This is that decision, made with a value the Console has
|
|
235
|
+
already shipped rather than a new one. */
|
|
236
|
+
--cg-online-strong: #085d3a;
|
|
237
|
+
/*
|
|
238
|
+
The word on an expired chip, and expired as running text. From the Console's
|
|
239
|
+
WARN band. 7.63:1 on --cg-expired-fill and 8.40:1 on white. */
|
|
240
|
+
--cg-expired-strong: #7a3d0a;
|
|
241
|
+
/*
|
|
242
|
+
The word on a pending chip. Aliased to expired's, like every other pending rung. */
|
|
243
|
+
--cg-pending-strong: var(--cg-expired-strong);
|
|
244
|
+
/*
|
|
245
|
+
The word on an unhealthy chip. 7.10:1 on --cg-unhealthy-fill and 7.68:1 on
|
|
246
|
+
white.
|
|
247
|
+
|
|
248
|
+
DERIVED, not adopted. unhealthy is the one tone here the Console never had, so
|
|
249
|
+
there is no shipped literal to take. This is --cg-unhealthy darkened along its
|
|
250
|
+
own hue and saturation until it reaches the band the two adopted words already
|
|
251
|
+
occupy — 7.17:1 for online and 7.63:1 for expired on their own fills — rather
|
|
252
|
+
than stopped at the first value that clears 4.5:1, so the words read as one set
|
|
253
|
+
instead of three matched and one nearly failing.
|
|
254
|
+
|
|
255
|
+
The tone is not simply reused because it does not clear the bar: --cg-unhealthy
|
|
256
|
+
measures 4.46:1 on this fill. Four hundredths under the floor is under it.
|
|
257
|
+
|
|
258
|
+
dE2000 to its neighbours in this rung: 20.6 from expired-strong, 23.1 from
|
|
259
|
+
online-strong. The tightest pair in the whole rung is expired-strong against
|
|
260
|
+
danger-strong at 19.8, above the 16.5 floor this palette was measured against. */
|
|
261
|
+
--cg-unhealthy-strong: #5d5401;
|
|
262
|
+
/*
|
|
263
|
+
The word on a danger chip, and danger as running text. 5.02:1 on
|
|
264
|
+
--cg-danger-fill and 5.70:1 on white.
|
|
265
|
+
|
|
266
|
+
--cg-danger itself is #E53935, which is 4.23:1 on white and 3.72:1 on its own
|
|
267
|
+
fill. It passes as a fill, a border or an icon, where the bar is 3:1, and fails
|
|
268
|
+
as a sentence. The token is deliberately NOT moved to fix that: every fill,
|
|
269
|
+
border and icon already consuming it would repaint, and the error is that danger
|
|
270
|
+
had no word rung, not that its tone is wrong.
|
|
271
|
+
|
|
272
|
+
The Console's retired --color-error #d42b2b is where this starts and is not
|
|
273
|
+
where it lands. It is 5.02:1 on white, but 4.42:1 on --cg-danger-fill — still
|
|
274
|
+
under the floor on the exact chip that needs it. This is that red darkened along
|
|
275
|
+
its own hue and saturation until it clears 5:1 on the fill, which is the
|
|
276
|
+
smallest move that holds on both grounds.
|
|
277
|
+
|
|
278
|
+
Kept lighter than the other three words on purpose. It is already the closest
|
|
279
|
+
pair in the rung, dE2000 19.8 from expired-strong, and darkening it into their
|
|
280
|
+
7:1 band closes that to 16.6 — a failed chip and an expiring one should not
|
|
281
|
+
converge. */
|
|
282
|
+
--cg-danger-strong: #c42828;
|
|
165
283
|
|
|
166
284
|
/* ---- brand-derived ----
|
|
167
285
|
brand derived (interaction only) */
|
|
@@ -174,6 +292,13 @@
|
|
|
174
292
|
--cg-neutral-0: #ffffff;
|
|
175
293
|
/* SOURCE: alternate page wash */
|
|
176
294
|
--cg-lavender-50: #f2f1f7;
|
|
295
|
+
/*
|
|
296
|
+
SOURCE: the Console's --color-canvas, the recessed ground behind a card.
|
|
297
|
+
|
|
298
|
+
Not a pure grey. It carries a slight blue cast where the rest of this ramp is
|
|
299
|
+
neutral, and that is recorded rather than corrected because the value is what
|
|
300
|
+
legacy raised panels already render and matching them is the point (#151). */
|
|
301
|
+
--cg-neutral-50: #f4f6f8;
|
|
177
302
|
/* SOURCE: hairline / card border */
|
|
178
303
|
--cg-neutral-100: #f1f1f1;
|
|
179
304
|
/* SOURCE: control border, inactive pill fill */
|
|
@@ -192,6 +317,21 @@
|
|
|
192
317
|
--cg-surface-page-alt: var(--cg-lavender-50);
|
|
193
318
|
--cg-surface-card: var(--cg-neutral-0);
|
|
194
319
|
--cg-surface-subtle: var(--cg-lavender-50);
|
|
320
|
+
/*
|
|
321
|
+
A neutral ground one step off white, for a panel that should read as lifted off
|
|
322
|
+
the page rather than flat on it.
|
|
323
|
+
|
|
324
|
+
#151: --cg-surface-card is #ffffff and --cg-surface-subtle is the lavender tint,
|
|
325
|
+
so a raised panel had no value-preserving target and every consumer faced the
|
|
326
|
+
same three-way choice with no right answer — flatten it to white, tint it
|
|
327
|
+
lavender, or pin a literal. This is the fourth answer, and it is the Console's
|
|
328
|
+
own #f4f6f8 rather than a new value.
|
|
329
|
+
|
|
330
|
+
Declared at :root only, like --cg-surface-subtle and --cg-surface-control. The
|
|
331
|
+
neutral grounds do not flip: inside a purple panel or on ink, depth is carried
|
|
332
|
+
by --cg-surface-card and the hairline instead. Making it flip is one entry in
|
|
333
|
+
each of the two surface sets. */
|
|
334
|
+
--cg-surface-raised: var(--cg-neutral-50);
|
|
195
335
|
--cg-surface-control: var(--cg-neutral-200);
|
|
196
336
|
--cg-surface-panel: var(--cg-primary);
|
|
197
337
|
--cg-surface-code: var(--cg-ink);
|
|
@@ -353,7 +493,7 @@
|
|
|
353
493
|
|
|
354
494
|
MEASURED 1.21:1 on white, which is below every bar there is. It works on the
|
|
355
495
|
panel (4.82:1) and on ink (13.13:1); on the page ground it reads only because
|
|
356
|
-
it is paired with
|
|
496
|
+
it is paired with volt-ink in a pill. Recorded rather than
|
|
357
497
|
silently corrected: darkening it would change the Console's appearance, which
|
|
358
498
|
is a design decision and not a contrast fix.
|
|
359
499
|
|
|
@@ -368,9 +508,6 @@
|
|
|
368
508
|
and until it is answered volt is the Console's brand mark and online is the
|
|
369
509
|
entity's state. */
|
|
370
510
|
--cg-volt: #b6ff2e;
|
|
371
|
-
/*
|
|
372
|
-
The halo behind a volt dot. Its job is to make the dot findable on a light ground, which is what carries the 1.21:1 above. */
|
|
373
|
-
--cg-volt-ring: rgba(182, 255, 46, 0.32);
|
|
374
511
|
/*
|
|
375
512
|
Text and glyphs ON volt. 9.95:1 against it — the one part of the volt family that
|
|
376
513
|
is comfortably legible.
|
|
@@ -423,7 +560,9 @@
|
|
|
423
560
|
--cg-danger: #FFDAD6;
|
|
424
561
|
|
|
425
562
|
/* ---- state ----
|
|
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.
|
|
563
|
+
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.
|
|
564
|
+
|
|
565
|
+
The -border and -strong rungs (#149, #150) are the exception to "measured per surface": they resolve to this surface's own hairline and its own primary text rather than to a tint of the tone. A dark ground has no darker rung, and the tints above are tuned to be dots — on the panel's chip fill they measure 3.03:1 to 3.60:1, which is an indicator and not a word. On ink they DO clear 4.5:1 on the ink fill (5.48:1 to 8.92:1), so tinting the word there is available and is five edits in this file; on the panel it is not available at any value, because white measures 4.31:1 on that fill and white is the lightest colour there is. */
|
|
427
566
|
/*
|
|
428
567
|
4.88:1 on the purple. The source green is 1.72:1 here — invisible, same class as the danger override. */
|
|
429
568
|
--cg-online: #C8F5DC;
|
|
@@ -458,6 +597,14 @@
|
|
|
458
597
|
/*
|
|
459
598
|
Translucent white rather than a tint; the panel foreground measures 3.35:1 on it. */
|
|
460
599
|
--cg-danger-fill: rgba(255, 255, 255, 0.16);
|
|
600
|
+
--cg-online-border: var(--cg-border-hairline);
|
|
601
|
+
--cg-expired-border: var(--cg-border-hairline);
|
|
602
|
+
--cg-pending-border: var(--cg-expired-border);
|
|
603
|
+
--cg-online-strong: var(--cg-text-primary);
|
|
604
|
+
--cg-expired-strong: var(--cg-text-primary);
|
|
605
|
+
--cg-pending-strong: var(--cg-expired-strong);
|
|
606
|
+
--cg-unhealthy-strong: var(--cg-text-primary);
|
|
607
|
+
--cg-danger-strong: var(--cg-text-primary);
|
|
461
608
|
|
|
462
609
|
/* ---- indicator ----
|
|
463
610
|
indicator + chrome: only hover flips. A dark wash is invisible on a dark ground. */
|
|
@@ -496,7 +643,9 @@
|
|
|
496
643
|
--cg-danger: #FF8A80;
|
|
497
644
|
|
|
498
645
|
/* ---- state ----
|
|
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.
|
|
646
|
+
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.
|
|
647
|
+
|
|
648
|
+
The -border and -strong rungs (#149, #150) are the exception to "measured per surface": they resolve to this surface's own hairline and its own primary text rather than to a tint of the tone. A dark ground has no darker rung, and the tints above are tuned to be dots — on the panel's chip fill they measure 3.03:1 to 3.60:1, which is an indicator and not a word. On ink they DO clear 4.5:1 on the ink fill (5.48:1 to 8.92:1), so tinting the word there is available and is five edits in this file; on the panel it is not available at any value, because white measures 4.31:1 on that fill and white is the lightest colour there is. */
|
|
500
649
|
/*
|
|
501
650
|
9.00:1 on #222. Ink has headroom, so this stays a recognisable green rather than washing to mint. */
|
|
502
651
|
--cg-online: #5FD99A;
|
|
@@ -523,6 +672,14 @@
|
|
|
523
672
|
--cg-unhealthy-fill: rgba(255, 255, 255, 0.08);
|
|
524
673
|
/* Translucent white; the ink foreground measures 5.45:1 on it. */
|
|
525
674
|
--cg-danger-fill: rgba(255, 255, 255, 0.08);
|
|
675
|
+
--cg-online-border: var(--cg-border-hairline);
|
|
676
|
+
--cg-expired-border: var(--cg-border-hairline);
|
|
677
|
+
--cg-pending-border: var(--cg-expired-border);
|
|
678
|
+
--cg-online-strong: var(--cg-text-primary);
|
|
679
|
+
--cg-expired-strong: var(--cg-text-primary);
|
|
680
|
+
--cg-pending-strong: var(--cg-expired-strong);
|
|
681
|
+
--cg-unhealthy-strong: var(--cg-text-primary);
|
|
682
|
+
--cg-danger-strong: var(--cg-text-primary);
|
|
526
683
|
|
|
527
684
|
/* ---- indicator ----
|
|
528
685
|
indicator + chrome: only hover flips. A dark wash is invisible on a dark ground. */
|
package/dist/tokens.json
CHANGED
|
@@ -18,12 +18,21 @@
|
|
|
18
18
|
"--cg-pending-fill": "var(--cg-expired-fill)",
|
|
19
19
|
"--cg-unhealthy-fill": "#fdf6df",
|
|
20
20
|
"--cg-danger-fill": "#fceded",
|
|
21
|
+
"--cg-online-border": "#abefc6",
|
|
22
|
+
"--cg-expired-border": "#fedf89",
|
|
23
|
+
"--cg-pending-border": "var(--cg-expired-border)",
|
|
24
|
+
"--cg-online-strong": "#085d3a",
|
|
25
|
+
"--cg-expired-strong": "#7a3d0a",
|
|
26
|
+
"--cg-pending-strong": "var(--cg-expired-strong)",
|
|
27
|
+
"--cg-unhealthy-strong": "#5d5401",
|
|
28
|
+
"--cg-danger-strong": "#c42828",
|
|
21
29
|
"--cg-primary-press": "#4b39db",
|
|
22
30
|
"--cg-primary-tint": "#eeecfe",
|
|
23
31
|
"--cg-secondary-press": "#a6cf57",
|
|
24
32
|
"--cg-tertiary-press": "#4dd8e0",
|
|
25
33
|
"--cg-neutral-0": "#ffffff",
|
|
26
34
|
"--cg-lavender-50": "#f2f1f7",
|
|
35
|
+
"--cg-neutral-50": "#f4f6f8",
|
|
27
36
|
"--cg-neutral-100": "#f1f1f1",
|
|
28
37
|
"--cg-neutral-200": "#e3e3e3",
|
|
29
38
|
"--cg-neutral-400": "#acacac",
|
|
@@ -34,6 +43,7 @@
|
|
|
34
43
|
"--cg-surface-page-alt": "var(--cg-lavender-50)",
|
|
35
44
|
"--cg-surface-card": "var(--cg-neutral-0)",
|
|
36
45
|
"--cg-surface-subtle": "var(--cg-lavender-50)",
|
|
46
|
+
"--cg-surface-raised": "var(--cg-neutral-50)",
|
|
37
47
|
"--cg-surface-control": "var(--cg-neutral-200)",
|
|
38
48
|
"--cg-surface-panel": "var(--cg-primary)",
|
|
39
49
|
"--cg-surface-code": "var(--cg-ink)",
|
|
@@ -127,7 +137,6 @@
|
|
|
127
137
|
"--cg-lift-hover": "translateY(-1px)",
|
|
128
138
|
"--cg-press-scale": "0.985",
|
|
129
139
|
"--cg-volt": "#b6ff2e",
|
|
130
|
-
"--cg-volt-ring": "rgba(182, 255, 46, 0.32)",
|
|
131
140
|
"--cg-volt-ink": "#243d00",
|
|
132
141
|
"--cg-scrim": "rgba(34, 34, 34, 0.4)",
|
|
133
142
|
"--cg-hover": "rgba(34, 34, 34, 0.045)"
|
|
@@ -155,6 +164,14 @@
|
|
|
155
164
|
"--cg-pending-fill": "var(--cg-expired-fill)",
|
|
156
165
|
"--cg-unhealthy-fill": "rgba(255, 255, 255, 0.16)",
|
|
157
166
|
"--cg-danger-fill": "rgba(255, 255, 255, 0.16)",
|
|
167
|
+
"--cg-online-border": "var(--cg-border-hairline)",
|
|
168
|
+
"--cg-expired-border": "var(--cg-border-hairline)",
|
|
169
|
+
"--cg-pending-border": "var(--cg-expired-border)",
|
|
170
|
+
"--cg-online-strong": "var(--cg-text-primary)",
|
|
171
|
+
"--cg-expired-strong": "var(--cg-text-primary)",
|
|
172
|
+
"--cg-pending-strong": "var(--cg-expired-strong)",
|
|
173
|
+
"--cg-unhealthy-strong": "var(--cg-text-primary)",
|
|
174
|
+
"--cg-danger-strong": "var(--cg-text-primary)",
|
|
158
175
|
"--cg-hover": "rgba(255, 255, 255, 0.10)"
|
|
159
176
|
},
|
|
160
177
|
"ink": {
|
|
@@ -182,6 +199,14 @@
|
|
|
182
199
|
"--cg-pending-fill": "var(--cg-expired-fill)",
|
|
183
200
|
"--cg-unhealthy-fill": "rgba(255, 255, 255, 0.08)",
|
|
184
201
|
"--cg-danger-fill": "rgba(255, 255, 255, 0.08)",
|
|
202
|
+
"--cg-online-border": "var(--cg-border-hairline)",
|
|
203
|
+
"--cg-expired-border": "var(--cg-border-hairline)",
|
|
204
|
+
"--cg-pending-border": "var(--cg-expired-border)",
|
|
205
|
+
"--cg-online-strong": "var(--cg-text-primary)",
|
|
206
|
+
"--cg-expired-strong": "var(--cg-text-primary)",
|
|
207
|
+
"--cg-pending-strong": "var(--cg-expired-strong)",
|
|
208
|
+
"--cg-unhealthy-strong": "var(--cg-text-primary)",
|
|
209
|
+
"--cg-danger-strong": "var(--cg-text-primary)",
|
|
185
210
|
"--cg-hover": "rgba(255, 255, 255, 0.06)"
|
|
186
211
|
}
|
|
187
212
|
}
|
package/package.json
CHANGED