@ds-mo/ui 15.0.0 → 15.1.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.
Files changed (145) hide show
  1. package/NOTICE +6 -0
  2. package/README.md +7 -0
  3. package/dist/.package-ready.json +1 -1
  4. package/dist/agent-patterns.json +1 -1
  5. package/dist/agent.json +2320 -90
  6. package/dist/components/ds-agent-questionnaire.js +1 -1
  7. package/dist/components/ds-agent-response.js +1 -1
  8. package/dist/components/ds-banner.js +1 -1
  9. package/dist/components/ds-banner.js.map +1 -1
  10. package/dist/components/ds-card-chart.js +1 -1
  11. package/dist/components/ds-card-chart.js.map +1 -1
  12. package/dist/components/ds-card-overview.js +1 -1
  13. package/dist/components/ds-card-overview.js.map +1 -1
  14. package/dist/components/ds-card-setting.js +1 -1
  15. package/dist/components/ds-card-setting.js.map +1 -1
  16. package/dist/components/ds-chart.js +1 -1
  17. package/dist/components/ds-chart.js.map +1 -1
  18. package/dist/components/ds-checkbox.js +1 -1
  19. package/dist/components/ds-conversation-list-item.js +1 -1
  20. package/dist/components/ds-conversation-list-item.js.map +1 -1
  21. package/dist/components/ds-data-filter.js +1 -1
  22. package/dist/components/ds-data-preferences.js +1 -1
  23. package/dist/components/ds-data-saved-views.js +1 -1
  24. package/dist/components/ds-data-search.js +1 -1
  25. package/dist/components/ds-data-search.js.map +1 -1
  26. package/dist/components/ds-filter-menu.js +1 -1
  27. package/dist/components/ds-message-scroller.js.map +1 -1
  28. package/dist/components/ds-mobile-sheet-nav.js +1 -1
  29. package/dist/components/ds-pagination.js +1 -1
  30. package/dist/components/ds-scroll-overlay.js.map +1 -1
  31. package/dist/components/ds-select.js +1 -1
  32. package/dist/components/ds-shell-app.js +1 -1
  33. package/dist/components/ds-table.js +1 -1
  34. package/dist/components/{p-Dpr2y6_S.js → p-B_jdoM7e.js} +2 -2
  35. package/dist/components/{p-CElUM-xU.js → p-C6xVxqOn.js} +2 -2
  36. package/dist/components/{p-MG4lcN0D.js → p-CdGXzRP8.js} +2 -2
  37. package/dist/components/{p-D6Wl3dRO.js → p-Cqc2U1ZQ.js} +2 -2
  38. package/dist/components/{p-D6Wl3dRO.js.map → p-Cqc2U1ZQ.js.map} +1 -1
  39. package/dist/components/{p-Nc76seSq.js → p-DEW7r0U9.js} +2 -2
  40. package/dist/components/{p-IEq_06J0.js → p-Dfs98tpL.js} +2 -2
  41. package/dist/components/{p-IEq_06J0.js.map → p-Dfs98tpL.js.map} +1 -1
  42. package/dist/components/p-ZhP2tVmM.js.map +1 -1
  43. package/dist/components/p-etifxiDc.js.map +1 -1
  44. package/dist/docs/components.json +1 -1
  45. package/dist/lint/STYLELINT-LICENSE +20 -0
  46. package/dist/lint/THIRD-PARTY-NOTICES +553 -0
  47. package/dist/lint/index.d.ts +7 -0
  48. package/dist/lint/index.js +51385 -0
  49. package/dist/lint/types.d.ts +13 -0
  50. package/dist/mcp-data/registry/agent-activity.json +6 -1
  51. package/dist/mcp-data/registry/agent-questionnaire.json +6 -1
  52. package/dist/mcp-data/registry/agent-response.json +12 -1
  53. package/dist/mcp-data/registry/agent-source-list.json +6 -1
  54. package/dist/mcp-data/registry/agent-tool-call.json +6 -1
  55. package/dist/mcp-data/registry/attachment-list.json +6 -1
  56. package/dist/mcp-data/registry/avatar.json +21 -1
  57. package/dist/mcp-data/registry/badge.json +30 -2
  58. package/dist/mcp-data/registry/banner.json +28 -2
  59. package/dist/mcp-data/registry/bar-action.json +24 -1
  60. package/dist/mcp-data/registry/bar-nav.json +6 -1
  61. package/dist/mcp-data/registry/bar-page-title.json +24 -1
  62. package/dist/mcp-data/registry/bar-title.json +26 -1
  63. package/dist/mcp-data/registry/bar-workflow.json +24 -1
  64. package/dist/mcp-data/registry/breadcrumb.json +6 -1
  65. package/dist/mcp-data/registry/button-filled.json +68 -1
  66. package/dist/mcp-data/registry/button-inverted.json +64 -1
  67. package/dist/mcp-data/registry/button-unfilled.json +66 -1
  68. package/dist/mcp-data/registry/calendar.json +14 -1
  69. package/dist/mcp-data/registry/card-action-center.json +24 -1
  70. package/dist/mcp-data/registry/card-chart.json +37 -2
  71. package/dist/mcp-data/registry/card-navigation.json +36 -1
  72. package/dist/mcp-data/registry/card-overview.json +27 -2
  73. package/dist/mcp-data/registry/card-setting.json +35 -2
  74. package/dist/mcp-data/registry/card-settings-scope.json +24 -1
  75. package/dist/mcp-data/registry/chart-legend.json +6 -1
  76. package/dist/mcp-data/registry/chart.json +7 -2
  77. package/dist/mcp-data/registry/checkbox.json +38 -2
  78. package/dist/mcp-data/registry/chip.json +50 -1
  79. package/dist/mcp-data/registry/code-block.json +30 -1
  80. package/dist/mcp-data/registry/conversation-list-item.json +25 -2
  81. package/dist/mcp-data/registry/conversation-list-section.json +14 -1
  82. package/dist/mcp-data/registry/conversation-list.json +6 -1
  83. package/dist/mcp-data/registry/data-customize.json +26 -1
  84. package/dist/mcp-data/registry/data-filter.json +26 -1
  85. package/dist/mcp-data/registry/data-group.json +26 -1
  86. package/dist/mcp-data/registry/data-preferences.json +26 -1
  87. package/dist/mcp-data/registry/data-saved-views.json +26 -1
  88. package/dist/mcp-data/registry/data-search.json +27 -2
  89. package/dist/mcp-data/registry/data-sort.json +26 -1
  90. package/dist/mcp-data/registry/data-toolbar.json +14 -1
  91. package/dist/mcp-data/registry/divider.json +6 -1
  92. package/dist/mcp-data/registry/empty-state.json +6 -1
  93. package/dist/mcp-data/registry/field.json +14 -1
  94. package/dist/mcp-data/registry/filter-menu.json +48 -1
  95. package/dist/mcp-data/registry/icon.json +21 -1
  96. package/dist/mcp-data/registry/input-date.json +48 -1
  97. package/dist/mcp-data/registry/input-time.json +48 -1
  98. package/dist/mcp-data/registry/input.json +48 -1
  99. package/dist/mcp-data/registry/loader.json +21 -1
  100. package/dist/mcp-data/registry/markdown.json +17 -1
  101. package/dist/mcp-data/registry/menu.json +26 -1
  102. package/dist/mcp-data/registry/message-actions.json +6 -1
  103. package/dist/mcp-data/registry/message-bubble.json +26 -1
  104. package/dist/mcp-data/registry/message-composer.json +24 -1
  105. package/dist/mcp-data/registry/message-scroller.json +12 -1
  106. package/dist/mcp-data/registry/message.json +6 -1
  107. package/dist/mcp-data/registry/mobile-bar-nav.json +24 -1
  108. package/dist/mcp-data/registry/mobile-header.json +26 -1
  109. package/dist/mcp-data/registry/mobile-section-switcher.json +26 -1
  110. package/dist/mcp-data/registry/mobile-sheet-nav.json +27 -2
  111. package/dist/mcp-data/registry/modal.json +24 -1
  112. package/dist/mcp-data/registry/pagination.json +6 -1
  113. package/dist/mcp-data/registry/panel-nav.json +28 -2
  114. package/dist/mcp-data/registry/panel-sub-nav.json +25 -0
  115. package/dist/mcp-data/registry/panel-tool-header.json +24 -1
  116. package/dist/mcp-data/registry/panel-tool-search.json +24 -1
  117. package/dist/mcp-data/registry/panel-tools.json +25 -1
  118. package/dist/mcp-data/registry/paper-texture.json +6 -1
  119. package/dist/mcp-data/registry/radio.json +35 -1
  120. package/dist/mcp-data/registry/registry.json +2319 -89
  121. package/dist/mcp-data/registry/score.json +38 -1
  122. package/dist/mcp-data/registry/scroll-overlay.json +6 -1
  123. package/dist/mcp-data/registry/select.json +52 -1
  124. package/dist/mcp-data/registry/shell-app.json +26 -1
  125. package/dist/mcp-data/registry/shell-page.json +6 -1
  126. package/dist/mcp-data/registry/shell-tools.json +25 -1
  127. package/dist/mcp-data/registry/skeleton.json +41 -1
  128. package/dist/mcp-data/registry/slider.json +42 -1
  129. package/dist/mcp-data/registry/swatch-picker.json +24 -1
  130. package/dist/mcp-data/registry/switch.json +37 -1
  131. package/dist/mcp-data/registry/tab-group.json +49 -1
  132. package/dist/mcp-data/registry/table.json +102 -1
  133. package/dist/mcp-data/registry/tag.json +52 -1
  134. package/dist/mcp-data/registry/text.json +22 -1
  135. package/dist/mcp-data/registry/textarea.json +38 -1
  136. package/dist/mcp-data/registry/time-picker.json +14 -1
  137. package/dist/mcp-data/registry/toast.json +24 -1
  138. package/dist/mcp-data/registry/tooltip-chart.json +24 -1
  139. package/dist/mcp-data/registry/tooltip.json +26 -1
  140. package/dist/mcp-data/registry/typing-indicator.json +6 -1
  141. package/package.json +32 -7
  142. /package/dist/components/{p-Dpr2y6_S.js.map → p-B_jdoM7e.js.map} +0 -0
  143. /package/dist/components/{p-CElUM-xU.js.map → p-C6xVxqOn.js.map} +0 -0
  144. /package/dist/components/{p-MG4lcN0D.js.map → p-CdGXzRP8.js.map} +0 -0
  145. /package/dist/components/{p-Nc76seSq.js.map → p-DEW7r0U9.js.map} +0 -0
