@salesforcedevs/docs-components 1.34.4-topic-alpha.5 → 1.34.5-tlv-alpha-1

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/package.json CHANGED
@@ -1,29 +1,30 @@
1
1
  {
2
- "name": "@salesforcedevs/docs-components",
3
- "version": "1.34.4-topic-alpha.5",
4
- "description": "Docs Lightning web components for DSC",
5
- "license": "MIT",
6
- "main": "index.js",
7
- "engines": {
8
- "node": "22.x"
9
- },
10
- "publishConfig": {
11
- "access": "public"
12
- },
13
- "dependencies": {
14
- "@api-components/amf-helper-mixin": "4.5.29",
15
- "classnames": "2.5.1",
16
- "dompurify": "3.2.4",
17
- "kagekiri": "1.4.2",
18
- "lodash.orderby": "4.6.0",
19
- "lodash.uniqby": "4.7.0",
20
- "query-string": "7.1.3",
21
- "sentence-case": "3.0.4"
22
- },
23
- "devDependencies": {
24
- "@types/classnames": "2.3.1",
25
- "@types/lodash.orderby": "4.6.9",
26
- "@types/lodash.uniqby": "4.7.9"
27
- },
28
- "gitHead": "4629fdd9ca18a13480044ad43515b91945d16aad"
29
- }
2
+ "name": "@salesforcedevs/docs-components",
3
+ "version": "1.34.5-tlv-alpha-1",
4
+ "description": "Docs Lightning web components for DSC",
5
+ "license": "MIT",
6
+ "main": "index.js",
7
+ "engines": {
8
+ "node": "22.x"
9
+ },
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "dependencies": {
14
+ "@api-components/amf-helper-mixin": "4.5.29",
15
+ "classnames": "2.5.1",
16
+ "dompurify": "3.2.4",
17
+ "kagekiri": "1.4.2",
18
+ "lodash.orderby": "4.6.0",
19
+ "lodash.uniqby": "4.7.0",
20
+ "query-string": "7.1.3",
21
+ "sentence-case": "3.0.4"
22
+ },
23
+ "devDependencies": {
24
+ "@types/classnames": "2.3.1",
25
+ "@types/lodash.orderby": "4.6.9",
26
+ "@types/lodash.uniqby": "4.7.9"
27
+ },
28
+ "gitHead": "4629fdd9ca18a13480044ad43515b91945d16aad",
29
+ "stableVersion": "1.34.4"
30
+ }
@@ -16,8 +16,6 @@
16
16
  }
17
17
 
18
18
  .icon {
19
- --dx-c-icon-size: var(--dx-g-icon-size-lg);
20
-
21
19
  flex-shrink: 0;
22
20
  margin-top: var(--dx-g-spacing-smd);
23
21
  margin-right: var(--dx-g-spacing-sm);
@@ -73,6 +71,47 @@
73
71
  cursor: pointer;
74
72
  }
75
73
 
