finding(components): button-group implements no selection, and never wires the per-button onClick / disabled it declares — the catalog authors 29 keys nothing reads #7077

Description

@os-sam

Filed unassigned out of objectui#6347, whose docs half deletes the page's ## Selection Mode section. Deleting the section is the honest thing to do about the documentation; it says nothing about whether the capability should exist. That question is this card.

What the renderer does, read off packages/components/src/renderers/basic/button-group.tsx

It maps schema.buttons to Button elements and reads exactly five things: schema.className, schema.variant, schema.size, button.variant, button.size, button.className, button.label. Nothing else. Specifically:

  • No selection behaviour at all — no selectionMode read, no group-level value read, no state, no onValueChange. selectionMode appears nowhere in the file; the only value reads in packages/components/src/renderers/** belong to other component types.
  • ButtonGroupButton.onClick is declared and never wired.navigation.ts:325 declares onClick?: () = a void function, mirrored at zod/navigation.zod.ts:147 as z.function().optional(). The renderer passes no onClick to Button, so a declared handler is inert.
  • disabled is declared twice and read neither time.ButtonGroupButton.disabled?: boolean (navigation.ts:321) and BaseSchema.disabled?: boolean | string reach ButtonGroupSchema; the renderer forwards neither to Button. Every sibling that declares item-level disabledtabs, select, dropdown-menu, menubar, context-menu, and toggle-group since objectui#4632 — forwards it. button-group is the outlier.

What the catalog authors, counted

All six examples/schema-catalog/src/schemas/components-basic-button-group/*.json fixtures author keys the shipped types do not declare. Counted per fixture:

fixturebuttons[].valuebuttons[].iconvalueselectionMode
basic-button-group.json3000
icon-toolbar.json3300
multiple-selection.json3311
outline-variant.json3000
single-selection.json3011
with-icons.json2200
total17822

29 occurrences, six of six fixtures. None of the four keys is declared on ButtonGroupSchema / ButtonGroupButton (navigation.ts:305-351, zod/navigation.zod.ts:142-159), and none is read by any renderer.

Overlap with objectui#5931, declared rather than left for a reader to find: that card already records with-icons.json's buttons[].icon (two occurrences), alongside the same shape in breadcrumb and command. What is new here is the other six icon occurrences in icon-toolbar.json and multiple-selection.json, the 17 buttons[].value, and the schema-level pair. If #5931 is settled first, its ruling should extend to this component's other two fixtures.

Why nothing goes red

BaseSchema is .passthrough() and carries [key: string]: any, so every one of the 29 parses green and type-checks — admitted unexamined, not refused (the reading component-fixture-declared-keys.test.ts and undeclared-but-consumed-keys-6150.test.ts both record). catalog-gallery-render.test.tsx renders each fixture and only fails on an unregistered type. So the demos render as inert button rows and every gate stays green.

The catalog is the corpus AI authoring tools retrieve from, which is what makes this more than cosmetic: an author copying single-selection.json gets a schema that validates, publishes, and does nothing.

The decision, stated as a fork

  1. Implement it. Wire onClick and disabled (mechanical, and the sibling convention above already fixes their spelling), then decide separately whether selectionMode / value / per-button value / icon become declared members with real readers. That is a capability addition and needs the four-axis judgement, not a dev's guess.
  2. Retire the authored surface. Under ADR-0049 enforce-or-remove, drop the 29 undeclared keys from the fixtures and leave the component a presentational group. onClick and disabled would then be declared-but-dead and face the same question one level in.

I did not choose. objectui#6347's PR corrects the documentation to the shipped surface and touches no fixture and no renderer.

Refs: objectui#6347 · objectui#5931 · objectui#4632 · objectui#6157.

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfindingpm:queuepm:retriageAwaiting triage re-judgement — coexists with the standing pm:* label; queued cards skip dispatch

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
       blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
      }
      } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
      })();
      (function(){
      try {
      var __m = "github.com";
      var __re = new RegExp('^' + "github\\.com" + '
      
      Skip to content

      finding(components): button-group implements no selection, and never wires the per-button onClick / disabled it declares — the catalog authors 29 keys nothing reads #7077

      Description

      @os-sam

      Filed unassigned out of objectui#6347, whose docs half deletes the page's ## Selection Mode section. Deleting the section is the honest thing to do about the documentation; it says nothing about whether the capability should exist. That question is this card.

      What the renderer does, read off packages/components/src/renderers/basic/button-group.tsx

      It maps schema.buttons to Button elements and reads exactly five things: schema.className, schema.variant, schema.size, button.variant, button.size, button.className, button.label. Nothing else. Specifically:

      • No selection behaviour at all — no selectionMode read, no group-level value read, no state, no onValueChange. selectionMode appears nowhere in the file; the only value reads in packages/components/src/renderers/** belong to other component types.
      • ButtonGroupButton.onClick is declared and never wired.navigation.ts:325 declares onClick?: () = a void function, mirrored at zod/navigation.zod.ts:147 as z.function().optional(). The renderer passes no onClick to Button, so a declared handler is inert.
      • disabled is declared twice and read neither time.ButtonGroupButton.disabled?: boolean (navigation.ts:321) and BaseSchema.disabled?: boolean | string reach ButtonGroupSchema; the renderer forwards neither to Button. Every sibling that declares item-level disabledtabs, select, dropdown-menu, menubar, context-menu, and toggle-group since objectui#4632 — forwards it. button-group is the outlier.

      What the catalog authors, counted

      All six examples/schema-catalog/src/schemas/components-basic-button-group/*.json fixtures author keys the shipped types do not declare. Counted per fixture:

      fixturebuttons[].valuebuttons[].iconvalueselectionMode
      basic-button-group.json3000
      icon-toolbar.json3300
      multiple-selection.json3311
      outline-variant.json3000
      single-selection.json3011
      with-icons.json2200
      total17822

      29 occurrences, six of six fixtures. None of the four keys is declared on ButtonGroupSchema / ButtonGroupButton (navigation.ts:305-351, zod/navigation.zod.ts:142-159), and none is read by any renderer.

      Overlap with objectui#5931, declared rather than left for a reader to find: that card already records with-icons.json's buttons[].icon (two occurrences), alongside the same shape in breadcrumb and command. What is new here is the other six icon occurrences in icon-toolbar.json and multiple-selection.json, the 17 buttons[].value, and the schema-level pair. If #5931 is settled first, its ruling should extend to this component's other two fixtures.

      Why nothing goes red

      BaseSchema is .passthrough() and carries [key: string]: any, so every one of the 29 parses green and type-checks — admitted unexamined, not refused (the reading component-fixture-declared-keys.test.ts and undeclared-but-consumed-keys-6150.test.ts both record). catalog-gallery-render.test.tsx renders each fixture and only fails on an unregistered type. So the demos render as inert button rows and every gate stays green.

      The catalog is the corpus AI authoring tools retrieve from, which is what makes this more than cosmetic: an author copying single-selection.json gets a schema that validates, publishes, and does nothing.

      The decision, stated as a fork

      1. Implement it. Wire onClick and disabled (mechanical, and the sibling convention above already fixes their spelling), then decide separately whether selectionMode / value / per-button value / icon become declared members with real readers. That is a capability addition and needs the four-axis judgement, not a dev's guess.
      2. Retire the authored surface. Under ADR-0049 enforce-or-remove, drop the 29 undeclared keys from the fixtures and leave the component a presentational group. onClick and disabled would then be declared-but-dead and face the same question one level in.

      I did not choose. objectui#6347's PR corrects the documentation to the shipped surface and touches no fixture and no renderer.

      Refs: objectui#6347 · objectui#5931 · objectui#4632 · objectui#6157.

      Metadata

      Metadata

      Assignees

      No one assigned

        Labels

        domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfindingpm:queuepm:retriageAwaiting triage re-judgement — coexists with the standing pm:* label; queued cards skip dispatch

        Type

        No type

        Projects

        No projects

          Milestone

          No milestone

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
          Skip to content

          finding(components): button-group implements no selection, and never wires the per-button onClick / disabled it declares — the catalog authors 29 keys nothing reads #7077

          Description

          @os-sam

          Filed unassigned out of objectui#6347, whose docs half deletes the page's ## Selection Mode section. Deleting the section is the honest thing to do about the documentation; it says nothing about whether the capability should exist. That question is this card.

          What the renderer does, read off packages/components/src/renderers/basic/button-group.tsx

          It maps schema.buttons to Button elements and reads exactly five things: schema.className, schema.variant, schema.size, button.variant, button.size, button.className, button.label. Nothing else. Specifically:

          • No selection behaviour at all — no selectionMode read, no group-level value read, no state, no onValueChange. selectionMode appears nowhere in the file; the only value reads in packages/components/src/renderers/** belong to other component types.
          • ButtonGroupButton.onClick is declared and never wired.navigation.ts:325 declares onClick?: () = a void function, mirrored at zod/navigation.zod.ts:147 as z.function().optional(). The renderer passes no onClick to Button, so a declared handler is inert.
          • disabled is declared twice and read neither time.ButtonGroupButton.disabled?: boolean (navigation.ts:321) and BaseSchema.disabled?: boolean | string reach ButtonGroupSchema; the renderer forwards neither to Button. Every sibling that declares item-level disabledtabs, select, dropdown-menu, menubar, context-menu, and toggle-group since objectui#4632 — forwards it. button-group is the outlier.

          What the catalog authors, counted

          All six examples/schema-catalog/src/schemas/components-basic-button-group/*.json fixtures author keys the shipped types do not declare. Counted per fixture:

          fixturebuttons[].valuebuttons[].iconvalueselectionMode
          basic-button-group.json3000
          icon-toolbar.json3300
          multiple-selection.json3311
          outline-variant.json3000
          single-selection.json3011
          with-icons.json2200
          total17822

          29 occurrences, six of six fixtures. None of the four keys is declared on ButtonGroupSchema / ButtonGroupButton (navigation.ts:305-351, zod/navigation.zod.ts:142-159), and none is read by any renderer.

          Overlap with objectui#5931, declared rather than left for a reader to find: that card already records with-icons.json's buttons[].icon (two occurrences), alongside the same shape in breadcrumb and command. What is new here is the other six icon occurrences in icon-toolbar.json and multiple-selection.json, the 17 buttons[].value, and the schema-level pair. If #5931 is settled first, its ruling should extend to this component's other two fixtures.

          Why nothing goes red

          BaseSchema is .passthrough() and carries [key: string]: any, so every one of the 29 parses green and type-checks — admitted unexamined, not refused (the reading component-fixture-declared-keys.test.ts and undeclared-but-consumed-keys-6150.test.ts both record). catalog-gallery-render.test.tsx renders each fixture and only fails on an unregistered type. So the demos render as inert button rows and every gate stays green.

          The catalog is the corpus AI authoring tools retrieve from, which is what makes this more than cosmetic: an author copying single-selection.json gets a schema that validates, publishes, and does nothing.

          The decision, stated as a fork

          1. Implement it. Wire onClick and disabled (mechanical, and the sibling convention above already fixes their spelling), then decide separately whether selectionMode / value / per-button value / icon become declared members with real readers. That is a capability addition and needs the four-axis judgement, not a dev's guess.
          2. Retire the authored surface. Under ADR-0049 enforce-or-remove, drop the 29 undeclared keys from the fixtures and leave the component a presentational group. onClick and disabled would then be declared-but-dead and face the same question one level in.

          I did not choose. objectui#6347's PR corrects the documentation to the shipped surface and touches no fixture and no renderer.

          Refs: objectui#6347 · objectui#5931 · objectui#4632 · objectui#6157.

          Metadata

          Metadata

          Assignees

          No one assigned

            Labels

            domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfindingpm:queuepm:retriageAwaiting triage re-judgement — coexists with the standing pm:* label; queued cards skip dispatch

            Type

            No type

            Projects

            No projects

              Milestone

              No milestone

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
              Skip to content

              finding(components): button-group implements no selection, and never wires the per-button onClick / disabled it declares — the catalog authors 29 keys nothing reads #7077

              Description

              @os-sam

              Filed unassigned out of objectui#6347, whose docs half deletes the page's ## Selection Mode section. Deleting the section is the honest thing to do about the documentation; it says nothing about whether the capability should exist. That question is this card.

              What the renderer does, read off packages/components/src/renderers/basic/button-group.tsx

              It maps schema.buttons to Button elements and reads exactly five things: schema.className, schema.variant, schema.size, button.variant, button.size, button.className, button.label. Nothing else. Specifically:

              • No selection behaviour at all — no selectionMode read, no group-level value read, no state, no onValueChange. selectionMode appears nowhere in the file; the only value reads in packages/components/src/renderers/** belong to other component types.
              • ButtonGroupButton.onClick is declared and never wired.navigation.ts:325 declares onClick?: () = a void function, mirrored at zod/navigation.zod.ts:147 as z.function().optional(). The renderer passes no onClick to Button, so a declared handler is inert.
              • disabled is declared twice and read neither time.ButtonGroupButton.disabled?: boolean (navigation.ts:321) and BaseSchema.disabled?: boolean | string reach ButtonGroupSchema; the renderer forwards neither to Button. Every sibling that declares item-level disabledtabs, select, dropdown-menu, menubar, context-menu, and toggle-group since objectui#4632 — forwards it. button-group is the outlier.

              What the catalog authors, counted

              All six examples/schema-catalog/src/schemas/components-basic-button-group/*.json fixtures author keys the shipped types do not declare. Counted per fixture:

              fixturebuttons[].valuebuttons[].iconvalueselectionMode
              basic-button-group.json3000
              icon-toolbar.json3300
              multiple-selection.json3311
              outline-variant.json3000
              single-selection.json3011
              with-icons.json2200
              total17822

              29 occurrences, six of six fixtures. None of the four keys is declared on ButtonGroupSchema / ButtonGroupButton (navigation.ts:305-351, zod/navigation.zod.ts:142-159), and none is read by any renderer.

              Overlap with objectui#5931, declared rather than left for a reader to find: that card already records with-icons.json's buttons[].icon (two occurrences), alongside the same shape in breadcrumb and command. What is new here is the other six icon occurrences in icon-toolbar.json and multiple-selection.json, the 17 buttons[].value, and the schema-level pair. If #5931 is settled first, its ruling should extend to this component's other two fixtures.

              Why nothing goes red

              BaseSchema is .passthrough() and carries [key: string]: any, so every one of the 29 parses green and type-checks — admitted unexamined, not refused (the reading component-fixture-declared-keys.test.ts and undeclared-but-consumed-keys-6150.test.ts both record). catalog-gallery-render.test.tsx renders each fixture and only fails on an unregistered type. So the demos render as inert button rows and every gate stays green.

              The catalog is the corpus AI authoring tools retrieve from, which is what makes this more than cosmetic: an author copying single-selection.json gets a schema that validates, publishes, and does nothing.

              The decision, stated as a fork

              1. Implement it. Wire onClick and disabled (mechanical, and the sibling convention above already fixes their spelling), then decide separately whether selectionMode / value / per-button value / icon become declared members with real readers. That is a capability addition and needs the four-axis judgement, not a dev's guess.
              2. Retire the authored surface. Under ADR-0049 enforce-or-remove, drop the 29 undeclared keys from the fixtures and leave the component a presentational group. onClick and disabled would then be declared-but-dead and face the same question one level in.

              I did not choose. objectui#6347's PR corrects the documentation to the shipped surface and touches no fixture and no renderer.

              Refs: objectui#6347 · objectui#5931 · objectui#4632 · objectui#6157.

              Metadata

              Metadata

              Assignees

              No one assigned

                Labels

                domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfindingpm:queuepm:retriageAwaiting triage re-judgement — coexists with the standing pm:* label; queued cards skip dispatch

                Type

                No type

                Projects

                No projects

                  Milestone

                  No milestone

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions

                  , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
                  Skip to content

                  finding(components): button-group implements no selection, and never wires the per-button onClick / disabled it declares — the catalog authors 29 keys nothing reads #7077

                  Description

                  @os-sam

                  Filed unassigned out of objectui#6347, whose docs half deletes the page's ## Selection Mode section. Deleting the section is the honest thing to do about the documentation; it says nothing about whether the capability should exist. That question is this card.

                  What the renderer does, read off packages/components/src/renderers/basic/button-group.tsx

                  It maps schema.buttons to Button elements and reads exactly five things: schema.className, schema.variant, schema.size, button.variant, button.size, button.className, button.label. Nothing else. Specifically:

                  • No selection behaviour at all — no selectionMode read, no group-level value read, no state, no onValueChange. selectionMode appears nowhere in the file; the only value reads in packages/components/src/renderers/** belong to other component types.
                  • ButtonGroupButton.onClick is declared and never wired.navigation.ts:325 declares onClick?: () = a void function, mirrored at zod/navigation.zod.ts:147 as z.function().optional(). The renderer passes no onClick to Button, so a declared handler is inert.
                  • disabled is declared twice and read neither time.ButtonGroupButton.disabled?: boolean (navigation.ts:321) and BaseSchema.disabled?: boolean | string reach ButtonGroupSchema; the renderer forwards neither to Button. Every sibling that declares item-level disabledtabs, select, dropdown-menu, menubar, context-menu, and toggle-group since objectui#4632 — forwards it. button-group is the outlier.

                  What the catalog authors, counted

                  All six examples/schema-catalog/src/schemas/components-basic-button-group/*.json fixtures author keys the shipped types do not declare. Counted per fixture:

                  fixturebuttons[].valuebuttons[].iconvalueselectionMode
                  basic-button-group.json3000
                  icon-toolbar.json3300
                  multiple-selection.json3311
                  outline-variant.json3000
                  single-selection.json3011
                  with-icons.json2200
                  total17822

                  29 occurrences, six of six fixtures. None of the four keys is declared on ButtonGroupSchema / ButtonGroupButton (navigation.ts:305-351, zod/navigation.zod.ts:142-159), and none is read by any renderer.

                  Overlap with objectui#5931, declared rather than left for a reader to find: that card already records with-icons.json's buttons[].icon (two occurrences), alongside the same shape in breadcrumb and command. What is new here is the other six icon occurrences in icon-toolbar.json and multiple-selection.json, the 17 buttons[].value, and the schema-level pair. If #5931 is settled first, its ruling should extend to this component's other two fixtures.

                  Why nothing goes red

                  BaseSchema is .passthrough() and carries [key: string]: any, so every one of the 29 parses green and type-checks — admitted unexamined, not refused (the reading component-fixture-declared-keys.test.ts and undeclared-but-consumed-keys-6150.test.ts both record). catalog-gallery-render.test.tsx renders each fixture and only fails on an unregistered type. So the demos render as inert button rows and every gate stays green.

                  The catalog is the corpus AI authoring tools retrieve from, which is what makes this more than cosmetic: an author copying single-selection.json gets a schema that validates, publishes, and does nothing.

                  The decision, stated as a fork

                  1. Implement it. Wire onClick and disabled (mechanical, and the sibling convention above already fixes their spelling), then decide separately whether selectionMode / value / per-button value / icon become declared members with real readers. That is a capability addition and needs the four-axis judgement, not a dev's guess.
                  2. Retire the authored surface. Under ADR-0049 enforce-or-remove, drop the 29 undeclared keys from the fixtures and leave the component a presentational group. onClick and disabled would then be declared-but-dead and face the same question one level in.

                  I did not choose. objectui#6347's PR corrects the documentation to the shipped surface and touches no fixture and no renderer.

                  Refs: objectui#6347 · objectui#5931 · objectui#4632 · objectui#6157.

                  Metadata

                  Metadata

                  Assignees

                  No one assigned

                    Labels

                    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfindingpm:queuepm:retriageAwaiting triage re-judgement — coexists with the standing pm:* label; queued cards skip dispatch

                    Type

                    No type

                    Projects

                    No projects

                      Milestone

                      No milestone

                      Relationships

                      None yet

                      Development

                      No branches or pull requests

                      Issue actions

                      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                      Skip to content

                      finding(components): button-group implements no selection, and never wires the per-button onClick / disabled it declares — the catalog authors 29 keys nothing reads #7077

                      Description

                      @os-sam

                      Filed unassigned out of objectui#6347, whose docs half deletes the page's ## Selection Mode section. Deleting the section is the honest thing to do about the documentation; it says nothing about whether the capability should exist. That question is this card.

                      What the renderer does, read off packages/components/src/renderers/basic/button-group.tsx

                      It maps schema.buttons to Button elements and reads exactly five things: schema.className, schema.variant, schema.size, button.variant, button.size, button.className, button.label. Nothing else. Specifically:

                      • No selection behaviour at all — no selectionMode read, no group-level value read, no state, no onValueChange. selectionMode appears nowhere in the file; the only value reads in packages/components/src/renderers/** belong to other component types.
                      • ButtonGroupButton.onClick is declared and never wired.navigation.ts:325 declares onClick?: () = a void function, mirrored at zod/navigation.zod.ts:147 as z.function().optional(). The renderer passes no onClick to Button, so a declared handler is inert.
                      • disabled is declared twice and read neither time.ButtonGroupButton.disabled?: boolean (navigation.ts:321) and BaseSchema.disabled?: boolean | string reach ButtonGroupSchema; the renderer forwards neither to Button. Every sibling that declares item-level disabledtabs, select, dropdown-menu, menubar, context-menu, and toggle-group since objectui#4632 — forwards it. button-group is the outlier.

                      What the catalog authors, counted

                      All six examples/schema-catalog/src/schemas/components-basic-button-group/*.json fixtures author keys the shipped types do not declare. Counted per fixture:

                      fixturebuttons[].valuebuttons[].iconvalueselectionMode
                      basic-button-group.json3000
                      icon-toolbar.json3300
                      multiple-selection.json3311
                      outline-variant.json3000
                      single-selection.json3011
                      with-icons.json2200
                      total17822

                      29 occurrences, six of six fixtures. None of the four keys is declared on ButtonGroupSchema / ButtonGroupButton (navigation.ts:305-351, zod/navigation.zod.ts:142-159), and none is read by any renderer.

                      Overlap with objectui#5931, declared rather than left for a reader to find: that card already records with-icons.json's buttons[].icon (two occurrences), alongside the same shape in breadcrumb and command. What is new here is the other six icon occurrences in icon-toolbar.json and multiple-selection.json, the 17 buttons[].value, and the schema-level pair. If #5931 is settled first, its ruling should extend to this component's other two fixtures.

                      Why nothing goes red

                      BaseSchema is .passthrough() and carries [key: string]: any, so every one of the 29 parses green and type-checks — admitted unexamined, not refused (the reading component-fixture-declared-keys.test.ts and undeclared-but-consumed-keys-6150.test.ts both record). catalog-gallery-render.test.tsx renders each fixture and only fails on an unregistered type. So the demos render as inert button rows and every gate stays green.

                      The catalog is the corpus AI authoring tools retrieve from, which is what makes this more than cosmetic: an author copying single-selection.json gets a schema that validates, publishes, and does nothing.

                      The decision, stated as a fork

                      1. Implement it. Wire onClick and disabled (mechanical, and the sibling convention above already fixes their spelling), then decide separately whether selectionMode / value / per-button value / icon become declared members with real readers. That is a capability addition and needs the four-axis judgement, not a dev's guess.
                      2. Retire the authored surface. Under ADR-0049 enforce-or-remove, drop the 29 undeclared keys from the fixtures and leave the component a presentational group. onClick and disabled would then be declared-but-dead and face the same question one level in.

                      I did not choose. objectui#6347's PR corrects the documentation to the shipped surface and touches no fixture and no renderer.

                      Refs: objectui#6347 · objectui#5931 · objectui#4632 · objectui#6157.

                      Metadata

                      Metadata

                      Assignees

                      No one assigned

                        Labels

                        domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfindingpm:queuepm:retriageAwaiting triage re-judgement — coexists with the standing pm:* label; queued cards skip dispatch

                        Type

                        No type

                        Projects

                        No projects

                          Milestone

                          No milestone

                          Relationships

                          None yet

                          Development

                          No branches or pull requests

                          Issue actions

                          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                          Skip to content

                          finding(components): button-group implements no selection, and never wires the per-button onClick / disabled it declares — the catalog authors 29 keys nothing reads #7077

                          Description

                          @os-sam

                          Filed unassigned out of objectui#6347, whose docs half deletes the page's ## Selection Mode section. Deleting the section is the honest thing to do about the documentation; it says nothing about whether the capability should exist. That question is this card.

                          What the renderer does, read off packages/components/src/renderers/basic/button-group.tsx

                          It maps schema.buttons to Button elements and reads exactly five things: schema.className, schema.variant, schema.size, button.variant, button.size, button.className, button.label. Nothing else. Specifically:

                          • No selection behaviour at all — no selectionMode read, no group-level value read, no state, no onValueChange. selectionMode appears nowhere in the file; the only value reads in packages/components/src/renderers/** belong to other component types.
                          • ButtonGroupButton.onClick is declared and never wired.navigation.ts:325 declares onClick?: () = a void function, mirrored at zod/navigation.zod.ts:147 as z.function().optional(). The renderer passes no onClick to Button, so a declared handler is inert.
                          • disabled is declared twice and read neither time.ButtonGroupButton.disabled?: boolean (navigation.ts:321) and BaseSchema.disabled?: boolean | string reach ButtonGroupSchema; the renderer forwards neither to Button. Every sibling that declares item-level disabledtabs, select, dropdown-menu, menubar, context-menu, and toggle-group since objectui#4632 — forwards it. button-group is the outlier.

                          What the catalog authors, counted

                          All six examples/schema-catalog/src/schemas/components-basic-button-group/*.json fixtures author keys the shipped types do not declare. Counted per fixture:

                          fixturebuttons[].valuebuttons[].iconvalueselectionMode
                          basic-button-group.json3000
                          icon-toolbar.json3300
                          multiple-selection.json3311
                          outline-variant.json3000
                          single-selection.json3011
                          with-icons.json2200
                          total17822

                          29 occurrences, six of six fixtures. None of the four keys is declared on ButtonGroupSchema / ButtonGroupButton (navigation.ts:305-351, zod/navigation.zod.ts:142-159), and none is read by any renderer.

                          Overlap with objectui#5931, declared rather than left for a reader to find: that card already records with-icons.json's buttons[].icon (two occurrences), alongside the same shape in breadcrumb and command. What is new here is the other six icon occurrences in icon-toolbar.json and multiple-selection.json, the 17 buttons[].value, and the schema-level pair. If #5931 is settled first, its ruling should extend to this component's other two fixtures.

                          Why nothing goes red

                          BaseSchema is .passthrough() and carries [key: string]: any, so every one of the 29 parses green and type-checks — admitted unexamined, not refused (the reading component-fixture-declared-keys.test.ts and undeclared-but-consumed-keys-6150.test.ts both record). catalog-gallery-render.test.tsx renders each fixture and only fails on an unregistered type. So the demos render as inert button rows and every gate stays green.

                          The catalog is the corpus AI authoring tools retrieve from, which is what makes this more than cosmetic: an author copying single-selection.json gets a schema that validates, publishes, and does nothing.

                          The decision, stated as a fork

                          1. Implement it. Wire onClick and disabled (mechanical, and the sibling convention above already fixes their spelling), then decide separately whether selectionMode / value / per-button value / icon become declared members with real readers. That is a capability addition and needs the four-axis judgement, not a dev's guess.
                          2. Retire the authored surface. Under ADR-0049 enforce-or-remove, drop the 29 undeclared keys from the fixtures and leave the component a presentational group. onClick and disabled would then be declared-but-dead and face the same question one level in.

                          I did not choose. objectui#6347's PR corrects the documentation to the shipped surface and touches no fixture and no renderer.

                          Refs: objectui#6347 · objectui#5931 · objectui#4632 · objectui#6157.

                          Metadata

                          Metadata

                          Assignees

                          No one assigned

                            Labels

                            domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfindingpm:queuepm:retriageAwaiting triage re-judgement — coexists with the standing pm:* label; queued cards skip dispatch

                            Type

                            No type

                            Projects

                            No projects

                              Milestone

                              No milestone

                              Relationships

                              None yet

                              Development

                              No branches or pull requests

                              Issue actions

                              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
                              Skip to content

                              finding(components): button-group implements no selection, and never wires the per-button onClick / disabled it declares — the catalog authors 29 keys nothing reads #7077

                              Description

                              @os-sam

                              Filed unassigned out of objectui#6347, whose docs half deletes the page's ## Selection Mode section. Deleting the section is the honest thing to do about the documentation; it says nothing about whether the capability should exist. That question is this card.

                              What the renderer does, read off packages/components/src/renderers/basic/button-group.tsx

                              It maps schema.buttons to Button elements and reads exactly five things: schema.className, schema.variant, schema.size, button.variant, button.size, button.className, button.label. Nothing else. Specifically:

                              • No selection behaviour at all — no selectionMode read, no group-level value read, no state, no onValueChange. selectionMode appears nowhere in the file; the only value reads in packages/components/src/renderers/** belong to other component types.
                              • ButtonGroupButton.onClick is declared and never wired.navigation.ts:325 declares onClick?: () = a void function, mirrored at zod/navigation.zod.ts:147 as z.function().optional(). The renderer passes no onClick to Button, so a declared handler is inert.
                              • disabled is declared twice and read neither time.ButtonGroupButton.disabled?: boolean (navigation.ts:321) and BaseSchema.disabled?: boolean | string reach ButtonGroupSchema; the renderer forwards neither to Button. Every sibling that declares item-level disabledtabs, select, dropdown-menu, menubar, context-menu, and toggle-group since objectui#4632 — forwards it. button-group is the outlier.

                              What the catalog authors, counted

                              All six examples/schema-catalog/src/schemas/components-basic-button-group/*.json fixtures author keys the shipped types do not declare. Counted per fixture:

                              fixturebuttons[].valuebuttons[].iconvalueselectionMode
                              basic-button-group.json3000
                              icon-toolbar.json3300
                              multiple-selection.json3311
                              outline-variant.json3000
                              single-selection.json3011
                              with-icons.json2200
                              total17822

                              29 occurrences, six of six fixtures. None of the four keys is declared on ButtonGroupSchema / ButtonGroupButton (navigation.ts:305-351, zod/navigation.zod.ts:142-159), and none is read by any renderer.

                              Overlap with objectui#5931, declared rather than left for a reader to find: that card already records with-icons.json's buttons[].icon (two occurrences), alongside the same shape in breadcrumb and command. What is new here is the other six icon occurrences in icon-toolbar.json and multiple-selection.json, the 17 buttons[].value, and the schema-level pair. If #5931 is settled first, its ruling should extend to this component's other two fixtures.

                              Why nothing goes red

                              BaseSchema is .passthrough() and carries [key: string]: any, so every one of the 29 parses green and type-checks — admitted unexamined, not refused (the reading component-fixture-declared-keys.test.ts and undeclared-but-consumed-keys-6150.test.ts both record). catalog-gallery-render.test.tsx renders each fixture and only fails on an unregistered type. So the demos render as inert button rows and every gate stays green.

                              The catalog is the corpus AI authoring tools retrieve from, which is what makes this more than cosmetic: an author copying single-selection.json gets a schema that validates, publishes, and does nothing.

                              The decision, stated as a fork

                              1. Implement it. Wire onClick and disabled (mechanical, and the sibling convention above already fixes their spelling), then decide separately whether selectionMode / value / per-button value / icon become declared members with real readers. That is a capability addition and needs the four-axis judgement, not a dev's guess.
                              2. Retire the authored surface. Under ADR-0049 enforce-or-remove, drop the 29 undeclared keys from the fixtures and leave the component a presentational group. onClick and disabled would then be declared-but-dead and face the same question one level in.

                              I did not choose. objectui#6347's PR corrects the documentation to the shipped surface and touches no fixture and no renderer.

                              Refs: objectui#6347 · objectui#5931 · objectui#4632 · objectui#6157.

                              Metadata

                              Metadata

                              Assignees

                              No one assigned

                                Labels

                                domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfindingpm:queuepm:retriageAwaiting triage re-judgement — coexists with the standing pm:* label; queued cards skip dispatch

                                Type

                                No type

                                Projects

                                No projects

                                  Milestone

                                  No milestone

                                  Relationships

                                  None yet

                                  Development

                                  No branches or pull requests

                                  Issue actions