@@ -0,0 +1,13 @@
1
+ /** Options for the opt-in CompoMo ESLint flat configuration. */
2
+ export interface LintOptions {
3
+ /** Consumer policy is the default; authoring preserves library exceptions. */
4
+ mode?: 'authoring' | 'consumer';
5
+ /** Promote consumer findings to errors without enabling additional rules. */
6
+ strict?: boolean;
7
+ /** Defaults to all CSS files. An empty array disables CSS checks. */
8
+ cssFiles?: string[];
9
+ /** Defaults to JS/JSX/TS/TSX. An empty array enables CSS-only adoption. */
10
+ jsxFiles?: string[];
11
+ /** Ignore patterns local to these presets, not the application's other rules. */
12
+ ignores?: string[];
13
+ }
@@ -43,7 +43,12 @@
43
43
  "label": "Storybook examples",
44
44
  "path": "src/wc/components/MessageScroller/MessageScroller.stories.ts"
45
45
  }
46
- ]
46
+ ],
47
+ "styling": {
48
+ "rationale": "Disclosure composition owns its rows; the host remains an application layout box.",
49
+ "protected": [],
50
+ "customProperties": []
51
+ }
47
52
  },
48
53
  "api": {
49
54
  "props": {
@@ -56,7 +56,12 @@
56
56
  "label": "Agent chat composition",
57
57
  "path": "agent/patterns/agent-chat/pattern.agent.json"
58
58
  }
59
- ]
59
+ ],
60
+ "styling": {
61
+ "rationale": "Question and action components own their recipes; the questionnaire host remains a responsive layout box.",
62
+ "protected": [],
63
+ "customProperties": []
64
+ }
60
65
  },