74
+ /* Centered variant: the message is centered in the banner and the icon hangs
75
+ to its left. The close button is taken out of the flow so it doesn't offset
76
+ the centering. */
77
+ .container_centered {
78
+ position: relative;
79
+ display: grid;
80
+ grid-template-columns: 1fr auto 1fr;
81
+ align-items: center;
82
+
83
+ /* Reserve the close button's width plus its right inset on both sides */
84
+ padding: 0
85
+ calc(
86
+ var(--dx-g-spacing-3xl) + var(--dx-g-spacing-xs) +
87
+ var(--doc-banner-padding-right)
88
+ );
89
+ }
90
+
91
+ .container_centered .icon {
92
+ grid-column: 1;
93
+ justify-self: end;
94
+ margin-top: 0;
95
+ }
96
+
97
+ .container_centered .main {
98
+ grid-column: 2;
99
+ align-items: center;
100
+ justify-content: center;
101
+ }
102
+
103
+ .container_centered .message {
104
+ text-align: center;
105
+ }
106
+
107
+ .container_centered .close {
108
+ position: absolute;
109
+ top: 50%;
110
+ right: var(--doc-banner-padding-right);
111
+ margin-left: 0;
112
+ transform: translateY(-50%);
113
+ }
114
+
76
115
  @media (max-width: 1279px) {
77
116
  :host {
78
117
  --doc-banner-padding-left: var(--dx-g-spacing-xl);
@@ -1,16 +1,17 @@
1
1
  <template>
2
2
  <template lwc:if={showBanner}>
3
- <div class="container" part="container">
3
+ <div class={containerClass} part="container">
4
4
  <dx-icon
5
5
  class="icon"
6
- symbol="info"
7
- size="override"
8
- color="gray-50"
6
+ sprite={iconSprite}
7
+ symbol={iconSymbol}
8
+ size={iconSize}
9
+ color={iconColor}
9
10
  part="icon"
10
11
  ></dx-icon>
11
12
  <div class="main">
12
13
  <div class="message" part="message" lwc:dom="manual"></div>
13
- <div class="actions" part="actions">
14
+ <div lwc:if={hasActions} class="actions" part="actions">
14
15
  <template lwc:if={hasPrimaryButton}>
15
16
  <dx-button
16
17
  href={buttonHref}
@@ -1,4 +1,6 @@
1
1
  import { LightningElement, api } from "lwc";
2
+ import { IconSize, IconSprite, IconSymbol } from "typings/custom";
3
+ import { normalizeBoolean } from "dxUtils/normalizers";
2
4
 
3
5
  const BANNER_STORAGE_PREFIX = "doc-banner-";
4
6
 
@@ -13,6 +15,24 @@ export default class Banner extends LightningElement {
13
15
 
14
16
  @api dismissStorageKey = "";
15
17
 
18
+ @api
19
+ get centered(): boolean {
20
+ return this._centered;
21
+ }
22
+ set centered(value: boolean | string) {
23
+ this._centered = normalizeBoolean(value);
24
+ }
25
+
26
+ @api iconSprite: IconSprite = "utility";
27
+
28
+ @api iconSymbol: IconSymbol = "info";
29
+
30
+ @api iconColor = "gray-50";
31
+
32
+ @api iconSize: IconSize = "large";
33
+
34
+ private _centered = false;
35
+
16
36
  private _dismissed = false;
17
37
 
18
38
  connectedCallback() {
@@ -33,10 +53,18 @@ export default class Banner extends LightningElement {
33
53
  }
34
54
  }
35
55
 
56
+ get containerClass(): string {
57
+ return this._centered ? "container container_centered" : "container";
58
+ }
59
+
36
60
  get showBanner(): boolean {
37
61
  return !this._dismissed;
38
62
  }
39
63
 
64
+ get hasActions(): boolean {
65
+ return this.hasPrimaryButton || this.hasSecondaryAction;
66
+ }
67
+
40
68
  get hasPrimaryButton(): boolean {
41
69
  return !!(this.buttonLabel && this.buttonHref);
42
70
  }
@@ -21,7 +21,6 @@ nav {
21
21
 
22
22
  dx-dropdown {
23
23
  --dx-c-dropdown-option-font-size: var(--dx-g-text-sm);
24
- --dx-c-dropdown-option-font-weight: var(--dx-g-font-bold);
25
24
  --dx-c-dropdown-option-label-color: var(
26
25
  --dx-c-breadcrumbs-breadcrumb-color
27
26
  );
@@ -127,26 +127,26 @@ export default class ContentActionToolbar extends LightningElement {
127
127
  {
128
128
  id: ACTION_COPY_MARKDOWN,
129
129
  label: COPY_MARKDOWN_LABEL,
130
- endIconSprite: "utility",
131
- endIconSymbol: "copy"
130
+ iconSprite: "utility",
131
+ iconSymbol: "copy"
132
132
  },
133
133
  {
134
134
  id: ACTION_VIEW_MARKDOWN,
135
135
  label: VIEW_MARKDOWN_LABEL,
136
- endIconSprite: "utility",
137
- endIconSymbol: "new_window"
136
+ iconSprite: "utility",
137
+ iconSymbol: "new_window"
138
138
  },
139
139
  {
140
140
  id: ACTION_COPY_MARKDOWN_URL,
141
141
  label: COPY_MARKDOWN_URL_LABEL,
142
- endIconSprite: "utility",
143
- endIconSymbol: "link"
142
+ iconSprite: "utility",
143
+ iconSymbol: "link"
144
144
  },
145
145
  ...AI_ASSISTANTS.map(({ action, label }) => ({
146
146
  id: action,
147
147
  label,
148
- endIconSprite: "utility",
149
- endIconSymbol: "new_window"
148
+ iconSprite: "utility",
149
+ iconSymbol: "new_window"
150
150
  }))
151
151
  ]
152
152
  });
@@ -159,14 +159,14 @@ export default class ContentActionToolbar extends LightningElement {
159
159
  {
160
160
  id: ACTION_VIEW_LLMS,
161
161
  label: VIEW_LLMS_LABEL,
162
- endIconSprite: "utility",
163
- endIconSymbol: "new_window"
162
+ iconSprite: "utility",
163
+ iconSymbol: "new_window"
164
164
  },
165
165
  {
166
166
  id: ACTION_COPY_LLMS_URL,
167
167
  label: COPY_LLMS_URL_LABEL,
168
- endIconSprite: "utility",
169
- endIconSymbol: "link"
168
+ iconSprite: "utility",
169
+ iconSymbol: "link"
170
170
  }
171
171
  ]
172
172
  });
@@ -5,6 +5,7 @@ import { HeaderBase } from "dxBaseElements/headerBase";
5
5
  import { toJson, normalizeBoolean } from "dxUtils/normalizers";
6
6
  import { track } from "dxUtils/analytics";
7
7
 
8
+ const TABLET_MATCH = "980px";
8
9
  const MOBILE_MATCH = "768px";
9
10
 
10
11
  const isStorybook = () => {
@@ -46,6 +47,8 @@ export default class Header extends HeaderBase {
46
47
  }
47
48
 
48
49
  private _scopedNavItems!: OptionWithNested[];
50
+ private tablet = false;
51
+ private tabletMatchMedia!: MediaQueryList;
49
52
  private shouldRender: boolean = false;
50
53
  private showDocDivider: boolean = false;
51
54
  private _devCenter!: DevCenterConfig;
@@ -80,6 +83,11 @@ export default class Header extends HeaderBase {
80
83
 
81
84
  connectedCallback(): void {
82
85
  super.connectedCallback();
86
+ this.tabletMatchMedia = window.matchMedia(
87
+ `(max-width: ${TABLET_MATCH})`
88
+ );
89
+ this.onTabletChange(this.tabletMatchMedia);
90
+ this.tabletMatchMedia.addEventListener("change", this.onTabletChange);
83
91
 
84
92
  if (
85
93
  (!this.shouldHideHeader && this.isDocsPath) ||
@@ -98,12 +106,23 @@ export default class Header extends HeaderBase {
98
106
  }
99
107
  }
100
108
 
109
+ disconnectedCallback(): void {
110
+ super.disconnectedCallback();
111
+ this.tabletMatchMedia.removeEventListener(
112
+ "change",
113
+ this.onTabletChange
114
+ );
115
+ }
116
+
101
117
  renderedCallback(): void {
102
118
  if (this.hideDocHeader) {
103
119
  this.shouldRender = !this.shouldHideHeader && this.isDocsPath;
104
120
  }
105
121
  }
106
122
 
123
+ private onTabletChange = (e: MediaQueryListEvent | MediaQueryList) =>
124
+ (this.tablet = e.matches);
125
+
107
126
  protected additionalClasses(): string {
108
127
  return cx(
109
128
  this.brand && "has-brand",
@@ -8,7 +8,7 @@
8
8
  }
9
9
 
10
10
  dx-sidebar {
11
- --dx-c-sidebar-content-width: unset;
11
+ --dx-c-sidebar-width: unset;
12
12
 
13
13
  display: block;
14
14
  }
@@ -10,6 +10,15 @@ import { throttle } from "throttle-debounce";
10
10
  import { pollUntil } from "dxUtils/async";
11
11
  import { toJson } from "dxUtils/normalizers";
12
12
  import { oldVersionDocInfo } from "docUtils/utils";
13
+ import {
14
+ applyVersionToHref,
15
+ collectSubtreeCanonicalHrefs,
16
+ normalizePath,
17
+ getTopicVersionKey,
18
+ persistTopicVersion,
19
+ toCanonicalHref
20
+ } from "dxUtils/topicVersion";
21
+ import type { VersionedNode } from "dxUtils/topicVersion";
13
22
  import type { DocPhaseInfo, OptionWithLink } from "typings/custom";
14
23
 
15
24
  declare global {
@@ -47,6 +56,8 @@ type ReferenceVersion = {
47
56
  type ReferenceConfig = {
48
57
  refList: ReferenceItem[];
49
58
  versions?: ReferenceVersion[];
59
+ /** Sidebar topic name. Sidebar stores the selected version under this key. */
60
+ versionTopicKey?: string;
50
61
  };
51
62
 
52
63
  const SCROLL_THROTTLE_DELAY = 50;
@@ -129,6 +140,29 @@ export default class RedocReference extends LightningElement {
129
140
  */
130
141
  @api headerHref: string | null = null;
131
142
 
143
+ /**
144
+ * Versioned topic that owns this spec, including its children.
145
+ * Href matches this page, so an older version still has its version
146
+ * in the path.
147
+ */
148
+ @api
149
+ set parentTopicFamily(value: string | { topics?: VersionedNode[] } | null) {
150
+ try {
151
+ const parsed = toJson(value) as { topics?: VersionedNode[] } | null;
152
+ this._parentTopicFamilyNode = parsed?.topics?.[0] ?? null;
153
+ } catch (error) {
154
+ this._parentTopicFamilyNode = null;
155
+ }
156
+ }
157
+
158
+ get parentTopicFamily(): { topics?: VersionedNode[] } | null {
159
+ return this._parentTopicFamilyNode
160
+ ? { topics: [this._parentTopicFamilyNode] }
161
+ : null;
162
+ }
163
+
164
+ private _parentTopicFamilyNode: VersionedNode | null = null;
165
+
132
166
  @api
133
167
  get contentType(): string {
134
168
  return this._contentType;
@@ -233,13 +267,64 @@ export default class RedocReference extends LightningElement {
233
267
  private onBackClick = (event: Event): void => {
234
268
  event.preventDefault();
235
269
 
236
- const target = this.getTranslatedBackTarget() ?? this.headerHref;
270
+ const target = this.getVersionedBackTarget() ?? this.headerHref;
237
271
 
238
272
  if (target) {
239
273
  window.location.assign(target);
240
274
  }
241
275
  };
242
276
 
277
+ /**
278
+ * When the back target is in this spec's topic, return it on the
279
+ * selected version. With no back target, do the same for the header
280
+ * link when that link is the versioned parent.
281
+ */
282
+ private getVersionedBackTarget(): string | null {
283
+ const stored = this.getTranslatedBackTarget();
284
+ const selectedVersion = this.selectedVersion;
285
+ const target = stored ?? this.headerHref;
286
+ if (!target || !selectedVersion) {
287
+ return stored;
288
+ }
289
+
290
+ let targetUrl: URL;
291
+ try {
292
+ targetUrl = new URL(target, window.location.origin);
293
+ } catch {
294
+ return target;
295
+ }
296
+
297
+ // Family hrefs match this page's version. Compare them without
298
+ // the version segment. Rewrite only the pathname so the target's
299
+ // query and hash survive.
300
+ const familyHrefs = this._parentTopicFamilyNode
301
+ ? collectSubtreeCanonicalHrefs(this._parentTopicFamilyNode).map(
302
+ (href) => toCanonicalHref(href, this.versions)
303
+ )
304
+ : [];
305
+
306
+ const canonicalTarget = toCanonicalHref(
307
+ targetUrl.pathname,
308
+ this.versions
309
+ );
310
+ const inFamily = familyHrefs.some(
311
+ (href) => normalizePath(href) === normalizePath(canonicalTarget)
312
+ );
313
+ if (!inFamily) {
314
+ return target;
315
+ }
316
+
317
+ const versioned = applyVersionToHref(
318
+ canonicalTarget,
319
+ selectedVersion,
320
+ this.versions
321
+ );
322
+ if (!versioned) {
323
+ return target;
324
+ }
325
+ return `${versioned}${targetUrl.search}${targetUrl.hash}`;
326
+ }
327
+
243
328
  private getTranslatedBackTarget(): string | null {
244
329
  const stored = this.getBackTargetFromSession();
245
330
  if (stored) {
@@ -362,6 +447,13 @@ export default class RedocReference extends LightningElement {
362
447
  // Track the back target, preserving it across locale switches
363
448
  this.trackBackTarget();
364
449
 
450
+ // Remember the selected version for the sidebar. Changing version
451
+ // reloads this page.
452
+ const topicKey = this.getParentTopicVersionKey();
453
+ if (topicKey && this.selectedVersion) {
454
+ persistTopicVersion(topicKey, this.selectedVersion.id);
455
+ }
456
+
365
457
  window.addEventListener("scroll", this.handleScrollAndResize);
366
458
  window.addEventListener("resize", this.handleScrollAndResize);
367
459
  }
@@ -670,6 +762,18 @@ export default class RedocReference extends LightningElement {
670
762
  return wrapper;
671
763
  }
672
764
 
765
+ /**
766
+ * Key the sidebar uses for this topic's selected version.
767
+ * Missing `versionTopicKey` returns null so nothing is written.
768
+ */
769
+ private getParentTopicVersionKey(): string | null {
770
+ const versionTopicKey = this._referenceConfig?.versionTopicKey;
771
+ if (!versionTopicKey) {
772
+ return null;
773
+ }
774
+ return getTopicVersionKey({ name: versionTopicKey }) || null;
775
+ }
776
+
673
777
  /** Stashes the current view (URL hash) before the version link navigates. */
674
778
  private onVersionChange = (): void => {
675
779
  sessionStorage.setItem(
@@ -16,36 +16,13 @@ import type { DocPhaseInfo, OptionWithLink, TreeNode } from "typings/custom";
16
16
  */
17
17
  const TOPIC_TYPE_SPEC = "spec";
18
18
 
19
- /**
20
- * Walks down the first-child chain and returns the first markdown content
21
- * page found, descending through nested content-less parents. Returns
22
- * undefined if the chain breaks on a spec or external link before reaching a
23
- * content page.
24
- */
25
- function firstChildContentPage(node: TreeNode): TreeNode | undefined {
26
- const firstChild = node.children?.[0];
27
- if (!firstChild) {
28
- return undefined;
29
- }
30
-
31
- const isSpec =
32
- (firstChild as { topicType?: string }).topicType === TOPIC_TYPE_SPEC;
33
- const isExternal = firstChild.link?.target === "_blank";
34
- if (isSpec || isExternal) {
35
- return undefined;
36
- }
37
-
38
- if (firstChild.link?.href) {
39
- return firstChild;
40
- }
41
-
42
- return firstChildContentPage(firstChild);
43
- }
44
-
45
19
  /**
46
20
  * Translates per-topic `topicType` into the generic `showForwardArrow` flag
47
- * that `dx-tree-tile` reads (spec topics get the forward arrow icon for Redoc),
48
- * and redirects content-less parent topics whose first child is a content page
21
+ * that `dx-tree-tile` reads (spec topics get the forward arrow icon for Redoc).
22
+ *
23
+ * Content-less parents are flagged with redirectToChild and left without a
24
+ * link. The tree selects the first markdown descendant. Copying that child's
25
+ * href onto the parent makes the parent win selection.
49
26
  */
50
27
  function decorateTopicsWithForwardArrow(
51
28
  topics:
@@ -67,18 +44,10 @@ function decorateTopicsWithForwardArrow(
67
44
  );
68
45
  }
69
46
 
70
- /** Redirect a parent that has no content of its own to its first child
71
- * but only when that first child is itself a content page
72
- */
73
- const isContentlessParent =
74
- !!decorated.children?.length && !decorated.link?.href;
75
-
76
- if (isContentlessParent) {
47
+ // Flag a parent with no page of its own. Leave its link empty so the
48
+ // child's URL stays indexed to the child.
49
+ if (!!decorated.children?.length && !decorated.link?.href) {
77
50
  decorated.redirectToChild = true;
78
- const firstChild = firstChildContentPage(topic);
79
- if (firstChild?.link) {
80
- decorated.link = { ...firstChild.link };
81
- }
82
51
  }
83
52
 
84
53
  return decorated;
@@ -1,18 +1,26 @@
1
1
  @import "dxHelpers/reset";
2
2
 
3
- /* The large picker fills its row (the sidebar sets the row padding). The small
4
- menu stops at --doc-version-picker-width, which the sidebar sets. */
3
+ /* NOTE: doc-version-picker-width width variable is used by both dx-button and dx-dropdown to maintain a consistent width. */
5
4
  :host {
6
- display: block;
5
+ /* Always positioned so the popover's containing block stays the picker.
6
+ Toggling position with aria-expanded lets the menu jump to the sidebar
7
+ (top of the ToC) for a frame before LWC hides it. */
8
+ position: relative;
7
9
 
8
- /* Menu labels wrap; break long unspaced labels instead of overflowing. */
9
- --dx-c-dropdown-option-overflow-wrap: anywhere;
10
+ --dx-c-dropdown-option-font-weight: normal;
11
+ --dx-c-dropdown-option-font-size: var(--dx-g-text-base);
12
+ --dx-c-dropdown-option-label-color: var(--dx-g-gray-10);
13
+ --dx-c-dropdown-option-active-label-color: var(--dx-g-gray-10);
14
+ --dx-c-dropdown-option-padding: var(--dx-g-spacing-sm)
15
+ var(--dx-g-spacing-lg);
16
+ --dx-c-popover-padding: var(--dx-g-spacing-sm) 0;
10
17
  --popover-container-open-transform: translateY(4px);
18
+ --dx-c-version-picker-focus-ring: 0 0 0 2px white,
19
+ 0 0 0 4px var(--dx-g-blue-vibrant-60);
11
20
  }
12
21
 
13
22
  /* Open menus must stack above later sibling pickers in the sidebar. */
14
23
  :host(:has([aria-expanded="true"])) {
15
- position: relative;
16
24
  z-index: 2;
17
25
  }
18
26
 
@@ -48,30 +56,15 @@
48
56
  .version-picker-button {
49
57
  display: flex;
50
58
  width: 100%;
59
+ overflow: visible;
51
60
 
52
61
  --dx-c-button-horizontal-spacing: var(--dx-g-spacing-sm);
53
- --dx-c-button-icon-color: var(--dx-g-gray-50);
54
-
55
- /* A real 1px border at rest. Hover and the open menu turn it dark blue and
56
- add a 1px inset shadow, so the edge looks 2px without resizing. */
57
- --dx-c-button-custom-color: var(--dx-g-gray-10);
58
- --dx-c-button-custom-color-hover: var(--dx-g-gray-10);
59
- --dx-c-button-custom-background: white;
60
- --dx-c-button-custom-background-hover: var(--dx-g-blue-vibrant-95);
61
- --dx-c-button-custom-border: var(--dx-g-gray-50);
62
- --dx-c-button-custom-border-hover: var(--dx-g-blue-vibrant-20);
63
- --dx-c-button-custom-border-shadow-hover: inset 0 0 0 1px
64
- var(--dx-g-blue-vibrant-20);
65
-
66
- /* Keyboard focus keeps the 2px edge inside the white band. */
67
- --dx-c-button-focus-shadow: inset 0 0 0 1px var(--dx-g-blue-vibrant-20),
68
- 0 0 0 2px white;
62
+ --dx-g-button-icon-color: var(--dx-g-gray-50);
69
63
  }
70
64
 
71
65
  .version-picker-button:hover,
72
- .version-picker-button:focus-within,
73
66
  .version-picker-button[aria-expanded="true"] {
74
- --dx-c-button-icon-color: var(--dx-g-blue-vibrant-20);
67
+ --dx-g-button-icon-color: var(--dx-g-blue-vibrant-20);
75
68
  }
76
69
 
77
70
  /**
@@ -83,6 +76,57 @@ dx-button::part(content) {
83
76
  overflow: hidden;
84
77
  }
85
78
 
79
+ /* Border width stays 1px in every state. Hover, open, and focus paint the
80
+ 2px blue stroke with an inset shadow so the box does not grow.
81
+ Tertiary's hover border is overridden. */
82
+ .version-picker-button::part(container) {
83
+ box-sizing: border-box;
84
+ background: white;
85
+ border: 1px solid var(--dx-g-gray-50);
86
+ border-radius: var(--dx-g-spacing-xs);
87
+ box-shadow: none;
88
+ color: var(--dx-g-gray-10);
89
+ outline: none;
90
+ transition: var(--dx-g-transition-hue-1x), box-shadow 0.15s linear;
91
+ }
92
+
93
+ /* Hover only (not open): 2px #032D60 on #EEF4FF, no ring.
94
+ Border color matches the fill so the 1px border does not add a third
95
+ pixel around the inset stroke.
96
+ :focus-visible is included so click-open + Escape (which upgrades the
97
+ trigger to keyboard focus) still looks like hover while the pointer is on
98
+ the picker. */
99
+ .version-picker-button:hover:not([aria-expanded="true"])::part(container),
100
+ .version-picker-button:hover:not([aria-expanded="true"])::part(
101
+ container
102
+ ):focus-visible {
103
+ background: var(--dx-g-blue-vibrant-95);
104
+ border: 1px solid var(--dx-g-blue-vibrant-95);
105
+ box-shadow: inset 0 0 0 2px var(--dx-g-blue-vibrant-20);
106
+ }
107
+
108
+ /* Open keeps the fill and the 2px stroke. The focus ring is keyboard-only,
109
+ on the closed picker below. */
110
+ .version-picker-button[aria-expanded="true"]::part(container) {
111
+ background: var(--dx-g-blue-vibrant-95);
112
+ border: 1px solid var(--dx-g-blue-vibrant-95);
113
+ border-radius: var(--dx-g-spacing-xs);
114
+ box-shadow: inset 0 0 0 2px var(--dx-g-blue-vibrant-20);
115
+ }
116
+
117
+ /* Keyboard focus while closed. */
118
+ .version-picker-button:not([aria-expanded="true"])::part(
119
+ container
120
+ ):focus-visible {
121
+ --dx-g-button-icon-color: var(--dx-g-blue-vibrant-20);
122
+
123
+ background: var(--dx-g-blue-vibrant-95);
124
+ border: 1px solid var(--dx-g-blue-vibrant-95);
125
+ border-radius: var(--dx-g-spacing-xs);
126
+ box-shadow: inset 0 0 0 2px var(--dx-g-blue-vibrant-20),
127
+ var(--dx-c-version-picker-focus-ring);
128
+ }
129
+
86
130
  .selected-version {
87
131
  display: flex;
88
132
  flex-direction: row;
@@ -106,11 +150,25 @@ dx-type-badge.not-latest-badge {
106
150
  * Small "dot" variant (opt-in via `small`). Additive
107
151
  * ------------------------------------------------------------------ */
108
152
 
109
- /* The small menu hugs its longest label but never grows wider than the sidebar
110
- content (--doc-version-picker-width, set by the sidebar). Labels wrap only at
111
- that width. The large menu always matches its trigger (width-mode="full"). */
153
+ /* Menu is capped at the ToC width (296px), not the 104px trigger, so options
154
+ keep the same 16px / 8px 24px / gray-10 type as the default picker. */
112
155
  .version-picker-dropdown-small {
156
+ --dx-c-dropdown-option-font-size: var(--dx-g-text-base);
157
+ --dx-c-dropdown-option-padding: var(--dx-g-spacing-sm)
158
+ var(--dx-g-spacing-lg);
159
+ --dx-c-dropdown-option-label-color: var(--dx-g-gray-10);
160
+ --dx-c-dropdown-option-border-radius: 0;
161
+ --dx-c-popover-border: none;
162
+
163
+ /* Hug the labels (down to the shortest) but never wider than the ToC
164
+ content: 296px on desktop, and on the smallest breakpoint the sidebar
165
+ variable grows with the viewport up to 720px. Long labels wrap inside
166
+ that cap. */
113
167
  --dx-c-dropdown-menu-max-width: var(--doc-version-picker-width, 296px);
168
+ --dx-c-dropdown-fit-white-space: normal;
169
+ --dx-c-dropdown-fit-overflow: hidden;
170
+ --dx-c-dropdown-fit-text-overflow: clip;
171
+ --dx-c-dropdown-option-overflow-wrap: anywhere;
114
172
  }
115
173
 
116
174
  .version-picker-button-small {
@@ -135,8 +193,7 @@ dx-type-badge.not-latest-badge {
135
193
  }
136
194
 
137
195
  /* width:100% re-ties to the 104px-capped host (container is width:inherit,
138
- which copies width but not max-width). Border, fill, and states come from the
139
- dx-button hooks on .version-picker-button. */
196
+ which copies width but not max-width). Border/bg/states are shared above. */
140
197
  .version-picker-button-small::part(container) {
141
198
  width: 100%;
142
199
  height: var(--dx-g-spacing-lg);
@@ -21,7 +21,7 @@
21
21
  >
22
22
  <dx-button
23
23
  class={triggerClass}
24
- variant="custom"
24
+ variant="tertiary"
25
25
  size="small"
26
26
  font="sans"
27
27
  icon-symbol="chevrondown"