@colixsystems/widget-sdk 0.158.0 → 0.159.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 +6 -0
- package/dist/host.d.ts +7 -0
- package/dist/navigation.cjs +35 -3
- package/dist/navigation.js +35 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -128,6 +128,12 @@ Additive — nothing in the existing `./host` surface changed, and the contract
|
|
|
128
128
|
|
|
129
129
|
### What's new in 0.151.0 (contract 1.118.0 — unchanged)
|
|
130
130
|
|
|
131
|
+
**`resolveSidebarTokens` answers `activeInk` — the rail's active colour made readable as TEXT (sc-8655, REQ-NAV-LOCAL / REQ-THEME-14).** `activeColor` is authored as a SURFACE: the filled style paints it and forces a white label, so nothing in the system ever pressured it to be legible as ink. Every place that paints it as text inherited that — the accent style's active label, and now the entry a folded sidebar section hands its marking to — and the brand default reads 3.5:1 on the rail, below the 4.5:1 AA asks of normal text and worse than the unmarked rows around it.
|
|
132
|
+
|
|
133
|
+
- `activeInk` is the same hue stepped toward the rail's own polarity (darker on a light rail, lighter on a dark one) only as far as AA needs, and is `activeColor` unchanged whenever that already reads. Derived, never authored: it moves with whatever colour the workspace sets, so there is no second control to keep in step.
|
|
134
|
+
- **Additive.** `activeColor` still names the surface the filled style paints and the colour a non-text mark takes — a dot or an accent edge answers to 3:1, not 4.5:1, so those keep the author's actual colour. `CONTRACT` is untouched (`version` stays `1.122.0`): a host resolver, not a widget-facing surface.
|
|
135
|
+
- Both hosts read it, so the Player, the exported drawer and the Design page's chrome preview cannot paint three different labels off one theme.
|
|
136
|
+
|
|
131
137
|
**`resolveSidebarUnfold` joins the host navigation surface: where a page's local menu unfolds inside the app's own sidebar (sc-7696, REQ-NAV-LOCAL).** A page's local navigation could only be a bar, above or below its content. `local_nav` now also takes `SIDEBAR`, which takes no strip of the page: the saved menu unfolds in the rail and the drawer, under the global menu entry it belongs to.
|
|
132
138
|
|
|
133
139
|
- `resolveSidebarUnfold({ entries, active, sections })` answers `{ under, items, standalone }` from host-neutral keys — page slugs on web, screen names in the export — so it is the ONE derivation both chromes read. Web calls it per render; the compiler asks it once per screen at build time, because the only runtime input is which screen is showing.
|
package/dist/host.d.ts
CHANGED
|
@@ -208,7 +208,14 @@ export function resolveFooterTokens(theme: unknown): FooterTokens;
|
|
|
208
208
|
export interface SidebarTokens {
|
|
209
209
|
backgroundColor: string;
|
|
210
210
|
textColor: string;
|
|
211
|
+
/** The active colour as a SURFACE — what `filled` paints, what a non-text
|
|
212
|
+
* mark (an accent edge, a folded section's dot) is drawn in. */
|
|
211
213
|
activeColor: string;
|
|
214
|
+
/** sc-8655: the same colour as INK. `activeColor` is authored to sit under a
|
|
215
|
+
* white label, so it can fall below AA as text on the rail; this is it
|
|
216
|
+
* stepped toward the rail's own polarity until it clears 4.5:1, and equal to
|
|
217
|
+
* `activeColor` whenever that already reads. Derived, never authored. */
|
|
218
|
+
activeInk: string;
|
|
212
219
|
activeStyle: "filled" | "accent";
|
|
213
220
|
borderColor: string | null;
|
|
214
221
|
borderWidth: number | null;
|
package/dist/navigation.cjs
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
// the SAME literal, so what a planner may persist and what a host draws cannot
|
|
26
26
|
// diverge.
|
|
27
27
|
|
|
28
|
-
const { CONTRACT, isHexColor } = require("./contract.cjs");
|
|
28
|
+
const { CONTRACT, isHexColor, contrastRatio, mixHex, readableTextColor } = require("./contract.cjs");
|
|
29
29
|
|
|
30
30
|
// Absent or unknown resolves here, so every app authored before menu types
|
|
31
31
|
// existed renders and compiles byte-identically.
|
|
@@ -267,14 +267,46 @@ function brandPrimary(config) {
|
|
|
267
267
|
* @returns {{ backgroundColor: string, textColor: string, activeColor: string,
|
|
268
268
|
* activeStyle: string, borderColor: string|null, borderWidth: number|null }}
|
|
269
269
|
*/
|
|
270
|
+
// sc-8655: the sidebar's active colour is authored to sit UNDER a white label
|
|
271
|
+
// (the filled style), so nothing ever pressured it to read as INK — the brand
|
|
272
|
+
// default lands at 3.5:1 on the rail, below the 4.5:1 AA asks of normal text,
|
|
273
|
+
// and worse than the inactive rows beside it. Every surface that paints the
|
|
274
|
+
// active colour as TEXT reads the derived ink instead: the same hue, stepped
|
|
275
|
+
// toward the rail's own polarity only as far as legibility needs.
|
|
276
|
+
const AA_TEXT_CONTRAST = 4.5;
|
|
277
|
+
const INK_STEP = 0.05;
|
|
278
|
+
const INK_MAX_SHIFT = 0.7;
|
|
279
|
+
|
|
280
|
+
function readableInk(color, background) {
|
|
281
|
+
if (!isHexColor(color) || !isHexColor(background)) return color;
|
|
282
|
+
if (contrastRatio(color, background) >= AA_TEXT_CONTRAST) return color;
|
|
283
|
+
// Darken on a light rail, lighten on a dark one — the direction that keeps
|
|
284
|
+
// the hue rather than washing it toward grey.
|
|
285
|
+
const toward = readableTextColor(background, "#000000", "#ffffff");
|
|
286
|
+
let ink = color;
|
|
287
|
+
for (let t = INK_STEP; t <= INK_MAX_SHIFT + 1e-9; t += INK_STEP) {
|
|
288
|
+
ink = mixHex(color, toward, t);
|
|
289
|
+
if (contrastRatio(ink, background) >= AA_TEXT_CONTRAST) break;
|
|
290
|
+
}
|
|
291
|
+
// A colour that cannot reach AA within the shift keeps the closest it got:
|
|
292
|
+
// the most readable answer available beats reverting to the unreadable one.
|
|
293
|
+
return ink;
|
|
294
|
+
}
|
|
295
|
+
|
|
270
296
|
function resolveSidebarTokens(theme) {
|
|
271
297
|
const config = isPlainObject(theme) ? theme : {};
|
|
272
298
|
const sidebar = isPlainObject(config.sidebar) ? config.sidebar : {};
|
|
273
299
|
const borderColor = hexOrNull(sidebar.borderColor);
|
|
300
|
+
const backgroundColor = hexOr(sidebar.backgroundColor, chromeSurface(config));
|
|
301
|
+
const activeColor = hexOr(sidebar.activeColor, brandPrimary(config));
|
|
274
302
|
return {
|
|
275
|
-
backgroundColor
|
|
303
|
+
backgroundColor,
|
|
276
304
|
textColor: hexOr(sidebar.textColor, DEFAULT_CHROME_TEXT),
|
|
277
|
-
activeColor
|
|
305
|
+
activeColor,
|
|
306
|
+
// sc-8655: `activeColor` where it is a SURFACE, `activeInk` where it is
|
|
307
|
+
// text on the rail. Both hosts read the second for an accent-marked label
|
|
308
|
+
// and for a folded section's entry.
|
|
309
|
+
activeInk: readableInk(activeColor, backgroundColor),
|
|
278
310
|
activeStyle: sidebar.activeStyle === "accent" ? "accent" : "filled",
|
|
279
311
|
// REQ-THEME-LOOK: the colour is the switch — no colour, no line.
|
|
280
312
|
borderColor,
|
package/dist/navigation.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// the SAME literal, so what a planner may persist and what a host draws cannot
|
|
17
17
|
// diverge.
|
|
18
18
|
|
|
19
|
-
import { CONTRACT, isHexColor } from "./contract.js";
|
|
19
|
+
import { CONTRACT, isHexColor, contrastRatio, mixHex, readableTextColor } from "./contract.js";
|
|
20
20
|
|
|
21
21
|
// Absent or unknown resolves here, so every app authored before menu types
|
|
22
22
|
// existed renders and compiles byte-identically.
|
|
@@ -258,14 +258,46 @@ function brandPrimary(config) {
|
|
|
258
258
|
* @returns {{ backgroundColor: string, textColor: string, activeColor: string,
|
|
259
259
|
* activeStyle: string, borderColor: string|null, borderWidth: number|null }}
|
|
260
260
|
*/
|
|
261
|
+
// sc-8655: the sidebar's active colour is authored to sit UNDER a white label
|
|
262
|
+
// (the filled style), so nothing ever pressured it to read as INK — the brand
|
|
263
|
+
// default lands at 3.5:1 on the rail, below the 4.5:1 AA asks of normal text,
|
|
264
|
+
// and worse than the inactive rows beside it. Every surface that paints the
|
|
265
|
+
// active colour as TEXT reads the derived ink instead: the same hue, stepped
|
|
266
|
+
// toward the rail's own polarity only as far as legibility needs.
|
|
267
|
+
const AA_TEXT_CONTRAST = 4.5;
|
|
268
|
+
const INK_STEP = 0.05;
|
|
269
|
+
const INK_MAX_SHIFT = 0.7;
|
|
270
|
+
|
|
271
|
+
function readableInk(color, background) {
|
|
272
|
+
if (!isHexColor(color) || !isHexColor(background)) return color;
|
|
273
|
+
if (contrastRatio(color, background) >= AA_TEXT_CONTRAST) return color;
|
|
274
|
+
// Darken on a light rail, lighten on a dark one — the direction that keeps
|
|
275
|
+
// the hue rather than washing it toward grey.
|
|
276
|
+
const toward = readableTextColor(background, "#000000", "#ffffff");
|
|
277
|
+
let ink = color;
|
|
278
|
+
for (let t = INK_STEP; t <= INK_MAX_SHIFT + 1e-9; t += INK_STEP) {
|
|
279
|
+
ink = mixHex(color, toward, t);
|
|
280
|
+
if (contrastRatio(ink, background) >= AA_TEXT_CONTRAST) break;
|
|
281
|
+
}
|
|
282
|
+
// A colour that cannot reach AA within the shift keeps the closest it got:
|
|
283
|
+
// the most readable answer available beats reverting to the unreadable one.
|
|
284
|
+
return ink;
|
|
285
|
+
}
|
|
286
|
+
|
|
261
287
|
export function resolveSidebarTokens(theme) {
|
|
262
288
|
const config = isPlainObject(theme) ? theme : {};
|
|
263
289
|
const sidebar = isPlainObject(config.sidebar) ? config.sidebar : {};
|
|
264
290
|
const borderColor = hexOrNull(sidebar.borderColor);
|
|
291
|
+
const backgroundColor = hexOr(sidebar.backgroundColor, chromeSurface(config));
|
|
292
|
+
const activeColor = hexOr(sidebar.activeColor, brandPrimary(config));
|
|
265
293
|
return {
|
|
266
|
-
backgroundColor
|
|
294
|
+
backgroundColor,
|
|
267
295
|
textColor: hexOr(sidebar.textColor, DEFAULT_CHROME_TEXT),
|
|
268
|
-
activeColor
|
|
296
|
+
activeColor,
|
|
297
|
+
// sc-8655: `activeColor` where it is a SURFACE, `activeInk` where it is
|
|
298
|
+
// text on the rail. Both hosts read the second for an accent-marked label
|
|
299
|
+
// and for a folded section's entry.
|
|
300
|
+
activeInk: readableInk(activeColor, backgroundColor),
|
|
269
301
|
activeStyle: sidebar.activeStyle === "accent" ? "accent" : "filled",
|
|
270
302
|
// REQ-THEME-LOOK: the colour is the switch — no colour, no line.
|
|
271
303
|
borderColor,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colixsystems/widget-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.159.0",
|
|
4
4
|
"description": "Common widget interface for AppStudio. Implements WidgetManifest, WidgetContext, property schema, and helper hooks.",
|
|
5
5
|
"homepage": "https://github.com/Colix-AB/AppStudio",
|
|
6
6
|
"type": "module",
|