61
66
  "api": {
62
67
  "props": {
@@ -56,7 +56,18 @@
56
56
  "label": "Prose distribution decision",
57
57
  "path": "docs/prose-foundation.md"
58
58
  }
59
- ]
59
+ ],
60
+ "styling": {
61
+ "rationale": "Structured response parts own their recipes; the host permits application layout and the public prose measure.",
62
+ "protected": [],
63
+ "customProperties": [
64
+ {
65
+ "name": "--ds-prose-text-max-inline-size",
66
+ "source": "src/wc/styles/prose.css",
67
+ "reason": "Application-owned readable prose measure."
68
+ }
69
+ ]
70
+ }
60
71
  },
61
72
  "api": {
62
73
  "props": {
@@ -43,7 +43,12 @@
43
43
  "label": "Source safety and disclosure stories",
44
44
  "path": "src/wc/components/AgentSourceList/AgentSourceList.stories.ts"
45
45
  }
46
- ]
46
+ ],
47
+ "styling": {
48
+ "rationale": "Source rows own their recipes; the disclosure host remains an application layout box.",
49
+ "protected": [],
50
+ "customProperties": []
51
+ }
47
52
  },
48
53
  "api": {
49
54
  "props": {
@@ -53,7 +53,12 @@
53
53
  "label": "Font ownership and loading",
54
54
  "path": "docs/font-ownership.md"
55
55
  }
56
- ]
56
+ ],
57
+ "styling": {
58
+ "rationale": "Slotted tool summaries and results retain application-owned styling and layout.",
59
+ "protected": [],
60
+ "customProperties": []
61
+ }
57
62
  },
58
63
  "api": {
59
64
  "props": {
@@ -39,7 +39,12 @@
39
39
  "label": "Storybook examples",
40
40
  "path": "src/wc/components/MessageScroller/MessageScroller.stories.ts"
41
41
  }
42
- ]
42
+ ],
43
+ "styling": {
44
+ "rationale": "Attachment items own their recipes; the list host permits application layout.",
45
+ "protected": [],
46
+ "customProperties": []
47
+ }
43
48
  },
44
49
  "api": {
45
50
  "props": {
@@ -37,7 +37,27 @@
37
37
  "label": "Storybook examples",
38
38
  "path": "src/wc/components/Avatar/Avatar.stories.ts"
39
39
  }
40
- ]
40
+ ],
41
+ "styling": {
42
+ "rationale": "The component owns the protected recipes below; unspecified host properties and application layout remain customizable.",
43
+ "protected": [
44
+ {
45
+ "properties": [
46
+ "width",
47
+ "height",
48
+ "min-width",
49
+ "min-height",
50
+ "max-width",
51
+ "max-height"
52
+ ],
53
+ "reason": "The primitive owns its size recipe; select size and position it through the parent layout.",
54
+ "props": [
55
+ "size"
56
+ ]
57
+ }
58
+ ],
59
+ "customProperties": []
60
+ }
41
61
  },
42
62
  "api": {
43
63
  "props": {
@@ -84,7 +84,35 @@
84
84
  "label": "Storybook examples",
85
85
  "path": "src/wc/components/Badge/Badge.stories.ts"
86
86
  }
87
- ]
87
+ ],
88
+ "styling": {
89
+ "rationale": "The component owns the protected recipes below; unspecified host properties and application layout remain customizable.",
90
+ "protected": [
91
+ {
92
+ "properties": [
93
+ "background",
94
+ "border",
95
+ "box-shadow",
96
+ "color"
97
+ ],
98
+ "reason": "The component owns its surface and interaction treatment; use its semantic presentation props or compose an application-owned surrounding surface.",
99
+ "props": [
100
+ "background",
101
+ "surface",
102
+ "variant",
103
+ "hasRing"
104
+ ]
105
+ },
106
+ {
107
+ "properties": [
108
+ "border-radius"
109
+ ],
110
+ "reason": "The component owns its surface radius; retain the shared recipe and customize an outer wrapper when needed.",
111
+ "props": []
112
+ }
113
+ ],
114
+ "customProperties": []
115
+ }
88
116
  },
