@assure-one/design-system 1.36.0 → 1.38.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.
@@ -55,6 +55,8 @@ If a UI element exists in the design system, use it. Do not write `<button>`, `<
55
55
  | `flex gap-*` wrappers | `Stack`, `Inline`; `Surface` / `Card` for boxes |
56
56
  | `title="…"`, `window.confirm()`, `alert()` | `Tooltip`, `ConfirmActionButton` / `AlertDialog`, `useToast` |
57
57
 
58
+ Two shareable lint configs report the first three rows for you: `@assure-one/design-system/eslint-config` and `@assure-one/design-system/stylelint-config` (warn-level, experimental — see the README section "Lint configs for consumers").
59
+
58
60
  Before writing any element, look it up in the ["Pick a component"](./components.md#pick-a-component) table. The only raw elements that are fine are structural ones the design system does not model (`<main>`, `<article>`, `<ul>` for plain lists, `<img>` for content images, `<form>` itself).
59
61
 
60
62
  ## 3. Forms: the `Field` recipe
@@ -0,0 +1,210 @@
1
+ /**
2
+ * `@assure-one/design-system/eslint-config` (W7-18).
3
+ *
4
+ * The consumer-facing half of the in-repo ratchets: the four findings the
5
+ * design system ratchets on its own source (`scripts/ratchet/*` — raw platform
6
+ * controls, direct Radix imports, deep imports past the public entries,
7
+ * `--ds-*` redefinition outside a preset) reported in a product's own lint run.
8
+ *
9
+ * Every rule is **warn** by default, deliberately: a product adopts this on a
10
+ * codebase that already has hundreds of findings, records the count, and
11
+ * ratchets it down (W7-19). Raise the ones you have cleaned to "error" in your
12
+ * own config.
13
+ *
14
+ * Flat config only:
15
+ *
16
+ * import ds from "@assure-one/design-system/eslint-config";
17
+ * export default [...ds.configs.recommended];
18
+ *
19
+ * Or wire the plugin yourself: `plugins: { ds }` plus the rules you want.
20
+ */
21
+
22
+ const ENTRY_SUBPATHS = new Set([
23
+ "tokens",
24
+ "icons",
25
+ "next",
26
+ "testing",
27
+ "styles.css",
28
+ "package.json",
29
+ "eslint-config",
30
+ "stylelint-config",
31
+ ]);
32
+
33
+ /** The DS component(s) that own each raw element (scripts/ratchet/lib/raw-controls-owners.mjs). */
34
+ const RAW_CONTROLS = {
35
+ button: "Button, IconButton, LinkButton, SubmitButton or Pressable",
36
+ input: "Input, NumberInput, SearchInput, Checkbox, RadioGroup, Switch, Slider, DatePicker…",
37
+ select: "Select, Combobox or MultiSelect",
38
+ textarea: "Textarea",
39
+ };
40
+
41
+ const DS_PROPERTY = /^--ds-/;
42
+ const DEFAULT_PRESET_PATH = "(^|/)presets/";
43
+
44
+ const isStringLiteral = (node) => node?.type === "Literal" && typeof node.value === "string";
45
+
46
+ /** The `--ds-*` name a property key or JSX attribute declares, or undefined. */
47
+ function dsPropertyName(key, computed) {
48
+ if (computed) return undefined;
49
+ const name = isStringLiteral(key) ? key.value : key?.type === "Identifier" ? key.name : undefined;
50
+ return name && DS_PROPERTY.test(name) ? name : undefined;
51
+ }
52
+
53
+ /** Import-like source string of a node, or undefined. */
54
+ function importSource(node) {
55
+ if (node.type === "ImportDeclaration" || node.type === "ExportNamedDeclaration") {
56
+ return isStringLiteral(node.source) ? node.source.value : undefined;
57
+ }
58
+ if (node.type === "ExportAllDeclaration") {
59
+ return isStringLiteral(node.source) ? node.source.value : undefined;
60
+ }
61
+ return undefined;
62
+ }
63
+
64
+ const importVisitors = (check) => ({
65
+ ImportDeclaration: (node) => check(node, importSource(node)),
66
+ ExportNamedDeclaration: (node) => check(node, importSource(node)),
67
+ ExportAllDeclaration: (node) => check(node, importSource(node)),
68
+ ImportExpression: (node) =>
69
+ check(node, isStringLiteral(node.source) ? node.source.value : undefined),
70
+ });
71
+
72
+ /** @type {import("eslint").Rule.RuleModule} */
73
+ const noRawControls = {
74
+ meta: {
75
+ type: "problem",
76
+ docs: {
77
+ description:
78
+ "Use the design system's control instead of a raw <button>, <input>, <select> or <textarea>.",
79
+ },
80
+ schema: [],
81
+ messages: {
82
+ raw: "<{{ element }}> is owned by the design system. Use {{ use }} (docs/components.md → “Never hand-roll”).",
83
+ },
84
+ },
85
+ create(context) {
86
+ return {
87
+ JSXOpeningElement(node) {
88
+ if (node.name?.type !== "JSXIdentifier") return;
89
+ const element = node.name.name;
90
+ const use = RAW_CONTROLS[element];
91
+ if (!use) return;
92
+ context.report({ node, messageId: "raw", data: { element, use } });
93
+ },
94
+ };
95
+ },
96
+ };
97
+
98
+ /** @type {import("eslint").Rule.RuleModule} */
99
+ const noRadixImports = {
100
+ meta: {
101
+ type: "problem",
102
+ docs: {
103
+ description: "Import the design system's primitive instead of the Radix package it wraps.",
104
+ },
105
+ schema: [],
106
+ messages: {
107
+ radix:
108
+ '"{{ source }}" is wrapped by the design system. Import the primitive from "@assure-one/design-system" so behaviour, tokens and the Radix version stay in one place.',
109
+ },
110
+ },
111
+ create(context) {
112
+ return importVisitors((node, source) => {
113
+ if (!source?.startsWith("@radix-ui/")) return;
114
+ context.report({ node, messageId: "radix", data: { source } });
115
+ });
116
+ },
117
+ };
118
+
119
+ /** @type {import("eslint").Rule.RuleModule} */
120
+ const noDeepImports = {
121
+ meta: {
122
+ type: "problem",
123
+ docs: {
124
+ description:
125
+ "Import from a published design-system entry, never from dist/ or src/ (ADR-011).",
126
+ },
127
+ schema: [],
128
+ messages: {
129
+ deep: '"{{ source }}" is not a published entry. Import from "@assure-one/design-system" or one of {{ entries }} — anything else may change without a major release (ADR-011).',
130
+ },
131
+ },
132
+ create(context) {
133
+ const entries = [...ENTRY_SUBPATHS].map((s) => `/${s}`).join(", ");
134
+ return importVisitors((node, source) => {
135
+ if (!source?.startsWith("@assure-one/design-system/")) return;
136
+ const subpath = source.slice("@assure-one/design-system/".length);
137
+ if (ENTRY_SUBPATHS.has(subpath) || subpath.startsWith("css/")) return;
138
+ context.report({ node, messageId: "deep", data: { source, entries } });
139
+ });
140
+ },
141
+ };
142
+
143
+ /** @type {import("eslint").Rule.RuleModule} */
144
+ const noDsTokenRedefinition = {
145
+ meta: {
146
+ type: "problem",
147
+ docs: {
148
+ description:
149
+ "Set a `--ds-*` custom property only in a theme preset, never inline in a component.",
150
+ },
151
+ schema: [
152
+ {
153
+ type: "object",
154
+ properties: { presetPath: { type: "string" } },
155
+ additionalProperties: false,
156
+ },
157
+ ],
158
+ messages: {
159
+ redefined:
160
+ "{{ name }} is a design-system token. Override it in a theme preset (a file under presets/, or ThemeScope/BrandScope), not inline: an inline value escapes the theme and the token contract.",
161
+ },
162
+ },
163
+ create(context) {
164
+ const presetPath = new RegExp(context.options?.[0]?.presetPath ?? DEFAULT_PRESET_PATH);
165
+ const filename = (context.filename ?? context.getFilename?.() ?? "").replace(/\\/g, "/");
166
+ if (presetPath.test(filename)) return {};
167
+ const report = (node, name) => context.report({ node, messageId: "redefined", data: { name } });
168
+ return {
169
+ Property(node) {
170
+ const name = dsPropertyName(node.key, node.computed);
171
+ if (name) report(node, name);
172
+ },
173
+ JSXAttribute(node) {
174
+ if (node.name?.type !== "JSXNamespacedName") return;
175
+ const name = `${node.name.namespace?.name}:${node.name.name?.name}`;
176
+ if (DS_PROPERTY.test(name)) report(node, name);
177
+ },
178
+ };
179
+ },
180
+ };
181
+
182
+ /** The `ds` plugin: the rules alone, for a config that wires them itself. */
183
+ export const plugin = {
184
+ meta: { name: "@assure-one/design-system/eslint-config" },
185
+ rules: {
186
+ "no-raw-controls": noRawControls,
187
+ "no-radix-imports": noRadixImports,
188
+ "no-deep-imports": noDeepImports,
189
+ "no-ds-token-redefinition": noDsTokenRedefinition,
190
+ },
191
+ };
192
+
193
+ /** Warn-level rule table (W7-18): every rule this config ships. */
194
+ export const rules = Object.fromEntries(
195
+ Object.keys(plugin.rules).map((name) => [`ds/${name}`, "warn"]),
196
+ );
197
+
198
+ /** Flat config: `export default [...ds.configs.recommended]`. */
199
+ export const configs = {
200
+ recommended: [
201
+ {
202
+ name: "@assure-one/design-system/recommended",
203
+ files: ["**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}"],
204
+ plugins: { ds: plugin },
205
+ rules,
206
+ },
207
+ ],
208
+ };
209
+
210
+ export default { plugin, rules, configs };
@@ -0,0 +1,54 @@
1
+ /**
2
+ * The two stylelint checks of W7-18, as plain PostCSS finders.
3
+ *
4
+ * Kept free of any `stylelint` import so they can be exercised directly (and
5
+ * reused by the repo's own CSS tooling): `stylelint.mjs` wraps them into the
6
+ * published plugin. They mirror the `css-hygiene` ratchet family's concerns
7
+ * (`scripts/ratchet/lib/css-hygiene-analyse.mjs`) from the consumer side.
8
+ */
9
+
10
+ /** A declaration whose property is a design-system token. */
11
+ const DS_PROPERTY = /^--ds-/;
12
+
13
+ /**
14
+ * Selectors that reach design-system DOM: a `ds:`-prefixed utility class
15
+ * (written `.ds\:flex` in CSS), the `ds-` component classes, and the
16
+ * `data-slot` / `data-ds-*` attributes components expose (ADR-008).
17
+ */
18
+ const DS_SELECTOR = /\.ds\\?:|\.ds-|\[data-slot|\[data-ds-/;
19
+
20
+ /** The nearest enclosing rule's selector, or undefined at the top level. */
21
+ function enclosingSelector(node) {
22
+ for (let parent = node.parent; parent; parent = parent.parent) {
23
+ if (parent.type === "rule") return parent.selector;
24
+ }
25
+ return undefined;
26
+ }
27
+
28
+ /** Every `--ds-*` declaration in a stylesheet: `[{ prop, decl }]`. */
29
+ export function dsTokenDeclarations(root) {
30
+ const found = [];
31
+ root.walkDecls((decl) => {
32
+ if (DS_PROPERTY.test(decl.prop)) found.push({ prop: decl.prop, decl });
33
+ });
34
+ return found;
35
+ }
36
+
37
+ /** Every `!important` declaration under a design-system selector: `[{ prop, selector, decl }]`. */
38
+ export function importantOnDsSelectors(root) {
39
+ const found = [];
40
+ root.walkDecls((decl) => {
41
+ if (!decl.important) return;
42
+ const selector = enclosingSelector(decl);
43
+ if (!selector || !DS_SELECTOR.test(selector)) return;
44
+ found.push({ prop: decl.prop, selector, decl });
45
+ });
46
+ return found;
47
+ }
48
+
49
+ export const messages = {
50
+ dsToken: (prop) =>
51
+ `${prop} is a design-system token. Set it in a theme preset (the block \`pnpm derive-consumer-preset\` writes, or ThemeScope/BrandScope), not in app CSS — an override here escapes the theme and the token contract.`,
52
+ important: (prop, selector) =>
53
+ `Avoid \`!important\` on a design-system selector (\`${selector}\` → ${prop}). Pass \`className\`/\`classNames\` instead: consumer classes already win by cascade (ADR-004).`,
54
+ };
@@ -0,0 +1,73 @@
1
+ /**
2
+ * `@assure-one/design-system/stylelint-config` (W7-18).
3
+ *
4
+ * The CSS half of the consumer lint surface: the two things a product's own
5
+ * stylesheet does to the design system that the `css-hygiene` ratchet catches
6
+ * inside this repository — redefining a `--ds-*` token outside a theme preset,
7
+ * and `!important` on a design-system selector.
8
+ *
9
+ * Both are **warnings** (the `severity: "warning"` below), for the same reason
10
+ * the ESLint rules are: a product adopts this on CSS that already has
11
+ * findings, records the count, and ratchets it down (W7-19).
12
+ *
13
+ * // stylelint.config.mjs
14
+ * import ds from "@assure-one/design-system/stylelint-config";
15
+ * export default { extends: [], ...ds };
16
+ *
17
+ * `stylelint` itself is the consumer's (an optional peer dependency here).
18
+ * Preset files are exempt: override `ignoreFiles`/`overrides` to move them.
19
+ */
20
+ import stylelint from "stylelint";
21
+
22
+ import { dsTokenDeclarations, importantOnDsSelectors, messages } from "./stylelint-rules.mjs";
23
+
24
+ const NAMESPACE = "assure-ds";
25
+
26
+ const plugin = (ruleName, find, message) =>
27
+ stylelint.createPlugin(`${NAMESPACE}/${ruleName}`, (primary, secondary, context) => {
28
+ const name = `${NAMESPACE}/${ruleName}`;
29
+ return (root, result) => {
30
+ if (!primary) return;
31
+ for (const found of find(root)) {
32
+ stylelint.utils.report({
33
+ result,
34
+ ruleName: name,
35
+ node: found.decl,
36
+ word: found.prop,
37
+ message: message(found),
38
+ severity: secondary?.severity ?? context?.severity ?? "warning",
39
+ });
40
+ }
41
+ };
42
+ });
43
+
44
+ /** The two plugin rules, for a config that wires them itself. */
45
+ export const plugins = [
46
+ plugin("no-ds-token-redefinition", dsTokenDeclarations, ({ prop }) => messages.dsToken(prop)),
47
+ plugin("no-important-on-ds-selectors", importantOnDsSelectors, ({ prop, selector }) =>
48
+ messages.important(prop, selector),
49
+ ),
50
+ ];
51
+
52
+ /** Warn-level rule table (W7-18). */
53
+ export const rules = {
54
+ [`${NAMESPACE}/no-ds-token-redefinition`]: [true, { severity: "warning" }],
55
+ [`${NAMESPACE}/no-important-on-ds-selectors`]: [true, { severity: "warning" }],
56
+ };
57
+
58
+ /**
59
+ * The shareable config. Theme presets are where `--ds-*` belongs, so any file
60
+ * under a `presets/` directory (or named `*.preset.css`) is exempt.
61
+ */
62
+ export const config = {
63
+ plugins,
64
+ rules,
65
+ overrides: [
66
+ {
67
+ files: ["**/presets/**/*.css", "**/*.preset.css"],
68
+ rules: { [`${NAMESPACE}/no-ds-token-redefinition`]: null },
69
+ },
70
+ ],
71
+ };
72
+
73
+ export default config;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assure-one/design-system",
3
- "version": "1.36.0",
3
+ "version": "1.38.0",
4
4
  "description": "Assure One design system — tokens, primitives, and patterns for the Assure Suite.",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,
@@ -45,12 +45,15 @@
45
45
  "default": "./dist/testing/index.cjs"
46
46
  }
47
47
  },
