@cueplusplus/ui 0.8.0 → 0.10.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/CHANGELOG.md +457 -0
- package/README.md +131 -0
- package/dist/chat/message-list.js +2 -1
- package/dist/configurator/_export.d.ts +1 -1
- package/dist/configurator/_export.js +53 -13
- package/dist/configurator/_overrides.d.ts +25 -6
- package/dist/configurator/_overrides.js +30 -17
- package/dist/configurator/configurator.js +8 -3
- package/dist/configurator/panel-sections.js +43 -13
- package/dist/elements/command-palette.js +1 -1
- package/dist/elements/flow-graph.js +2 -2
- package/dist/elements/markdown.js +1 -1
- package/dist/elements/surfaces.js +4 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.js +5 -2
- package/dist/instruments/meter.d.ts +5 -1
- package/dist/layout/bento.d.ts +144 -0
- package/dist/layout/bento.js +266 -0
- package/dist/layout/index.d.ts +3 -2
- package/dist/layout/index.js +3 -2
- package/dist/midi/piano-keyboard.js +5 -1
- package/dist/primitives/button.d.ts +12 -0
- package/dist/styles.css +23 -1
- package/dist/system/density.d.ts +17 -8
- package/dist/system/density.js +39 -16
- package/dist/system/index.d.ts +5 -2
- package/dist/system/index.js +3 -1
- package/dist/system/overrides.d.ts +80 -0
- package/dist/system/overrides.js +277 -0
- package/dist/system/portal.d.ts +4 -2
- package/dist/system/portal.js +34 -3
- package/dist/system/prepaint.d.ts +58 -7
- package/dist/system/prepaint.js +72 -20
- package/dist/system/theme-provider.d.ts +133 -8
- package/dist/system/theme-provider.js +203 -72
- package/dist/system/theme-registry.d.ts +53 -0
- package/dist/system/theme-registry.js +66 -0
- package/dist/system/use-density.d.ts +13 -5
- package/dist/system/use-density.js +142 -13
- package/dist/system/use-theme.d.ts +5 -3
- package/dist/system/use-theme.js +5 -3
- package/dist/system/vocabulary.d.ts +15 -0
- package/dist/system/vocabulary.js +111 -0
- package/dist/theming/contrast.d.ts +2 -122
- package/dist/theming/contrast.js +2 -194
- package/dist/theming/create-theme.d.ts +37 -11
- package/dist/theming/create-theme.js +54 -17
- package/dist/theming/index.d.ts +3 -4
- package/dist/theming/index.js +3 -4
- package/dist/theming/serialize.d.ts +24 -11
- package/dist/theming/serialize.js +16 -18
- package/manifest/components/accordion.json +24 -6
- package/manifest/components/activity-graph.json +20 -5
- package/manifest/components/agent-card.json +37 -9
- package/manifest/components/agent-handoff.json +21 -5
- package/manifest/components/agent-mode-badge.json +22 -4
- package/manifest/components/agent-pile.json +38 -8
- package/manifest/components/agent-plan.json +9 -2
- package/manifest/components/agent-status.json +17 -3
- package/manifest/components/agent-surface.json +26 -11
- package/manifest/components/alert-dialog.json +29 -5
- package/manifest/components/animated-number.json +14 -6
- package/manifest/components/app-bar.json +66 -13
- package/manifest/components/app-shell.json +133 -31
- package/manifest/components/app-window-frame.json +28 -6
- package/manifest/components/approval-card.json +37 -7
- package/manifest/components/artifact-card.json +16 -4
- package/manifest/components/ask-box.json +99 -21
- package/manifest/components/audience-icon.json +1 -7
- package/manifest/components/autocomplete.json +80 -18
- package/manifest/components/avatar-group.json +13 -2
- package/manifest/components/avatar.json +22 -4
- package/manifest/components/background-inbox.json +9 -2
- package/manifest/components/bento.json +306 -0
- package/manifest/components/branch-picker.json +29 -7
- package/manifest/components/breadcrumb.json +13 -3
- package/manifest/components/button-group.json +21 -4
- package/manifest/components/button.json +41 -4
- package/manifest/components/canvas-split-body.json +4 -1
- package/manifest/components/canvas-split-header.json +17 -4
- package/manifest/components/canvas-split-line.json +4 -1
- package/manifest/components/canvas-split-message.json +8 -1
- package/manifest/components/card.json +14 -3
- package/manifest/components/carousel.json +54 -12
- package/manifest/components/catalogue-icon.json +1 -7
- package/manifest/components/channel-beta-icon.json +1 -7
- package/manifest/components/channel-matrix.json +80 -15
- package/manifest/components/channel-released-icon.json +1 -7
- package/manifest/components/chart-container.json +26 -10
- package/manifest/components/chart-ramp.json +13 -3
- package/manifest/components/chart-swatch.json +9 -2
- package/manifest/components/chart-tooltip-content.json +31 -7
- package/manifest/components/chart.json +30 -6
- package/manifest/components/chat-panel-composer.json +9 -2
- package/manifest/components/checkbox-group.json +4 -2
- package/manifest/components/checkbox.json +17 -4
- package/manifest/components/checkpoint-history.json +13 -3
- package/manifest/components/chip.json +35 -5
- package/manifest/components/clamp.json +30 -7
- package/manifest/components/cli-tool-icon.json +1 -7
- package/manifest/components/code-diff.json +20 -5
- package/manifest/components/code-runner.json +32 -6
- package/manifest/components/collapsible.json +16 -4
- package/manifest/components/color-area.json +4 -1
- package/manifest/components/color-field.json +13 -3
- package/manifest/components/color-picker.json +75 -17
- package/manifest/components/color-slider.json +12 -3
- package/manifest/components/color-swatch.json +13 -2
- package/manifest/components/colors-section.json +10 -7
- package/manifest/components/combobox.json +81 -18
- package/manifest/components/command-palette.json +57 -13
- package/manifest/components/compaction-row.json +35 -7
- package/manifest/components/comparison-card.json +17 -4
- package/manifest/components/composer-attachment-chip.json +9 -2
- package/manifest/components/composer-bar.json +4 -1
- package/manifest/components/composer-command-item.json +8 -2
- package/manifest/components/composer-context.json +4 -1
- package/manifest/components/composer-input.json +5 -1
- package/manifest/components/composer-menu-item.json +4 -1
- package/manifest/components/composer-menu.json +12 -2
- package/manifest/components/composer-model-item.json +8 -2
- package/manifest/components/composer-model-trigger.json +8 -2
- package/manifest/components/composer-person-item.json +8 -2
- package/manifest/components/composer-send.json +8 -2
- package/manifest/components/composer-voice-button.json +4 -1
- package/manifest/components/composer-voice.json +8 -2
- package/manifest/components/composer.json +76 -17
- package/manifest/components/computer-use.json +17 -4
- package/manifest/components/confidence-marker.json +13 -3
- package/manifest/components/connection-state.json +23 -4
- package/manifest/components/container.json +14 -2
- package/manifest/components/context-breakdown.json +8 -2
- package/manifest/components/context-menu.json +16 -4
- package/manifest/components/context-usage.json +16 -4
- package/manifest/components/conversation-search.json +22 -5
- package/manifest/components/copy-button.json +32 -7
- package/manifest/components/cost-meter.json +12 -3
- package/manifest/components/cue-logotype.json +17 -3
- package/manifest/components/cue-mark.json +17 -3
- package/manifest/components/cue-portal-frame.json +14 -4
- package/manifest/components/data-row.json +21 -7
- package/manifest/components/data-table-pagination.json +18 -4
- package/manifest/components/data-table-toolbar.json +30 -7
- package/manifest/components/data-tree.json +42 -9
- package/manifest/components/date-field.json +13 -2
- package/manifest/components/date-picker.json +81 -19
- package/manifest/components/date-range-picker.json +81 -19
- package/manifest/components/day-separator.json +4 -1
- package/manifest/components/delegation-card.json +64 -12
- package/manifest/components/density.json +25 -5
- package/manifest/components/description-list.json +8 -1
- package/manifest/components/diagram.json +33 -7
- package/manifest/components/dialog.json +29 -6
- package/manifest/components/disclosure.json +31 -7
- package/manifest/components/dmx-bar.json +16 -4
- package/manifest/components/dmx-strip.json +20 -5
- package/manifest/components/document-reference.json +21 -5
- package/manifest/components/draft-restore.json +18 -4
- package/manifest/components/drawer.json +30 -6
- package/manifest/components/dropdown-menu.json +55 -10
- package/manifest/components/edit-message.json +32 -7
- package/manifest/components/elements-command-palette.json +27 -6
- package/manifest/components/elements-data-table.json +9 -2
- package/manifest/components/elements-timeline.json +8 -2
- package/manifest/components/elicitation-form.json +31 -6
- package/manifest/components/empty-state-composer.json +9 -2
- package/manifest/components/empty-state-suggestion.json +4 -1
- package/manifest/components/empty-state.json +25 -5
- package/manifest/components/end-of-turn-summary.json +16 -4
- package/manifest/components/env-var-input.json +54 -12
- package/manifest/components/error-state.json +17 -4
- package/manifest/components/export-dialog.json +27 -11
- package/manifest/components/eyebrow.json +4 -1
- package/manifest/components/feedback-dialog.json +33 -7
- package/manifest/components/field-description.json +4 -1
- package/manifest/components/field-error.json +13 -3
- package/manifest/components/field-label.json +4 -1
- package/manifest/components/field.json +4 -1
- package/manifest/components/file-tree.json +16 -4
- package/manifest/components/file-upload.json +58 -13
- package/manifest/components/flow-graph.json +12 -3
- package/manifest/components/folder-icon.json +1 -7
- package/manifest/components/footer.json +24 -4
- package/manifest/components/frac.json +10 -2
- package/manifest/components/generation-loader.json +17 -3
- package/manifest/components/generative-ui.json +39 -7
- package/manifest/components/grid.json +56 -5
- package/manifest/components/group-bar.json +25 -6
- package/manifest/components/guardrail-notice.json +22 -5
- package/manifest/components/hover-card.json +45 -8
- package/manifest/components/icon-button.json +22 -4
- package/manifest/components/image-generation.json +8 -2
- package/manifest/components/info-tip.json +42 -7
- package/manifest/components/inline-citation.json +14 -3
- package/manifest/components/input-group.json +24 -5
- package/manifest/components/input.json +13 -3
- package/manifest/components/item.json +42 -8
- package/manifest/components/job-progress.json +25 -6
- package/manifest/components/launcher-bubble.json +32 -7
- package/manifest/components/ledger.json +87 -19
- package/manifest/components/link.json +16 -3
- package/manifest/components/live-region-announcer.json +21 -5
- package/manifest/components/log-viewer.json +42 -10
- package/manifest/components/map-answer.json +17 -4
- package/manifest/components/markdown-text.json +36 -5
- package/manifest/components/math-block.json +12 -3
- package/manifest/components/mcp-server-icon.json +1 -7
- package/manifest/components/mcp-server-panel.json +18 -4
- package/manifest/components/memory-chips.json +9 -2
- package/manifest/components/menubar.json +33 -7
- package/manifest/components/message-actions.json +32 -7
- package/manifest/components/message-attachments.json +9 -2
- package/manifest/components/message-branches.json +14 -3
- package/manifest/components/message-list.json +27 -6
- package/manifest/components/message-pair.json +25 -5
- package/manifest/components/message-queue.json +13 -3
- package/manifest/components/message-timing.json +8 -2
- package/manifest/components/message.json +41 -9
- package/manifest/components/meter.json +43 -7
- package/manifest/components/mobile-composer.json +47 -10
- package/manifest/components/model-picker.json +13 -3
- package/manifest/components/multi-select.json +78 -17
- package/manifest/components/musical-time-input.json +34 -8
- package/manifest/components/navigation-menu.json +54 -12
- package/manifest/components/node-card.json +42 -7
- package/manifest/components/node-handle.json +17 -2
- package/manifest/components/number-field.json +28 -6
- package/manifest/components/number-ticker.json +8 -2
- package/manifest/components/onboarding.json +18 -4
- package/manifest/components/otp-field.json +43 -10
- package/manifest/components/page-shell.json +35 -6
- package/manifest/components/pagination.json +21 -5
- package/manifest/components/panel-header.json +15 -3
- package/manifest/components/password-input.json +38 -8
- package/manifest/components/permission-grant.json +28 -5
- package/manifest/components/permission-scopes.json +57 -12
- package/manifest/components/piano-keyboard.json +38 -9
- package/manifest/components/plussie.json +34 -5
- package/manifest/components/popover.json +45 -8
- package/manifest/components/preset-section.json +14 -8
- package/manifest/components/progress.json +25 -6
- package/manifest/components/prompt-library.json +27 -6
- package/manifest/components/queue-dock.json +36 -8
- package/manifest/components/quota-banner.json +25 -6
- package/manifest/components/quote-reply.json +29 -7
- package/manifest/components/radio-group.json +12 -2
- package/manifest/components/radio.json +17 -3
- package/manifest/components/rating.json +60 -13
- package/manifest/components/read-aloud.json +35 -8
- package/manifest/components/reasoning-effort.json +17 -4
- package/manifest/components/reasoning-panel.json +33 -8
- package/manifest/components/recommendation-card.json +35 -10
- package/manifest/components/regenerate-menu.json +22 -5
- package/manifest/components/replay-player.json +29 -7
- package/manifest/components/research-report.json +12 -3
- package/manifest/components/resizable.json +4 -1
- package/manifest/components/retrieval-chunks.json +16 -4
- package/manifest/components/revert-dock.json +61 -14
- package/manifest/components/reviewable-diff.json +23 -5
- package/manifest/components/risk-badge.json +18 -3
- package/manifest/components/row.json +17 -4
- package/manifest/components/schedule-card.json +25 -6
- package/manifest/components/score-breakdown.json +20 -5
- package/manifest/components/scroll-anchor.json +13 -3
- package/manifest/components/scroll-area.json +31 -8
- package/manifest/components/scrollable-tabs-list.json +12 -4
- package/manifest/components/scrub-input.json +84 -18
- package/manifest/components/seam-cell.json +19 -3
- package/manifest/components/seam-grid.json +12 -3
- package/manifest/components/search-input.json +49 -10
- package/manifest/components/section-header.json +36 -8
- package/manifest/components/segmented-control.json +50 -11
- package/manifest/components/select.json +74 -16
- package/manifest/components/separator.json +4 -1
- package/manifest/components/settings-panel.json +41 -9
- package/manifest/components/shape-section.json +10 -7
- package/manifest/components/shared-conversation.json +21 -5
- package/manifest/components/sheet.json +29 -6
- package/manifest/components/shimmer-label.json +4 -2
- package/manifest/components/sidebar.json +60 -13
- package/manifest/components/skill-icon.json +1 -7
- package/manifest/components/slider.json +22 -5
- package/manifest/components/sources.json +17 -4
- package/manifest/components/sparkline.json +17 -4
- package/manifest/components/speaker-identity.json +4 -1
- package/manifest/components/spec-sheet.json +16 -4
- package/manifest/components/spectrum-visualizer.json +41 -10
- package/manifest/components/spinner.json +12 -2
- package/manifest/components/stack-icon.json +1 -7
- package/manifest/components/stack.json +54 -7
- package/manifest/components/stacks-matrix-icon.json +1 -7
- package/manifest/components/stat.json +28 -4
- package/manifest/components/status-bar.json +50 -9
- package/manifest/components/status-dot.json +23 -4
- package/manifest/components/stepper.json +22 -5
- package/manifest/components/stopped-run.json +19 -4
- package/manifest/components/streaming-text.json +12 -3
- package/manifest/components/subagent-list.json +21 -5
- package/manifest/components/suggestions.json +27 -5
- package/manifest/components/swap-label.json +18 -4
- package/manifest/components/switch.json +18 -3
- package/manifest/components/table-scroll-region.json +4 -1
- package/manifest/components/table.json +54 -11
- package/manifest/components/tabs.json +16 -5
- package/manifest/components/tags-input.json +79 -17
- package/manifest/components/tail-status.json +37 -8
- package/manifest/components/terminal-block.json +25 -5
- package/manifest/components/terminal-frame.json +34 -8
- package/manifest/components/textarea.json +8 -3
- package/manifest/components/theme-configurator.json +47 -14
- package/manifest/components/theme-provider.json +97 -18
- package/manifest/components/thinking-indicator.json +8 -2
- package/manifest/components/thread-list.json +13 -3
- package/manifest/components/thread-search.json +22 -5
- package/manifest/components/threshold-rail.json +77 -14
- package/manifest/components/time-boundary.json +8 -2
- package/manifest/components/time-field.json +13 -2
- package/manifest/components/timeline-ruler.json +51 -12
- package/manifest/components/timeline.json +19 -3
- package/manifest/components/title-bar.json +19 -4
- package/manifest/components/toast.json +12 -3
- package/manifest/components/todo-list.json +8 -2
- package/manifest/components/toggle-group.json +33 -7
- package/manifest/components/toggle.json +23 -4
- package/manifest/components/token-editor.json +26 -10
- package/manifest/components/tool-call-card.json +83 -16
- package/manifest/components/tool-call.json +37 -9
- package/manifest/components/tool-error.json +34 -8
- package/manifest/components/tool-group.json +17 -4
- package/manifest/components/tool-timeline.json +37 -9
- package/manifest/components/toolbar.json +37 -8
- package/manifest/components/tooltip.json +41 -7
- package/manifest/components/trace-waterfall.json +12 -3
- package/manifest/components/tree-visibility-toggle.json +18 -3
- package/manifest/components/tree.json +52 -11
- package/manifest/components/turn-footer.json +36 -8
- package/manifest/components/two-step-button.json +46 -9
- package/manifest/components/typing-indicator.json +8 -1
- package/manifest/components/universe-grid.json +37 -10
- package/manifest/components/unread-divider.json +4 -1
- package/manifest/components/usage-chart.json +46 -9
- package/manifest/components/verdict-row.json +20 -5
- package/manifest/components/voice-conversation.json +37 -7
- package/manifest/components/web-preview.json +23 -5
- package/manifest/components/web-search.json +20 -5
- package/manifest/components/work-collapse.json +30 -7
- package/manifest/fixtures.json +106 -0
- package/manifest/manifest.json +449 -310
- package/manifest/tokens.json +122 -12
- package/package.json +16 -6
- package/dist/theming/_presets.d.ts +0 -11
- package/dist/theming/_presets.js +0 -678
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useIsomorphicLayoutEffect } from "./use-isomorphic-layout-effect.js";
|
|
3
|
-
import { DensityContext } from "./density.js";
|
|
3
|
+
import { DEFAULT_DENSITY, DensityContext } from "./density.js";
|
|
4
|
+
import { warnOnce } from "./vocabulary.js";
|
|
5
|
+
import { ThemeRegistryContext } from "./theme-registry.js";
|
|
6
|
+
import { ThemeContext } from "./theme-provider.js";
|
|
4
7
|
import * as React from "react";
|
|
5
8
|
//#region src/system/use-density.ts
|
|
6
9
|
/**
|
|
7
|
-
* The control ladder in rem, mirroring
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
+
* The **base** control ladder in rem, mirroring
|
|
11
|
+
* `packages/tokens/src/density/*.tokens.json`.
|
|
12
|
+
*
|
|
13
|
+
* Keyed by `BaseDensity` — the closed five — and not by the open `Density`,
|
|
14
|
+
* because those five are the only rungs this package can know at build time. A
|
|
15
|
+
* rung a theme adds exists nowhere in `ui`'s source; it arrives at runtime on a
|
|
16
|
+
* manifest, and {@link ladderFor} is what goes and gets it.
|
|
17
|
+
*
|
|
18
|
+
* Only a fallback either way: {@link useControlHeight} measures the live CSS
|
|
19
|
+
* first, so per-app token overrides and the `@media (pointer: coarse)` re-raise
|
|
20
|
+
* win.
|
|
10
21
|
*/
|
|
11
22
|
const CONTROL_REM = {
|
|
12
23
|
"ultra-compact": {
|
|
@@ -37,16 +48,112 @@ const CONTROL_REM = {
|
|
|
37
48
|
};
|
|
38
49
|
/** Root font size assumed before the first measurement, so SSR and hydration agree. */
|
|
39
50
|
const ASSUMED_ROOT_FONT_SIZE = 16;
|
|
51
|
+
/** The three tokens the ladder is made of, in the order a warning should name them. */
|
|
52
|
+
const CONTROL_TOKENS = [
|
|
53
|
+
"control-sm",
|
|
54
|
+
"control-md",
|
|
55
|
+
"control-lg"
|
|
56
|
+
];
|
|
57
|
+
/**
|
|
58
|
+
* A `"1.25rem"` geometry value as a number of rem, or `null`.
|
|
59
|
+
*
|
|
60
|
+
* `null` rather than a throw, and rem rather than any length, because the three
|
|
61
|
+
* `control-*` tokens are rem *by convention*: `validateManifest` requires only a
|
|
62
|
+
* non-empty string, and the base geometry block itself ships `radius-control:
|
|
63
|
+
* "3px"`, so a manifest that passes validation can perfectly well carry a value
|
|
64
|
+
* this cannot use. See {@link ladderFor} for what happens then.
|
|
65
|
+
*
|
|
66
|
+
* Strictly positive, too. `"0rem"` and `"-1rem"` parse perfectly well and are
|
|
67
|
+
* not control heights: a virtualiser handed a row height of zero divides by it
|
|
68
|
+
* or renders every row in the list, and a negative one is not a height at all.
|
|
69
|
+
* Both are manifest defects, and a defect is better reported than propagated.
|
|
70
|
+
*/
|
|
71
|
+
function rem(value) {
|
|
72
|
+
if (value === void 0) return null;
|
|
73
|
+
const match = /^(-?\d*\.?\d+)rem$/.exec(value.trim());
|
|
74
|
+
if (match === null) return null;
|
|
75
|
+
const parsed = Number.parseFloat(match[1]);
|
|
76
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The rem ladder for a rung: the base table, else the registry, else a warning.
|
|
80
|
+
*
|
|
81
|
+
* The registry is the only place an added rung's geometry exists on the client.
|
|
82
|
+
* The stylesheet has it — `theme-tools` emitted a `[data-theme="foo"]
|
|
83
|
+
* [data-density="foo-dense"]` block for it — but CSS is not readable as data
|
|
84
|
+
* before layout, and this value is needed by a `useState` initializer, which is
|
|
85
|
+
* the one moment nothing has been laid out yet.
|
|
86
|
+
*
|
|
87
|
+
* Every failure degrades rather than throws, and the reason is that this runs
|
|
88
|
+
* during render from a `useState` initializer with no error boundary anywhere
|
|
89
|
+
* near it: a throw there unmounts the application, in production, over a
|
|
90
|
+
* pre-measurement seed. And the seed really is only a seed — the added rung's
|
|
91
|
+
* CSS exists whatever this returns, so the layout effect's probe measures the
|
|
92
|
+
* true height one tick later and the fallback is visible for one commit. A
|
|
93
|
+
* `control-*` rem rule in `validateManifest` is what would make this
|
|
94
|
+
* impossible, and that is a Plan 4 carry.
|
|
95
|
+
*/
|
|
96
|
+
function ladderFor(registry, density) {
|
|
97
|
+
const base = CONTROL_REM[density];
|
|
98
|
+
if (base !== void 0) return base;
|
|
99
|
+
const added = registry?.densities.find((d) => d.name === density)?.geometry;
|
|
100
|
+
if (added !== void 0) {
|
|
101
|
+
const ladder = {
|
|
102
|
+
sm: rem(added["control-sm"]),
|
|
103
|
+
md: rem(added["control-md"]),
|
|
104
|
+
lg: rem(added["control-lg"])
|
|
105
|
+
};
|
|
106
|
+
if (ladder.sm !== null && ladder.md !== null && ladder.lg !== null) return ladder;
|
|
107
|
+
const offender = CONTROL_TOKENS.find((token) => rem(added[token]) === null);
|
|
108
|
+
warnOnce(`density "${density}" declares ${offender} as ${JSON.stringify(added[offender])}, which is not a positive rem length; the ${DEFAULT_DENSITY} ladder seeds the measurement instead`);
|
|
109
|
+
return CONTROL_REM[DEFAULT_DENSITY];
|
|
110
|
+
}
|
|
111
|
+
warnOnce(`density "${density}" is not a base rung and no registered theme adds it; the ${DEFAULT_DENSITY} ladder is used until the probe measures it`);
|
|
112
|
+
return CONTROL_REM[DEFAULT_DENSITY];
|
|
113
|
+
}
|
|
40
114
|
function rootFontSize() {
|
|
41
115
|
if (typeof document === "undefined") return ASSUMED_ROOT_FONT_SIZE;
|
|
42
116
|
const size = Number.parseFloat(getComputedStyle(document.documentElement).fontSize);
|
|
43
117
|
return Number.isFinite(size) && size > 0 ? size : ASSUMED_ROOT_FONT_SIZE;
|
|
44
118
|
}
|
|
45
|
-
|
|
46
|
-
|
|
119
|
+
/**
|
|
120
|
+
* Measure `--cue-control-<size>` on a throwaway element stamped like the caller.
|
|
121
|
+
*
|
|
122
|
+
* `theme` as well as `density`, because a rung a theme adds is emitted under
|
|
123
|
+
* that theme's `[data-theme]`: a bare `data-density` div on `document.body`
|
|
124
|
+
* matches the **root** theme's rules, so under a nested provider of another
|
|
125
|
+
* theme it would measure a ladder the caller is not on. `null` stamps nothing,
|
|
126
|
+
* which is right for a caller with no provider above it at all.
|
|
127
|
+
*
|
|
128
|
+
* `data-cue-theme` rides beside `data-theme`, both from the same value: an
|
|
129
|
+
* inert attribute nothing here reads back, but the emitters' exact-match
|
|
130
|
+
* selector does — see `densityRules`/`themeSelector`'s comment on why a CSS
|
|
131
|
+
* ancestor guard alone cannot tell *nearest* theme from *any* theme in the
|
|
132
|
+
* chain, and how a runtime-published stamp closes that where the DOM shape
|
|
133
|
+
* lets it.
|
|
134
|
+
*
|
|
135
|
+
* `ladder` is passed in rather than looked up here so that both reads — this
|
|
136
|
+
* one and the state seed — go through {@link ladderFor} exactly once each, and
|
|
137
|
+
* so this stays a plain function that a test can reason about without a tree.
|
|
138
|
+
*
|
|
139
|
+
* What this element cannot see is the standing concession of the whole
|
|
140
|
+
* approach, and it is worth naming where it is caused rather than where it is
|
|
141
|
+
* felt: hanging off `document.body` puts the probe outside every provider
|
|
142
|
+
* root, so a rule scoped under `[data-cue-root]` does not reach it. That is the
|
|
143
|
+
* reason the provider's density override layer is emitted as one
|
|
144
|
+
* document-scoped `<style>` instead — a subtree-scoped rule would leave this
|
|
145
|
+
* measuring unoverridden geometry while the control beside it rendered the
|
|
146
|
+
* override, and the two would disagree by exactly the override.
|
|
147
|
+
*/
|
|
148
|
+
function measureControlHeight(ladder, density, theme, size) {
|
|
149
|
+
const fallback = ladder[size] * rootFontSize();
|
|
47
150
|
if (typeof document === "undefined" || document.body === null) return fallback;
|
|
48
151
|
const probe = document.createElement("div");
|
|
49
152
|
probe.setAttribute("data-density", density);
|
|
153
|
+
if (theme !== null) {
|
|
154
|
+
probe.setAttribute("data-theme", theme);
|
|
155
|
+
probe.setAttribute("data-cue-theme", theme);
|
|
156
|
+
}
|
|
50
157
|
probe.setAttribute("aria-hidden", "true");
|
|
51
158
|
probe.style.cssText = `position:absolute;top:-9999px;left:-9999px;width:0;visibility:hidden;pointer-events:none;height:var(--cue-control-${size})`;
|
|
52
159
|
document.body.appendChild(probe);
|
|
@@ -81,10 +188,18 @@ function useDensity() {
|
|
|
81
188
|
* canvas renderer needs when it cannot ask CSS.
|
|
82
189
|
*
|
|
83
190
|
* Measured from a throwaway `document.body` probe stamped with the ambient
|
|
84
|
-
* density
|
|
85
|
-
* re-raise of `ultra-compact
|
|
86
|
-
*
|
|
87
|
-
*
|
|
191
|
+
* density *and the ambient theme*, so app-wide token overrides, the `@media
|
|
192
|
+
* (pointer: coarse)` re-raise of `ultra-compact`, and a rung a theme adds are
|
|
193
|
+
* all honoured. Overrides scoped to a subtree the probe is not in are still
|
|
194
|
+
* not: the probe hangs off `document.body`, outside every provider root.
|
|
195
|
+
*
|
|
196
|
+
* Falls back to the authored ladder × 16px during SSR and before the first
|
|
197
|
+
* layout effect, which keeps hydration stable. A rung the ambient theme does
|
|
198
|
+
* not declare falls back to the default ladder with a development warning
|
|
199
|
+
* rather than throwing — the probe corrects it on the next tick either way.
|
|
200
|
+
*
|
|
201
|
+
* Safe outside a `<ThemeProvider>`: it reads the theme and the registry from
|
|
202
|
+
* context directly instead of through `useTheme()`, which throws.
|
|
88
203
|
*
|
|
89
204
|
* @param size - Which rung to measure. Defaults to `"md"`.
|
|
90
205
|
* @returns Height in CSS pixels.
|
|
@@ -93,10 +208,24 @@ function useDensity() {
|
|
|
93
208
|
*/
|
|
94
209
|
function useControlHeight(size = "md") {
|
|
95
210
|
const density = useDensity();
|
|
96
|
-
const
|
|
211
|
+
const theme = React.useContext(ThemeContext)?.theme ?? null;
|
|
212
|
+
const ladder = ladderFor(React.useContext(ThemeRegistryContext), density);
|
|
213
|
+
const { sm, md, lg } = ladder;
|
|
214
|
+
const [height, setHeight] = React.useState(() => ladder[size] * ASSUMED_ROOT_FONT_SIZE);
|
|
97
215
|
useIsomorphicLayoutEffect(() => {
|
|
98
|
-
setHeight(measureControlHeight(
|
|
99
|
-
|
|
216
|
+
setHeight(measureControlHeight({
|
|
217
|
+
sm,
|
|
218
|
+
md,
|
|
219
|
+
lg
|
|
220
|
+
}, density, theme, size));
|
|
221
|
+
}, [
|
|
222
|
+
sm,
|
|
223
|
+
md,
|
|
224
|
+
lg,
|
|
225
|
+
density,
|
|
226
|
+
theme,
|
|
227
|
+
size
|
|
228
|
+
]);
|
|
100
229
|
return height;
|
|
101
230
|
}
|
|
102
231
|
//#endregion
|
|
@@ -3,12 +3,14 @@ import { ThemeContextValue } from "./theme-provider.js";
|
|
|
3
3
|
/**
|
|
4
4
|
* Read and drive the ambient theme state.
|
|
5
5
|
*
|
|
6
|
-
* Returns `{ theme, density, mode, font, resolvedMode, setTheme,
|
|
7
|
-
* setMode, setFont }`.
|
|
6
|
+
* Returns `{ theme, manifest, density, mode, font, resolvedMode, setTheme,
|
|
7
|
+
* setDensity, setMode, setFont }`.
|
|
8
8
|
* `density` here is the *provider's* level — a nested `<Density>` island does not
|
|
9
9
|
* change it; use `useDensity()` when you want the level that actually applies
|
|
10
10
|
* where you are. `font` is the pairing the provider holds; it has no island, so
|
|
11
|
-
* it is always the one in force.
|
|
11
|
+
* it is always the one in force. `manifest` is the active theme's, or `null`
|
|
12
|
+
* when that name is not in the provider's `themes` — which is every name in an
|
|
13
|
+
* app that has registered none; `useThemes()` is what was registered.
|
|
12
14
|
*
|
|
13
15
|
* @throws If called outside a `<ThemeProvider>`. Theme state is never guessed:
|
|
14
16
|
* a missing provider is a wiring bug, not a fallback situation.
|
package/dist/system/use-theme.js
CHANGED
|
@@ -5,12 +5,14 @@ import * as React from "react";
|
|
|
5
5
|
/**
|
|
6
6
|
* Read and drive the ambient theme state.
|
|
7
7
|
*
|
|
8
|
-
* Returns `{ theme, density, mode, font, resolvedMode, setTheme,
|
|
9
|
-
* setMode, setFont }`.
|
|
8
|
+
* Returns `{ theme, manifest, density, mode, font, resolvedMode, setTheme,
|
|
9
|
+
* setDensity, setMode, setFont }`.
|
|
10
10
|
* `density` here is the *provider's* level — a nested `<Density>` island does not
|
|
11
11
|
* change it; use `useDensity()` when you want the level that actually applies
|
|
12
12
|
* where you are. `font` is the pairing the provider holds; it has no island, so
|
|
13
|
-
* it is always the one in force.
|
|
13
|
+
* it is always the one in force. `manifest` is the active theme's, or `null`
|
|
14
|
+
* when that name is not in the provider's `themes` — which is every name in an
|
|
15
|
+
* app that has registered none; `useThemes()` is what was registered.
|
|
14
16
|
*
|
|
15
17
|
* @throws If called outside a `<ThemeProvider>`. Theme state is never guessed:
|
|
16
18
|
* a missing provider is a wiring bug, not a fallback situation.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Density, DensityEntry, FontEntry, FontName, ThemeManifest } from "@cueplusplus/theme-base";
|
|
2
|
+
//#region src/system/vocabulary.d.ts
|
|
3
|
+
/** What the three registry hooks read: the manifests, and the axes the ACTIVE theme offers. */
|
|
4
|
+
interface ThemeRegistry {
|
|
5
|
+
/** Every registered manifest, in registration order. */
|
|
6
|
+
readonly themes: readonly ThemeManifest[];
|
|
7
|
+
/** First registered manifest's name, or `null` with nothing registered. */
|
|
8
|
+
readonly defaultTheme: string | null;
|
|
9
|
+
/** The rungs the ACTIVE theme offers: base five, then its adds. */
|
|
10
|
+
readonly densities: readonly DensityEntry[];
|
|
11
|
+
/** The pairings the ACTIVE theme offers, by the same rule. */
|
|
12
|
+
readonly fonts: readonly FontEntry[];
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
15
|
+
export { ThemeRegistry };
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { DEFAULT_DENSITY, DEFAULT_FONT } from "@cueplusplus/tokens";
|
|
2
|
+
import { DENSITIES, FONTS, THEME_NAME_PATTERN, resolveDensities, resolveFonts } from "@cueplusplus/theme-base";
|
|
3
|
+
//#region src/system/vocabulary.ts
|
|
4
|
+
/**
|
|
5
|
+
* The registry's pure half: what a set of manifests accepts, and what an app
|
|
6
|
+
* may offer under one of them.
|
|
7
|
+
*
|
|
8
|
+
* No `"use client"` and no React, on purpose. `prepaint.ts` is server-safe by
|
|
9
|
+
* contract — the docs app's root layout is a Server Component and calls
|
|
10
|
+
* `prepaintScript()` during render — and it reaches this module to inline the
|
|
11
|
+
* vocabulary into the blocking script. A module reached through a client
|
|
12
|
+
* boundary arrives in a server graph as a client *reference* rather than a
|
|
13
|
+
* function, and calling one throws at prerender. So the pure half lives here,
|
|
14
|
+
* directive-free, and `theme-registry.tsx` holds the context and the hooks and
|
|
15
|
+
* re-exports this file so the barrel still has one source.
|
|
16
|
+
* `test/system/server-safe.test.ts` holds this file, and every file
|
|
17
|
+
* `prepaint.ts` reaches, to that rule.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* What each axis accepts, and the one place that is decided.
|
|
21
|
+
*
|
|
22
|
+
* Six code paths validated a name before this existed — three against a frozen
|
|
23
|
+
* list from `@cueplusplus/tokens`, two against local copies, one against
|
|
24
|
+
* nothing at all — and they disagreed with each other at the edges. They all
|
|
25
|
+
* ask this now, so a stored preference means the same thing to the pre-paint
|
|
26
|
+
* script, the restore effect, and a portal.
|
|
27
|
+
*
|
|
28
|
+
* An empty registry is the deliberate case, not the degenerate one: an app that
|
|
29
|
+
* registers nothing has the base axes and no registry, and for one release that
|
|
30
|
+
* is every app that has not migrated — this repository's own docs site
|
|
31
|
+
* included. (It does not follow that such an app is unpainted: a stylesheet it
|
|
32
|
+
* imported may well declare the block the provider stamps. Colour is a question
|
|
33
|
+
* for CSS; this file only decides what a name is allowed to be.) Refusing every theme name there would reject a visitor's stored
|
|
34
|
+
* preference on first paint. So with nothing registered, theme validation is
|
|
35
|
+
* *off* (any name the selector grammar allows), and density and font validate
|
|
36
|
+
* against the base ladder and pairings alone. A registered set closes all
|
|
37
|
+
* three: only registered themes, and a theme's added rung or pairing only under
|
|
38
|
+
* that theme, which is what the cascade actually scopes it to.
|
|
39
|
+
*
|
|
40
|
+
* The order matters wherever a whole stored quadruple is read: resolve the
|
|
41
|
+
* theme first, then each axis under the theme resolution chose. See
|
|
42
|
+
* `acceptedPreferences()` in `theme-provider.tsx`, which is the one place that
|
|
43
|
+
* rule is applied, and the pre-paint script, which applies it the same way.
|
|
44
|
+
*/
|
|
45
|
+
function vocabulary(themes) {
|
|
46
|
+
const byName = /* @__PURE__ */ new Map();
|
|
47
|
+
for (const manifest of themes) if (!byName.has(manifest.name)) byName.set(manifest.name, manifest);
|
|
48
|
+
const names = themes.length === 0 ? null : [...byName.keys()];
|
|
49
|
+
const densitiesFor = (theme) => [...DENSITIES, ...Object.keys((theme === null ? void 0 : byName.get(theme))?.densities.adds ?? {})];
|
|
50
|
+
const fontsFor = (theme) => [...FONTS, ...Object.keys((theme === null ? void 0 : byName.get(theme))?.fontPairings.adds ?? {})];
|
|
51
|
+
const preferredDensity = (theme) => {
|
|
52
|
+
const wanted = (theme === null ? void 0 : byName.get(theme))?.densities.default;
|
|
53
|
+
return wanted !== void 0 && densitiesFor(theme).includes(wanted) ? wanted : DEFAULT_DENSITY;
|
|
54
|
+
};
|
|
55
|
+
const preferredFont = (theme) => {
|
|
56
|
+
const wanted = (theme === null ? void 0 : byName.get(theme))?.fontPairings.default;
|
|
57
|
+
return wanted !== void 0 && fontsFor(theme).includes(wanted) ? wanted : DEFAULT_FONT;
|
|
58
|
+
};
|
|
59
|
+
return {
|
|
60
|
+
themes: names,
|
|
61
|
+
densitiesFor,
|
|
62
|
+
fontsFor,
|
|
63
|
+
preferredDensity,
|
|
64
|
+
preferredFont,
|
|
65
|
+
acceptsTheme: (name) => typeof name === "string" && (names === null ? THEME_NAME_PATTERN.test(name) : names.includes(name)),
|
|
66
|
+
acceptsDensity: (theme, name) => typeof name === "string" && densitiesFor(theme).includes(name),
|
|
67
|
+
acceptsFont: (theme, name) => typeof name === "string" && fontsFor(theme).includes(name),
|
|
68
|
+
supportsLight: (theme) => theme === null ? true : byName.get(theme)?.supportsLight ?? true
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/** Derive the context value. Pure; the provider memoises it on `themes` and the active name. */
|
|
72
|
+
function buildRegistry(themes, active) {
|
|
73
|
+
return {
|
|
74
|
+
themes,
|
|
75
|
+
defaultTheme: themes[0]?.name ?? null,
|
|
76
|
+
densities: resolveDensities(themes, active),
|
|
77
|
+
fonts: resolveFonts(themes, active)
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const seen = /* @__PURE__ */ new Set();
|
|
81
|
+
/**
|
|
82
|
+
* A development warning, once per distinct message per process.
|
|
83
|
+
*
|
|
84
|
+
* Once, because the things this warns about — a `themes` prop nobody passed, a
|
|
85
|
+
* rung a theme does not offer — are configuration, and configuration is wrong
|
|
86
|
+
* the same way on every render. A warning per render is a warning nobody reads,
|
|
87
|
+
* and fourteen provider tests would otherwise be fourteen copies of one line.
|
|
88
|
+
*/
|
|
89
|
+
function warnOnce(message) {
|
|
90
|
+
if (process.env.NODE_ENV === "production" || seen.has(message)) return;
|
|
91
|
+
seen.add(message);
|
|
92
|
+
console.warn(`[cue] ${message}`);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* {@link warnOnce}, one tier quieter.
|
|
96
|
+
*
|
|
97
|
+
* `contrastReport()` keeps two lists on purpose — a *failure* is what makes a
|
|
98
|
+
* screen unreadable, an *advisory* is a focus ring nobody has been failed on
|
|
99
|
+
* yet — and folding the second into `console.warn` would either train a reader
|
|
100
|
+
* to ignore the first or push a repository to turn both off. They share the
|
|
101
|
+
* `seen` set so "once per distinct message" is one budget and `resetWarnings()`
|
|
102
|
+
* clears both; the two message texts cannot collide, since a failure and an
|
|
103
|
+
* advisory for one pair say different things about it.
|
|
104
|
+
*/
|
|
105
|
+
function noteOnce(message) {
|
|
106
|
+
if (process.env.NODE_ENV === "production" || seen.has(message)) return;
|
|
107
|
+
seen.add(message);
|
|
108
|
+
console.info(`[cue] ${message}`);
|
|
109
|
+
}
|
|
110
|
+
//#endregion
|
|
111
|
+
export { buildRegistry, noteOnce, vocabulary, warnOnce };
|
|
@@ -1,122 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
declare const WCAG_AA_NON_TEXT = 3;
|
|
4
|
-
/** Body-text AA. The floor for anything a reader is expected to read. */
|
|
5
|
-
declare const WCAG_AA_TEXT = 4.5;
|
|
6
|
-
/** Body-text AAA. What this portfolio holds its primary ink to. */
|
|
7
|
-
declare const WCAG_AAA_TEXT = 7;
|
|
8
|
-
/**
|
|
9
|
-
* WCAG relative luminance: 0 for black, 1 for white.
|
|
10
|
-
*
|
|
11
|
-
* Alpha is ignored — a translucent colour has no luminance of its own, only one
|
|
12
|
-
* in front of something. {@link contrastRatio} is where that composite happens.
|
|
13
|
-
*
|
|
14
|
-
* @param color - Any CSS colour string.
|
|
15
|
-
* @returns Relative luminance in `[0, 1]`.
|
|
16
|
-
* @throws TypeError if the value is not a colour.
|
|
17
|
-
* @example
|
|
18
|
-
* relativeLuminance("#767676"); // → 0.1845…
|
|
19
|
-
*/
|
|
20
|
-
declare function relativeLuminance(color: string): number;
|
|
21
|
-
/**
|
|
22
|
-
* WCAG contrast ratio between two colours, from 1:1 to 21:1.
|
|
23
|
-
*
|
|
24
|
-
* Symmetric, because a ratio has no direction — the arguments are named for
|
|
25
|
-
* readability, not for order. A **translucent foreground is composited over the
|
|
26
|
-
* background first**, which is not decoration: half this system's `fg-muted`
|
|
27
|
-
* tokens are white-alpha ink, and measuring them raw would report the ratio of
|
|
28
|
-
* pure white and pass everything.
|
|
29
|
-
*
|
|
30
|
-
* @param foreground - The ink. Composited over `background` if it has alpha.
|
|
31
|
-
* @param background - The ground. Its own alpha is ignored — nothing is behind it.
|
|
32
|
-
* @returns The ratio, `>= 1`.
|
|
33
|
-
* @throws TypeError naming whichever value could not be read.
|
|
34
|
-
* @example
|
|
35
|
-
* contrastRatio("#767676", "#ffffff"); // → 4.5422…
|
|
36
|
-
*/
|
|
37
|
-
declare function contrastRatio(foreground: string, background: string): number;
|
|
38
|
-
/** One pair a theme is checked on. */
|
|
39
|
-
interface ContrastRequirement {
|
|
40
|
-
/** `"<foreground>/<background>"` — stable, and readable in a failure message. */
|
|
41
|
-
id: string;
|
|
42
|
-
/** Bare token name of the ink. */
|
|
43
|
-
foreground: string;
|
|
44
|
-
/** Bare token name of the ground. */
|
|
45
|
-
background: string;
|
|
46
|
-
/** The ratio the pair has to reach. */
|
|
47
|
-
minimum: number;
|
|
48
|
-
/** Why this minimum, in one line — the configurator surfaces it. */
|
|
49
|
-
reason: string;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* The eleven pairs that decide whether a theme is usable.
|
|
53
|
-
*
|
|
54
|
-
* Not every pair in the palette: the ones a reader's comprehension actually
|
|
55
|
-
* depends on. Primary ink is held to AAA because it carries the prose; secondary
|
|
56
|
-
* ink and accent ink to AA because they carry labels; the five status colours
|
|
57
|
-
* and the live hue to the non-text AA floor because they are read as *signals*
|
|
58
|
-
* — a dot, a rim, a bar — never as body copy.
|
|
59
|
-
*
|
|
60
|
-
* `danger` and `warn` each appear twice, and they are the only tones that do.
|
|
61
|
-
* `ok`, `busy`, `info` and `stream` are *only* ever signals, so the non-text
|
|
62
|
-
* floor is the whole truth about them. The red and the amber are also grounds:
|
|
63
|
-
* they are the two tones this system fills a control with, and what they fill is
|
|
64
|
-
* the confirm on something irreversible and the confirm on something merely
|
|
65
|
-
* consequential. Text on either fill is text, so its ink is held to the text
|
|
66
|
-
* floor against the tone it sits on rather than against the page.
|
|
67
|
-
*
|
|
68
|
-
* `stream` is measured for the same reason `ok` is: `createTheme()` darkens it
|
|
69
|
-
* to this floor when it has to invent a light block, and a report that did not
|
|
70
|
-
* check what the derivation targets would be checking the wrong thing.
|
|
71
|
-
*/
|
|
72
|
-
declare const CONTRAST_REQUIREMENTS: readonly ContrastRequirement[];
|
|
73
|
-
/** One measured pair. */
|
|
74
|
-
interface ContrastCheck extends ContrastRequirement {
|
|
75
|
-
/** Which block the pair was measured in. */
|
|
76
|
-
mode: "dark" | "light";
|
|
77
|
-
/** The colour the foreground token resolved to. */
|
|
78
|
-
foregroundValue: string;
|
|
79
|
-
/** The colour the background token resolved to. */
|
|
80
|
-
backgroundValue: string;
|
|
81
|
-
/** The measured ratio. */
|
|
82
|
-
ratio: number;
|
|
83
|
-
/** Whether {@link ContrastRequirement.minimum} was reached. */
|
|
84
|
-
passes: boolean;
|
|
85
|
-
/** Whether the values were derived rather than authored — see the light variant. */
|
|
86
|
-
provisional: boolean;
|
|
87
|
-
}
|
|
88
|
-
/** One block's worth of resolved colours. */
|
|
89
|
-
interface ContrastInput {
|
|
90
|
-
/** Which block these are. */
|
|
91
|
-
mode: "dark" | "light";
|
|
92
|
-
/** Bare token name → resolved CSS colour. Pairs with a missing token are skipped. */
|
|
93
|
-
tokens: Readonly<Record<string, string>>;
|
|
94
|
-
/** Mark the resulting checks provisional. Defaults to `false`. */
|
|
95
|
-
provisional?: boolean;
|
|
96
|
-
}
|
|
97
|
-
/** Every pair, measured, plus the verdict. */
|
|
98
|
-
interface ContrastReport {
|
|
99
|
-
/** True when nothing failed. */
|
|
100
|
-
passes: boolean;
|
|
101
|
-
/** True when any measured block was derived rather than authored. */
|
|
102
|
-
provisional: boolean;
|
|
103
|
-
/** Every pair that could be measured, in requirement order, dark block first. */
|
|
104
|
-
checks: readonly ContrastCheck[];
|
|
105
|
-
/** The subset that failed. Empty when {@link ContrastReport.passes}. */
|
|
106
|
-
failures: readonly ContrastCheck[];
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Measure every requirement against every block handed in.
|
|
110
|
-
*
|
|
111
|
-
* A pair whose tokens are missing is **skipped**, not failed: the report says
|
|
112
|
-
* what it measured, and a caller checking a partial palette should not be told
|
|
113
|
-
* that a colour it never supplied is unreadable.
|
|
114
|
-
*
|
|
115
|
-
* @param inputs - One entry per block, dark first by convention.
|
|
116
|
-
* @returns The report.
|
|
117
|
-
* @example
|
|
118
|
-
* contrastReport([{ mode: "dark", tokens: { fg: "#fff", bg: "#000" } }]).passes; // → true
|
|
119
|
-
*/
|
|
120
|
-
declare function contrastReport(inputs: readonly ContrastInput[]): ContrastReport;
|
|
121
|
-
//#endregion
|
|
122
|
-
export { CONTRAST_REQUIREMENTS, ContrastCheck, ContrastInput, ContrastReport, ContrastRequirement, WCAG_AAA_TEXT, WCAG_AA_NON_TEXT, WCAG_AA_TEXT, contrastRatio, contrastReport, relativeLuminance };
|
|
1
|
+
import { CONTRAST_REQUIREMENTS, ContrastCheck, ContrastInput, ContrastReport, ContrastRequirement, WCAG_AAA_TEXT, WCAG_AA_NON_TEXT, WCAG_AA_TEXT, contrastRatio, contrastReport as contrastReport$1, relativeLuminance } from "@cueplusplus/theme-base/contrast";
|
|
2
|
+
export { CONTRAST_REQUIREMENTS, type ContrastCheck, type ContrastInput, type ContrastReport, type ContrastRequirement, WCAG_AAA_TEXT, WCAG_AA_NON_TEXT, WCAG_AA_TEXT, contrastRatio, contrastReport$1 as contrastReport, relativeLuminance };
|