89
117
  "api": {
90
118
  "props": {
@@ -280,7 +308,7 @@
280
308
  "files": [
281
309
  {
282
310
  "path": "src/wc/components/Badge/Badge.css",
283
- "content": "@import '../../utils/visually-hidden.css';\n@import '../../utils/forced-colors.css';\n\n:host {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n container-name: ds-badge-gradient;\n pointer-events: none;\n vertical-align: middle;\n\n --_badge-bg: var(--color-background-bold-brand);\n --_badge-fg: var(--color-foreground-on-bold-background-primary);\n --_badge-ring: var(--color-background-secondary);\n --_badge-ring-width: var(--dimension-stroke-width-018);\n --_badge-gradient-context: var(--ds-shell-gradient-context, flat);\n}\n\n:host(.badge--force-gradient-background) {\n --_badge-gradient-context: gradient;\n}\n\n:host(.badge--force-flat-background) {\n --_badge-gradient-context: flat;\n}\n\n.badge__mark {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n box-sizing: border-box;\n color: var(--_badge-fg);\n background: var(--_badge-bg);\n box-shadow: 0 0 0 var(--_badge-ring-width) var(--_badge-ring);\n white-space: nowrap;\n}\n\n:host(.badge--no-ring) {\n --_badge-ring-width: 0;\n}\n\n:host(.badge--dot) .badge__mark {\n width: var(--dimension-size-075);\n height: var(--dimension-size-075);\n border-radius: var(--dimension-radius-half);\n}\n\n:host(.badge--counter) .badge__mark {\n width: auto;\n min-width: var(--dimension-size-150);\n height: var(--dimension-size-150);\n padding: 0 var(--dimension-space-025);\n border-radius: var(--dimension-radius-half);\n}\n\n/* Shell gradient ring — base fill + wash donut (option 3). */\n@container ds-badge-gradient style(--_badge-gradient-context: gradient) {\n /* Match the scoped base rule's specificity; Stencil does not yet scope style-query selectors. */\n .badge__mark.badge__mark {\n position: relative;\n z-index: 0;\n box-shadow: none;\n isolation: isolate;\n }\n\n .badge__mark::before,\n .badge__mark::after {\n content: '';\n position: absolute;\n inset: calc(-1 * var(--_badge-ring-width));\n border-radius: inherit;\n box-sizing: border-box;\n padding: var(--_badge-ring-width);\n pointer-events: none;\n /* stylelint-disable color-no-hex -- mask luminance stops for ring cutout, not theme colors */\n -webkit-mask:\n linear-gradient(#fff 0 0) content-box,\n linear-gradient(#fff 0 0);\n -webkit-mask-composite: xor;\n mask:\n linear-gradient(#fff 0 0) content-box,\n linear-gradient(#fff 0 0);\n /* stylelint-enable color-no-hex */\n mask-composite: exclude;\n }\n\n .badge__mark::before {\n z-index: -2;\n background-color: var(--_badge-ring);\n }\n\n .badge__mark::after {\n z-index: -1;\n background-image: var(--ds-shell-gradient-image, none);\n background-size: var(--ds-shell-gradient-size, auto);\n background-position: 0 0;\n background-repeat: no-repeat;\n background-attachment: fixed;\n opacity: var(--ds-shell-gradient-opacity, 1);\n }\n}\n\n@media (forced-colors: active) {\n .badge__mark {\n color: var(--ds-forced-color-on-selected);\n background: var(--ds-forced-color-selected);\n box-shadow: none;\n outline: var(--_badge-ring-width) solid var(--ds-forced-color-surface);\n }\n\n .badge__mark::before,\n .badge__mark::after {\n display: none;\n }\n}\n",
311
+ "content": "@import '../../utils/visually-hidden.css';\n@import '../../utils/forced-colors.css';\n\n:host {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n container-name: ds-badge-gradient;\n pointer-events: none;\n vertical-align: middle;\n\n --_badge-bg: var(--color-background-bold-brand);\n --_badge-fg: var(--color-foreground-on-bold-background-primary);\n --_badge-ring: var(--color-background-secondary);\n --_badge-ring-width: var(--dimension-stroke-width-018);\n --_badge-gradient-context: var(--ds-shell-gradient-context, flat);\n}\n\n:host(.badge--force-gradient-background) {\n --_badge-gradient-context: gradient;\n}\n\n:host(.badge--force-flat-background) {\n --_badge-gradient-context: flat;\n}\n\n.badge__mark {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n box-sizing: border-box;\n color: var(--_badge-fg);\n background: var(--_badge-bg);\n box-shadow: 0 0 0 var(--_badge-ring-width) var(--_badge-ring);\n white-space: nowrap;\n}\n\n:host(.badge--no-ring) {\n --_badge-ring-width: 0;\n}\n\n:host(.badge--dot) .badge__mark {\n width: var(--dimension-size-075);\n height: var(--dimension-size-075);\n border-radius: var(--dimension-radius-half);\n}\n\n:host(.badge--counter) .badge__mark {\n width: auto;\n min-width: var(--dimension-size-150);\n height: var(--dimension-size-150);\n padding: 0 var(--dimension-space-025);\n border-radius: var(--dimension-radius-half);\n}\n\n/* Shell gradient ring — base fill + wash donut (option 3). */\n@container ds-badge-gradient style(--_badge-gradient-context: gradient) {\n /* Match the scoped base rule's specificity; Stencil does not yet scope style-query selectors. */\n .badge__mark.badge__mark {\n position: relative;\n z-index: 0;\n box-shadow: none;\n isolation: isolate;\n }\n\n .badge__mark::before,\n .badge__mark::after {\n content: '';\n position: absolute;\n inset: calc(-1 * var(--_badge-ring-width));\n border-radius: inherit;\n box-sizing: border-box;\n padding: var(--_badge-ring-width);\n pointer-events: none;\n /* eslint-disable compomo/color-no-hex -- mask luminance stops for ring cutout, not theme colors */\n -webkit-mask:\n linear-gradient(#fff 0 0) content-box,\n linear-gradient(#fff 0 0);\n -webkit-mask-composite: xor;\n mask:\n linear-gradient(#fff 0 0) content-box,\n linear-gradient(#fff 0 0);\n /* eslint-enable compomo/color-no-hex */\n mask-composite: exclude;\n }\n\n .badge__mark::before {\n z-index: -2;\n background-color: var(--_badge-ring);\n }\n\n .badge__mark::after {\n z-index: -1;\n background-image: var(--ds-shell-gradient-image, none);\n background-size: var(--ds-shell-gradient-size, auto);\n background-position: 0 0;\n background-repeat: no-repeat;\n background-attachment: fixed;\n opacity: var(--ds-shell-gradient-opacity, 1);\n }\n}\n\n@media (forced-colors: active) {\n .badge__mark {\n color: var(--ds-forced-color-on-selected);\n background: var(--ds-forced-color-selected);\n box-shadow: none;\n outline: var(--_badge-ring-width) solid var(--ds-forced-color-surface);\n }\n\n .badge__mark::before,\n .badge__mark::after {\n display: none;\n }\n}\n",
284
312
  "type": "registry:ui"
285
313
  },
286
314
  {
@@ -91,7 +91,33 @@
91
91
  "label": "Application shell contract",
92
92
  "path": "agent/patterns/application-shell/pattern.agent.json"
93
93
  }
94
- ]
94
+ ],
95
+ "styling": {
96
+ "rationale": "The component owns the protected recipes below; unspecified host properties and application layout remain customizable.",
97
+ "protected": [
98
+ {
99
+ "properties": [
100
+ "background",
101
+ "border",
102
+ "box-shadow",
103
+ "color"
104
+ ],
105
+ "reason": "The component owns its surface and interaction treatment; use its semantic presentation props or compose an application-owned surrounding surface.",
106
+ "props": [
107
+ "intent",
108
+ "contrast"
109
+ ]
110
+ },
111
+ {
112
+ "properties": [
113
+ "border-radius"
114
+ ],
115
+ "reason": "The component owns its surface radius; retain the shared recipe and customize an outer wrapper when needed.",
116
+ "props": []
117
+ }
118
+ ],
119
+ "customProperties": []
120
+ }
95
121
  },