48
+ "./eslint-config": "./eslint-config/index.mjs",
49
+ "./stylelint-config": "./eslint-config/stylelint.mjs",
48
50
  "./package.json": "./package.json"
49
51
  },
50
52
  "files": [
51
53
  "dist",
52
54
  "codemods",
53
55
  "!codemods/__test__",
56
+ "eslint-config",
54
57
  "docs/components.md",
55
58
  "docs/components.registry.json",
56
59
  "docs/for-ai-agents.md",
@@ -65,12 +68,16 @@
65
68
  },
66
69
  "peerDependencies": {
67
70
  "next": ">=15",
71
+ "stylelint": ">=16",
68
72
  "react": ">=19",
69
73
  "react-dom": ">=19"
70
74
  },
71
75
  "peerDependenciesMeta": {
72
76
  "next": {
73
77
  "optional": true
78
+ },
79
+ "stylelint": {
80
+ "optional": true
74
81
  }
75
82
  },
76
83
  "dependencies": {
@@ -1,220 +0,0 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { ComponentType, AnchorHTMLAttributes, Ref, ReactNode, ImgHTMLAttributes } from 'react';
3
-
4
- /**
5
- * L0 foundation (W5-20, ADR-010). The typed messages catalogue every
6
- * design-system component reads its generic strings from (target architecture
7
- * §38): accessibility and micro-copy with a universal meaning — Clear, No
8
- * results, Loading, optional — never product copy, which is always the
9
- * consumer's (`children`, `label`, `placeholder`).
10
- *
11
- * The catalogue is keyed by component namespace; each namespace is the
12
- * `*Messages` interface that component already accepted through its
13
- * `messages` prop before the provider existed, so the resolution order is one
14
- * rule everywhere: **prop > provider > English default**. A value is a string
15
- * or, where a parameter is involved, a function of that parameter (no string
16
- * concatenation or pluralisation inside components).
17
- *
18
- * The English defaults live in `./en.ts`; `./en.json` is generated from it
19
- * (`pnpm messages:generate`) so translators start from the full list.
20
- */
21
- /** The copy a `Select` renders. @experimental */
22
- interface SelectMessages {
23
- /** The accessible name of the `clearable` button. Default `"Clear selection"`. */
24
- clear: string;
25
- }
26
- /** The copy a `SearchInput` renders. @experimental */
27
- interface SearchInputMessages {
28
- /** The accessible name of the clear button. Default `"Clear search"`. */
29
- clear: string;
30
- }
31
- /** The copy a `Combobox` (and `MultiSelect`) renders. @experimental */
32
- interface ComboboxMessages {
33
- /** The empty state when the query matches nothing. */
34
- empty: string;
35
- /** The loading state while `loading`. */
36
- loading: string;
37
- /** The label of the create row for a query. */
38
- create: (query: string) => string;
39
- /** The accessible name of the clear button. */
40
- clear: string;
41
- /** The accessible name of the toggle button. */
42
- toggle: string;
43
- /** The accessible name of a chip's remove button (`multiple`). */
44
- remove: (label: string) => string;
45
- }
46
- /**
47
- * The copy a `Field` renders (target architecture §24.5).
48
- *
49
- * @experimental
50
- */
51
- interface FieldMessages {
52
- /** The indicator after the label of an optional field. Default `"optional"`, rendered in parentheses. */
53
- optional: string;
54
- }
55
- /** The default dropzone copy a `FileUpload` renders when it has no `children`. @experimental */
56
- interface FileUploadMessages {
57
- /** The emphasised call to action. Default `"Click to upload"`. */
58
- prompt: string;
59
- /** The rest of the sentence after the prompt. Default `" or drag and drop"`. */
60
- dragHint: string;
61
- /** The accepted-types line; receives the `accept` list. */
62
- accepted: (accept: string) => string;
63
- /** The size-limit line; receives the formatted limit (e.g. `"10 MB"`). */
64
- maxSize: (size: string) => string;
65
- }
66
- /**
67
- * The whole catalogue, one namespace per component. This is the type a
68
- * consumer's i18n lint can check for completeness.
69
- *
70
- * @experimental
71
- */
72
- interface DsMessages {
73
- select: SelectMessages;
74
- searchInput: SearchInputMessages;
75
- combobox: ComboboxMessages;
76
- field: FieldMessages;
77
- fileUpload: FileUploadMessages;
78
- }
79
- /** A component namespace of the catalogue. @experimental */
80
- type DsMessageNamespace = keyof DsMessages;
81
- /**
82
- * What `DesignSystemProvider messages` accepts: any subset of namespaces, each
83
- * any subset of its keys. Every omitted key keeps its English default.
84
- *
85
- * @experimental
86
- */
87
- type DsMessagesOverride = {
88
- [N in DsMessageNamespace]?: Partial<DsMessages[N]>;
89
- };
90
-
91
- /**
92
- * L0 foundation (W5-21, ADR-010). The adapters through which the design
93
- * system renders an application's router link and image component without
94
- * importing them itself.
95
- *
96
- * `LinkButton` and `Logo` read these from `DesignSystemProvider`. Without a
97
- * provider they keep their static `next/link` / `next/image` imports (C-NEXT-PEER)
98
- * until 2.0, so nothing changes for a Next.js app that renders no provider;
99
- * `@assure-one/design-system/next` exports a provider that passes exactly
100
- * those two, and a non-Next app passes its own.
101
- */
102
- /** The props a link adapter receives: a plain anchor with an `href`. @experimental */
103
- interface LinkComponentProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
104
- href: string;
105
- ref?: Ref<HTMLAnchorElement>;
106
- children?: ReactNode;
107
- }
108
- /**
109
- * A component that renders a link — `next/link`, React Router's `Link`, or a
110
- * plain `<a>`. It must forward every anchor attribute and the ref.
111
- *
112
- * @experimental
113
- */
114
- type LinkComponent = ComponentType<LinkComponentProps>;
115
- /** The props an image adapter receives: `src`, `alt` and the intrinsic size. @experimental */
116
- interface ImageComponentProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, "src" | "width" | "height"> {
117
- src: string;
118
- alt: string;
119
- width: number;
120
- height: number;
121
- /** Load eagerly, above the fold (`next/image` `priority`). */
122
- priority?: boolean;
123
- }
124
- /**
125
- * A component that renders an image — `next/image` or a plain `<img>`.
126
- *
127
- * @experimental
128
- */
129
- type ImageComponent = ComponentType<ImageComponentProps>;
130
-
131
- /** Text direction the provider announces. Context only until components adopt logical properties (W5-22). @experimental */
132
- type DsDirection = "ltr" | "rtl";
133
- /**
134
- * The resolved messages of one component namespace: the component's own
135
- * `messages` prop wins over the provider's catalogue, which wins over the
136
- * English default. Works without a provider (English). Stable while the three
137
- * inputs are.
138
- *
139
- * @experimental
140
- */
141
- declare function useDsMessages<N extends DsMessageNamespace>(namespace: N, override?: Partial<DsMessages[N]> | undefined): DsMessages[N];
142
-
143
- /**
144
- * Props of `DesignSystemProvider` (W5-20, ADR-010, target architecture §43).
145
- *
146
- * @experimental
147
- */
148
- interface DesignSystemProviderProps {
149
- /**
150
- * Overrides for the generic strings components render (Clear, No results,
151
- * optional…), by component namespace. Any subset; omitted keys keep their
152
- * English default. A component's own `messages` prop still wins. The full
153
- * list is `DsMessages` (and `messages/en.json` in the package source).
154
- */
155
- messages?: DsMessagesOverride;
156
- /**
157
- * The BCP 47 tag locale-aware components format with (dates, numbers).
158
- * Default `"en-US"` on the server and on the client (amendment A3): pass the
159
- * locale your app already knows server-side; never `navigator.language`
160
- * during render.
161
- */
162
- locale?: string;
163
- /** Text direction. Context only for now — components adopt logical properties in W5-22. */
164
- dir?: DsDirection;
165
- /**
166
- * Where portalled content (dialogs, menus, tooltips) mounts. `undefined`
167
- * (default) keeps Radix's `document.body`. Read by the shared Portal of the
168
- * overlay primitives as they adopt it.
169
- */
170
- portalContainer?: HTMLElement | null;
171
- /**
172
- * The application's router link, rendered by `LinkButton` and `Logo`
173
- * instead of their static `next/link` import (W5-21). A Next.js app uses
174
- * `NextDesignSystemProvider` from `@assure-one/design-system/next`.
175
- */
176
- linkComponent?: LinkComponent;
177
- /** The application's image component, rendered by `Logo` instead of its static `next/image` import. */
178
- imageComponent?: ImageComponent;
179
- /**
180
- * Shared tooltip defaults (`delayDuration`, `skipDelayDuration`,
181
- * `disableHoverableContent`) for tooltips built directly on the Radix
182
- * root. `false` mounts no `TooltipProvider`. The design system's own
183
- * `Tooltip` still wraps its own provider today, so it is unaffected.
184
- */
185
- tooltip?: TooltipDefaults | false;
186
- /**
187
- * Mount the toast region (`ToastProvider`) so `useToast()` works anywhere
188
- * below. Off by default: an app that already renders `ToastProvider` keeps
189
- * its single region (the `aria-label="Notifications"` region consumers
190
- * select on, C-TOAST-ARIALABEL, must exist exactly once).
191
- */
192
- toasts?: boolean;
193
- children?: ReactNode;
194
- }
195
- /** The Radix `TooltipProvider` props the provider forwards. @experimental */
196
- interface TooltipDefaults {
197
- delayDuration?: number;
198
- skipDelayDuration?: number;
199
- disableHoverableContent?: boolean;
200
- }
201
- /**
202
- * The optional application-level provider of the design system (W5-20,
203
- * ADR-010). Thin by design: it owns only what CSS cannot — the messages
204
- * catalogue, the locale, the text direction, the portal container, the link
205
- * and image adapters, shared tooltip defaults and (opt-in) the toast region.
206
- * Colour scheme, brand and density stay attributes + CSS on `<html>`.
207
- *
208
- * Everything works identically without it: every component falls back to
209
- * the English catalogue, `"en-US"`, `document.body` and its static `next/*`
210
- * import. The provider renders **no DOM wrapper** (only the toast region when
211
- * `toasts` is set), so it cannot cause a hydration mismatch.
212
- *
213
- * Reserved: the `useConfirm` / `usePrompt` host (W5-18) mounts here once it
214
- * exists — see the comment in the render tree.
215
- *
216
- * @experimental
217
- */
218
- declare function DesignSystemProvider({ messages, locale, dir, portalContainer, linkComponent, imageComponent, tooltip, toasts, children, }: DesignSystemProviderProps): react_jsx_runtime.JSX.Element;
219
-
220
- export { type ComboboxMessages as C, type DesignSystemProviderProps as D, type FileUploadMessages as F, type ImageComponent as I, type LinkComponent as L, type SearchInputMessages as S, type TooltipDefaults as T, type SelectMessages as a, type FieldMessages as b, DesignSystemProvider as c, type DsDirection as d, type DsMessageNamespace as e, type DsMessages as f, type DsMessagesOverride as g, type ImageComponentProps as h, type LinkComponentProps as i, useDsMessages as u };