96
122
  "api": {
97
123
  "props": {
@@ -332,7 +358,7 @@
332
358
  },
333
359
  {
334
360
  "path": "src/wc/components/Banner/Banner.tsx",
335
- "content": "import {\n Component,\n Element,\n Event,\n EventEmitter,\n h,\n Host,\n Prop,\n State,\n Watch,\n} from '@stencil/core';\nimport { resolveMotionTimeMs, TOKEN_DEFAULTS } from '../../utils';\n\nexport type BannerIntent = 'neutral' | 'brand' | 'positive' | 'warning' | 'caution' | 'negative';\n\nexport type BannerContrast = 'faint' | 'medium' | 'strong' | 'bold';\nexport type BannerAnnouncement = 'none' | 'polite' | 'assertive';\nexport type BannerOrientation = 'horizontal' | 'vertical';\n\ntype BannerPhase = 'closed' | 'open' | 'closing';\n\n@Component({\n tag: 'ds-banner',\n styleUrl: 'Banner.css',\n scoped: true,\n})\nexport class Banner {\n @Element() el!: HTMLElement;\n\n /** Required explanatory copy. */\n @Prop() description!: string;\n /** Optional visual heading rendered before the description. */\n @Prop() heading: string = '';\n /** Semantic color family. */\n @Prop({ reflect: true }) intent: BannerIntent = 'neutral';\n /** Surface fill weight. */\n @Prop({ reflect: true }) contrast: BannerContrast = 'faint';\n /** Copy and action-lane arrangement, selected by the application. */\n @Prop({ reflect: true }) orientation: BannerOrientation = 'horizontal';\n /** Controlled visibility. Keep the element mounted until dsAfterClose when unmounting. */\n @Prop({ reflect: true }) open: boolean = true;\n /** Localized accessible label for the close control. */\n @Prop() dismissLabel: string = 'Dismiss banner';\n /** Live-region urgency, independent of visual intent. */\n @Prop() announcement: BannerAnnouncement = 'none';\n\n /** Emitted when the close control requests dismissal. The application controls open. */\n @Event() dsClose!: EventEmitter<MouseEvent>;\n /** Emitted once after controlled exit motion and layout collapse complete. */\n @Event() dsAfterClose!: EventEmitter<void>;\n\n @State() private rendered = false;\n @State() private phase: BannerPhase = 'closed';\n @State() private hasActions = false;\n\n private loaded = false;\n private transitionTimer: ReturnType<typeof setTimeout> | null = null;\n private closePending = false;\n\n componentWillLoad() {\n this.rendered = this.open;\n this.phase = this.open ? 'open' : 'closed';\n }\n\n componentDidLoad() {\n this.loaded = true;\n this.updateActionsPresence();\n }\n\n disconnectedCallback() {\n this.clearTransitionWork();\n }\n\n @Watch('open')\n onOpenChange(isOpen: boolean) {\n if (!this.loaded) return;\n if (isOpen) this.startOpen();\n else this.startClose();\n }\n\n private get closeMotionMs(): number {\n return resolveMotionTimeMs(TOKEN_DEFAULTS.motionShort3, TOKEN_DEFAULTS.animationDurationShort3);\n }\n\n private clearTransitionWork() {\n if (this.transitionTimer) clearTimeout(this.transitionTimer);\n this.transitionTimer = null;\n }\n\n private startOpen() {\n this.clearTransitionWork();\n this.closePending = false;\n this.rendered = true;\n this.phase = 'open';\n }\n\n private startClose() {\n this.clearTransitionWork();\n if (!this.rendered || this.phase === 'closed') {\n this.rendered = false;\n return;\n }\n\n this.closePending = true;\n this.phase = 'closing';\n const duration = this.closeMotionMs;\n if (duration <= 0) {\n this.finishClose();\n return;\n }\n this.transitionTimer = setTimeout(() => {\n this.transitionTimer = null;\n this.finishClose();\n }, duration);\n }\n\n private finishClose() {\n if (this.open || !this.closePending) return;\n this.closePending = false;\n this.rendered = false;\n this.phase = 'closed';\n this.dsAfterClose.emit();\n }\n\n private updateActionsPresence(slot?: HTMLSlotElement) {\n const actions = slot\n ? slot.assignedElements().filter(element => element.tagName === 'DS-BUTTON-UNFILLED')\n : Array.from(this.el.querySelectorAll('ds-button-unfilled[slot=\"actions\"]'));\n this.hasActions = actions.length > 0;\n }\n\n private requestClose(event: CustomEvent<MouseEvent>) {\n if (!this.open || this.phase === 'closing') return;\n this.dsClose.emit(event.detail);\n }\n\n private get role(): 'status' | 'alert' | undefined {\n if (this.announcement === 'polite') return 'status';\n if (this.announcement === 'assertive') return 'alert';\n return undefined;\n }\n\n private get ariaLive(): 'polite' | 'assertive' | undefined {\n return this.announcement === 'none' ? undefined : this.announcement;\n }\n\n render() {\n const closing = this.phase === 'closing';\n const hostClass = {\n banner: true,\n 'banner--open': this.phase === 'open',\n 'banner--closing': closing,\n 'banner--hidden': !this.rendered,\n 'banner--has-actions': this.hasActions,\n [`banner--orientation-${this.orientation}`]: true,\n [`banner--intent-${this.intent}`]: true,\n [`banner--contrast-${this.contrast}`]: true,\n };\n\n return (\n <Host class={hostClass}>\n <div class=\"banner-browser-edge\" aria-hidden=\"true\" />\n <div class=\"banner-overflow\">\n <section\n class=\"banner-surface ds-chrome-header ds-chrome-header--wrapping\"\n role={this.role}\n aria-live={this.ariaLive}\n aria-atomic={this.announcement === 'none' ? undefined : 'true'}\n aria-hidden={closing ? 'true' : undefined}\n inert={closing ? true : undefined}\n data-phase={this.phase}\n >\n {/* eslint-disable-next-line local/prefer-direct-ds-text -- The header-copy lane owns the 6px container inset separately from the inline text flow's 2px inset. */}\n <div class=\"banner-copy ds-chrome-header__copy ds-chrome-header__copy--wrapping ds-control--md\">\n <ds-text class=\"banner-copy-flow\" as=\"div\" variant=\"text-body-medium\" color=\"inherit\">\n {this.heading && (\n <ds-text\n class=\"banner-heading\"\n as=\"span\"\n variant=\"text-title-small\"\n color=\"inherit\"\n >\n {this.heading}\n </ds-text>\n )}\n <span class=\"banner-description\">{this.description}</span>\n </ds-text>\n </div>\n <div class=\"banner-trailing ds-chrome-header__trailing\">\n <div class={{ 'banner-actions': true, 'banner-actions--empty': !this.hasActions }}>\n <slot\n name=\"actions\"\n onSlotchange={(event: Event) =>\n this.updateActionsPresence(event.currentTarget as HTMLSlotElement)\n }\n />\n </div>\n <div class=\"banner-dismiss\">\n <ds-tooltip label={this.dismissLabel} side=\"bottom\" size=\"sm\">\n <ds-button-unfilled\n class=\"banner-close\"\n variant=\"icon\"\n icon=\"Cross\"\n size=\"md\"\n type=\"button\"\n background={this.contrast}\n aria-label={this.dismissLabel}\n hasBorder={false}\n activeFill={false}\n onDsClick={(event: CustomEvent<MouseEvent>) => this.requestClose(event)}\n />\n </ds-tooltip>\n </div>\n </div>\n </section>\n </div>\n </Host>\n );\n }\n}\n",
361
+ "content": "import {\n Component,\n Element,\n Event,\n EventEmitter,\n h,\n Host,\n Prop,\n State,\n Watch,\n} from '@stencil/core';\nimport { resolveMotionTimeMs, TOKEN_DEFAULTS } from '../../utils';\n\nexport type BannerIntent = 'neutral' | 'brand' | 'positive' | 'warning' | 'caution' | 'negative';\n\nexport type BannerContrast = 'faint' | 'medium' | 'strong' | 'bold';\nexport type BannerAnnouncement = 'none' | 'polite' | 'assertive';\nexport type BannerOrientation = 'horizontal' | 'vertical';\n\ntype BannerPhase = 'closed' | 'open' | 'closing';\n\n@Component({\n tag: 'ds-banner',\n styleUrl: 'Banner.css',\n scoped: true,\n})\nexport class Banner {\n @Element() el!: HTMLElement;\n\n /** Required explanatory copy. */\n @Prop() description!: string;\n /** Optional visual heading rendered before the description. */\n @Prop() heading: string = '';\n /** Semantic color family. */\n @Prop({ reflect: true }) intent: BannerIntent = 'neutral';\n /** Surface fill weight. */\n @Prop({ reflect: true }) contrast: BannerContrast = 'faint';\n /** Copy and action-lane arrangement, selected by the application. */\n @Prop({ reflect: true }) orientation: BannerOrientation = 'horizontal';\n /** Controlled visibility. Keep the element mounted until dsAfterClose when unmounting. */\n @Prop({ reflect: true }) open: boolean = true;\n /** Localized accessible label for the close control. */\n @Prop() dismissLabel: string = 'Dismiss banner';\n /** Live-region urgency, independent of visual intent. */\n @Prop() announcement: BannerAnnouncement = 'none';\n\n /** Emitted when the close control requests dismissal. The application controls open. */\n @Event() dsClose!: EventEmitter<MouseEvent>;\n /** Emitted once after controlled exit motion and layout collapse complete. */\n @Event() dsAfterClose!: EventEmitter<void>;\n\n @State() private rendered = false;\n @State() private phase: BannerPhase = 'closed';\n @State() private hasActions = false;\n\n private loaded = false;\n private transitionTimer: ReturnType<typeof setTimeout> | null = null;\n private closePending = false;\n\n componentWillLoad() {\n this.rendered = this.open;\n this.phase = this.open ? 'open' : 'closed';\n }\n\n componentDidLoad() {\n this.loaded = true;\n this.updateActionsPresence();\n }\n\n disconnectedCallback() {\n this.clearTransitionWork();\n }\n\n @Watch('open')\n onOpenChange(isOpen: boolean) {\n if (!this.loaded) return;\n if (isOpen) this.startOpen();\n else this.startClose();\n }\n\n private get closeMotionMs(): number {\n return resolveMotionTimeMs(TOKEN_DEFAULTS.motionShort3, TOKEN_DEFAULTS.animationDurationShort3);\n }\n\n private clearTransitionWork() {\n if (this.transitionTimer) clearTimeout(this.transitionTimer);\n this.transitionTimer = null;\n }\n\n private startOpen() {\n this.clearTransitionWork();\n this.closePending = false;\n this.rendered = true;\n this.phase = 'open';\n }\n\n private startClose() {\n this.clearTransitionWork();\n if (!this.rendered || this.phase === 'closed') {\n this.rendered = false;\n return;\n }\n\n this.closePending = true;\n this.phase = 'closing';\n const duration = this.closeMotionMs;\n if (duration <= 0) {\n this.finishClose();\n return;\n }\n this.transitionTimer = setTimeout(() => {\n this.transitionTimer = null;\n this.finishClose();\n }, duration);\n }\n\n private finishClose() {\n if (this.open || !this.closePending) return;\n this.closePending = false;\n this.rendered = false;\n this.phase = 'closed';\n this.dsAfterClose.emit();\n }\n\n private updateActionsPresence(slot?: HTMLSlotElement) {\n const actions = slot\n ? slot.assignedElements().filter(element => element.tagName === 'DS-BUTTON-UNFILLED')\n : Array.from(this.el.querySelectorAll('ds-button-unfilled[slot=\"actions\"]'));\n this.hasActions = actions.length > 0;\n }\n\n private requestClose(event: CustomEvent<MouseEvent>) {\n if (!this.open || this.phase === 'closing') return;\n this.dsClose.emit(event.detail);\n }\n\n private get role(): 'status' | 'alert' | undefined {\n if (this.announcement === 'polite') return 'status';\n if (this.announcement === 'assertive') return 'alert';\n return undefined;\n }\n\n private get ariaLive(): 'polite' | 'assertive' | undefined {\n return this.announcement === 'none' ? undefined : this.announcement;\n }\n\n render() {\n const closing = this.phase === 'closing';\n const hostClass = {\n banner: true,\n 'banner--open': this.phase === 'open',\n 'banner--closing': closing,\n 'banner--hidden': !this.rendered,\n 'banner--has-actions': this.hasActions,\n [`banner--orientation-${this.orientation}`]: true,\n [`banner--intent-${this.intent}`]: true,\n [`banner--contrast-${this.contrast}`]: true,\n };\n\n return (\n <Host class={hostClass}>\n <div class=\"banner-browser-edge\" aria-hidden=\"true\" />\n <div class=\"banner-overflow\">\n <section\n class=\"banner-surface ds-chrome-header ds-chrome-header--wrapping\"\n role={this.role}\n aria-live={this.ariaLive}\n aria-atomic={this.announcement === 'none' ? undefined : 'true'}\n aria-hidden={closing ? 'true' : undefined}\n inert={closing ? true : undefined}\n data-phase={this.phase}\n >\n {/* eslint-disable-next-line compomo/prefer-direct-ds-text -- The header-copy lane owns the 6px container inset separately from the inline text flow's 2px inset. */}\n <div class=\"banner-copy ds-chrome-header__copy ds-chrome-header__copy--wrapping ds-control--md\">\n <ds-text class=\"banner-copy-flow\" as=\"div\" variant=\"text-body-medium\" color=\"inherit\">\n {this.heading && (\n <ds-text\n class=\"banner-heading\"\n as=\"span\"\n variant=\"text-title-small\"\n color=\"inherit\"\n >\n {this.heading}\n </ds-text>\n )}\n <span class=\"banner-description\">{this.description}</span>\n </ds-text>\n </div>\n <div class=\"banner-trailing ds-chrome-header__trailing\">\n <div class={{ 'banner-actions': true, 'banner-actions--empty': !this.hasActions }}>\n <slot\n name=\"actions\"\n onSlotchange={(event: Event) =>\n this.updateActionsPresence(event.currentTarget as HTMLSlotElement)\n }\n />\n </div>\n <div class=\"banner-dismiss\">\n <ds-tooltip label={this.dismissLabel} side=\"bottom\" size=\"sm\">\n <ds-button-unfilled\n class=\"banner-close\"\n variant=\"icon\"\n icon=\"Cross\"\n size=\"md\"\n type=\"button\"\n background={this.contrast}\n aria-label={this.dismissLabel}\n hasBorder={false}\n activeFill={false}\n onDsClick={(event: CustomEvent<MouseEvent>) => this.requestClose(event)}\n />\n </ds-tooltip>\n </div>\n </div>\n </section>\n </div>\n </Host>\n );\n }\n}\n",
336
362
  "type": "registry:ui"
337
363
  }
338
364
  ]
@@ -82,7 +82,30 @@
82
82
  "label": "Rendered behavior coverage",
83
83
  "path": "tests/e2e/bar-action.spec.ts"
84
84
  }
85
- ]
85
+ ],
86
+ "styling": {
87
+ "rationale": "The component owns the protected recipes below; unspecified host properties and application layout remain customizable.",
88
+ "protected": [
89
+ {
90
+ "properties": [
91
+ "background",
92
+ "border",
93
+ "box-shadow",
94
+ "color"
95
+ ],
96
+ "reason": "The component owns its surface and interaction treatment; use its semantic presentation props or compose an application-owned surrounding surface.",
97
+ "props": []
98
+ },
99
+ {
100
+ "properties": [
101
+ "border-radius"
102
+ ],
103
+ "reason": "The component owns its surface radius; retain the shared recipe and customize an outer wrapper when needed.",
104
+ "props": []
105
+ }
106
+ ],
107
+ "customProperties": []
108
+ }
86
109
  },
87
110
  "api": {
88
111
  "props": {
@@ -106,7 +106,12 @@
106
106
  "label": "Surface ownership and trigger state",
107
107
  "path": "src/docs/SurfaceOwnership.mdx"
108
108
  }
109
- ]
109
+ ],
110
+ "styling": {
111
+ "rationale": "The host participates in shell-owned layout; internal tabs and chrome own their recipes.",
112
+ "protected": [],
113
+ "customProperties": []
114
+ }
110
115
  },
111
116
  "api": {
112
117
  "props": {
@@ -104,7 +104,30 @@
104
104
  "label": "Surface ownership and trigger state",
105
105
  "path": "src/docs/SurfaceOwnership.mdx"
106
106
  }
107
- ]
107
+ ],
108
+ "styling": {
109
+ "rationale": "The component owns the protected recipes below; unspecified host properties and application layout remain customizable.",
110
+ "protected": [
111
+ {
112
+ "properties": [
113
+ "background",
114
+ "border",
115
+ "box-shadow",
116
+ "color"
117
+ ],
118
+ "reason": "The component owns its surface and interaction treatment; use its semantic presentation props or compose an application-owned surrounding surface.",
119
+ "props": []
120
+ },
121
+ {
122
+ "properties": [
123
+ "border-radius"
124
+ ],
125
+ "reason": "The component owns its surface radius; retain the shared recipe and customize an outer wrapper when needed.",
126
+ "props": []
127
+ }
128
+ ],
129
+ "customProperties": []
130
+ }
108
131
  },
109
132
  "api": {
110
133
  "props": {
@@ -123,7 +123,32 @@
123
123
  "label": "Surface ownership and trigger state",
124
124
  "path": "src/docs/SurfaceOwnership.mdx"
125
125
  }
126
- ]
126
+ ],
127
+ "styling": {
128
+ "rationale": "The component owns the protected recipes below; unspecified host properties and application layout remain customizable.",
129
+ "protected": [
130
+ {
131
+ "properties": [
132
+ "background",
133
+ "border",
134
+ "box-shadow",
135
+ "color"
136
+ ],
137
+ "reason": "The component owns its surface and interaction treatment; use its semantic presentation props or compose an application-owned surrounding surface.",
138
+ "props": [
139
+ "variant"
140
+ ]
141
+ },
142
+ {
143
+ "properties": [
144
+ "border-radius"
145
+ ],
146
+ "reason": "The component owns its surface radius; retain the shared recipe and customize an outer wrapper when needed.",
147
+ "props": []
148
+ }
149
+ ],
150
+ "customProperties": []
151
+ }
127
152
  },
128
153
  "api": {
129
154
  "props": {
@@ -90,7 +90,30 @@
90
90
  "label": "Cross-browser workflow behavior",
91
91
  "path": "tests/e2e/bar-workflow.spec.ts"
92
92
  }
93
- ]
93
+ ],
94
+ "styling": {
95
+ "rationale": "The component owns the protected recipes below; unspecified host properties and application layout remain customizable.",
96
+ "protected": [
97
+ {
98
+ "properties": [
99
+ "background",
100
+ "border",
101
+ "box-shadow",
102
+ "color"
103
+ ],
104
+ "reason": "The component owns its surface and interaction treatment; use its semantic presentation props or compose an application-owned surrounding surface.",
105
+ "props": []
106
+ },
107
+ {
108
+ "properties": [
109
+ "border-radius"
110
+ ],
111
+ "reason": "The component owns its surface radius; retain the shared recipe and customize an outer wrapper when needed.",
112
+ "props": []
113
+ }
114
+ ],
115
+ "customProperties": []
116
+ }
94
117
  },
95
118
  "api": {
96
119
  "props": {
@@ -82,7 +82,12 @@
82
82
  "label": "Expanded page-header composition",
83
83
  "path": "src/wc/components/BarTitle/BarTitle.tsx"
84
84
  }
85
- ]
85
+ ],
86
+ "styling": {
87
+ "rationale": "The breadcrumb owns internal truncation; the application supplies the host width and placement.",
88
+ "protected": [],
89
+ "customProperties": []
90
+ }
86
91
  },
87
92
  "api": {
88
93
  "props": {
@@ -107,7 +107,74 @@
107
107
  "label": "Surface ownership and trigger state",
108
108
  "path": "src/docs/SurfaceOwnership.mdx"
109
109
  }
110
- ]
110
+ ],
111
+ "styling": {
112
+ "rationale": "The component owns the protected recipes below; unspecified host properties and application layout remain customizable.",
113
+ "protected": [
114
+ {
115
+ "properties": [
116
+ "font",
117
+ "font-size",
118
+ "font-weight",
119
+ "font-family",
120
+ "line-height",
121
+ "letter-spacing",
122
+ "height",
123
+ "min-height",
124
+ "max-height",
125
+ "padding",
126
+ "gap"
127
+ ],
128
+ "reason": "The control density is one atomic recipe; use the size and inset props instead of replacing internal metrics.",
129
+ "props": [
130
+ "size",
131
+ "isInset",
132
+ "insetDepth",
133
+ "labelEmphasis"
134
+ ]
135
+ },
136
+ {
137
+ "properties": [
138
+ "border-radius"
139
+ ],
140
+ "reason": "The component owns its corner treatment; select the supported radius treatment.",
141
+ "props": [
142
+ "rounded",
143
+ "size"
144
+ ]
145
+ },
146
+ {
147
+ "properties": [
148
+ "width",
149
+ "min-width",
150
+ "max-width"
151
+ ],
152
+ "reason": "The button owns hug/fill sizing and its minimum square geometry; use width and constrain the surrounding layout.",
153
+ "props": [
154
+ "width",
155
+ "size"
156
+ ]
157
+ },
158
+ {
159
+ "properties": [
160
+ "background",
161
+ "border",
162
+ "box-shadow",
163
+ "color"
164
+ ],
165
+ "reason": "The component owns its surface and interaction treatment; use its semantic presentation props or compose an application-owned surrounding surface.",
166
+ "props": [
167
+ "intent",
168
+ "contrast",
169
+ "background",
170
+ "variant",
171
+ "hasBorder",
172
+ "size"
173
+ ]
174
+ }
175
+ ],
176
+ "customProperties": []
177
+ }
111
178
  },
112
179
  "api": {
113
180
  "props": {
@@ -96,7 +96,70 @@
96
96
  "label": "Surface ownership and trigger state",
97
97
  "path": "src/docs/SurfaceOwnership.mdx"
98
98
  }
99
- ]
99
+ ],
100
+ "styling": {
101
+ "rationale": "The component owns the protected recipes below; unspecified host properties and application layout remain customizable.",
102
+ "protected": [
103
+ {
104
+ "properties": [
105
+ "font",
106
+ "font-size",
107
+ "font-weight",
108
+ "font-family",
109
+ "line-height",
110
+ "letter-spacing",
111
+ "height",
112
+ "min-height",
113
+ "max-height",
114
+ "padding",
115
+ "gap"
116
+ ],
117
+ "reason": "The control density is one atomic recipe; use the size and inset props instead of replacing internal metrics.",
118
+ "props": [
119
+ "size",
120
+ "isInset",
121
+ "insetDepth",
122
+ "labelEmphasis"
123
+ ]
124
+ },
125
+ {
126
+ "properties": [
127
+ "border-radius"
128
+ ],
129
+ "reason": "The component owns its corner treatment; select the supported radius treatment.",
130
+ "props": [
131
+ "rounded",
132
+ "size"
133
+ ]
134
+ },
135
+ {
136
+ "properties": [
137
+ "width",
138
+ "min-width",
139
+ "max-width"
140
+ ],
141
+ "reason": "The button owns hug/fill sizing and its minimum square geometry; use width and constrain the surrounding layout.",
142
+ "props": [
143
+ "width",
144
+ "size"
145
+ ]
146
+ },
147
+ {
148
+ "properties": [
149
+ "background",
150
+ "border",
151
+ "box-shadow",
152
+ "color"
153
+ ],
154
+ "reason": "The component owns its surface and interaction treatment; use its semantic presentation props or compose an application-owned surrounding surface.",
155
+ "props": [
156
+ "variant",
157
+ "size"
158
+ ]
159
+ }
160
+ ],
161
+ "customProperties": []
162
+ }
100
163
  },
101
164
  "api": {
102
165
  "props": {