Repository: kufu/smarthr-ui
Pull request #5522: chore: DropdownMenuGroupをmenuとしてmarkupする
Author: login: AtsushiM
Labels: name: accessibility
Created: 2025-04-03T23:57:13Z | Merged: 2025-05-20T23:17:47Z

<!--
PRのタイトルは、PRによる変更内容が利用者にも伝わるようにお願いします。
通常、PRをスカッシュマージした場合のコミットメッセージがそのままリリースノートやCHANGELOGに反映されるためです。
-->

## 関連URL

<!--
PRに関連するURLなどを記載してください。

e.g.
- JIRA チケットURL (社内向け)
- Slack URL (社内向け)
- GitHub Issues URL (社外向け)
-->

## 概要

<!--
PRを作成した目的や解決したい課題を簡潔に記載してください。
-->

## 変更内容

<!--
PRではどのような変更を加えたのか、マージ後にどう変わるかなどを具体的に記載してください。
コンポーネントの新規追加やスタイル変更などがある場合、キャプチャを添付してください。
コンポーネントのインタフェースが変更(特に破壊的変更)がある場合、変更前後での使用例を記載してください。
-->

## 確認方法

<!--
PRの変更内容を確認する方法について記載してください。
Storybook や Chromatic での確認で十分な場合、そのURLを記載してください。
-->



Diff:
diff --git a/packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/DropdownMenuButton.tsx b/packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/DropdownMenuButton.tsx
index de31fa7e36..fc238ec65c 100644
--- a/packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/DropdownMenuButton.tsx
+++ b/packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/DropdownMenuButton.tsx
@@ -115,7 +115,7 @@ export const DropdownMenuButton: FC<Props & ElementProps> = ({
         classNames={classNames}
       />
       <DropdownContent controllable={true}>
-        <menu ref={containerRef} className={classNames.actionList}>
+        <menu ref={containerRef} role="menu" className={classNames.actionList}>
           {renderButtonList(children)}
         </menu>
       </DropdownContent>
@@ -201,11 +201,12 @@ export const renderButtonList = (children: Actions) =>
     }
 
     return (
-      <li>
+      <li role="presentation">
         <DropdownCloser>
           {cloneElement(item as ReactElement, {
             variant: 'text',
             wide: true,
+            role: 'menuitem',
             className: actionListItemButton({ className: item.props.className }),
           })}
         </DropdownCloser>
diff --git a/packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/DropdownMenuGroup.tsx b/packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/DropdownMenuGroup.tsx
index 896ecbeaeb..bec2b57d12 100644
--- a/packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/DropdownMenuGroup.tsx
+++ b/packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/DropdownMenuGroup.tsx
@@ -4,6 +4,7 @@ import {
   type PropsWithChildren,
   type ReactNode,
   memo,
+  useId,
   useMemo,
 } from 'react'
 import { tv } from 'tailwind-variants'
@@ -12,10 +13,11 @@ import { Text } from '../../Text'
 
 import { renderButtonList } from './DropdownMenuButton'
 
-type Props = PropsWithChildren<{
+type AbstractProps = PropsWithChildren<{
   name?: ReactNode
 }>
-type ElementProps = Omit<ComponentProps<'li'>, keyof Props>
+type ElementProps = Omit<ComponentProps<'li'>, keyof AbstractProps>
+type Props = AbstractProps & ElementProps
 
 const classNameGenerator = tv({
   slots: {
@@ -35,32 +37,60 @@ const classNameGenerator = tv({
       ],
     ],
     groupName: 'shr-px-1 shr-py-0.5',
+    subMenu: 'shr-list-none',
   },
 })
 
-export const DropdownMenuGroup: FC<Props & ElementProps> = ({ name, children, className }) => {
+export const DropdownMenuGroup: FC<Props> = ({ name, children, className }) => {
+  const subMenuId = useId()
   const classNames = useMemo(() => {
-    const { group, groupName } = classNameGenerator()
+    const { group, groupName, subMenu } = classNameGenerator()
 
     return {
       group: group({ className }),
       groupName: groupName(),
+      subMenu: subMenu(),
     }
   }, [className])
 
+  const subMenu = (
+    <menu
+      role="group"
+      aria-labelledby={name ? subMenuId : undefined}
+      className={classNames.subMenu}
+    >
+      {renderButtonList(children)}
+    </menu>
+  )
+
   return (
-    <li className={classNames.group}>
-      <NameText className={classNames.groupName}>{name}</NameText>
-      <ul>{renderButtonList(children)}</ul>
+    <li role="presentation" className={classNames.group}>
+      {name ? (
+        <>
+          <NameText id={subMenuId} className={classNames.groupName}>
+            {name}
+          </NameText>
+          {subMenu}
+        </>
+      ) : (
+        subMenu
+      )}
     </li>
   )
 }
 
-const NameText = memo<PropsWithChildren<{ className: string }>>(
-  ({ children, className }) =>
-    children && (
-      <Text as="p" size="S" weight="bold" color="TEXT_GREY" leading="NONE" className={className}>
-        {children}
-      </Text>
-    ),
+const NameText = memo<PropsWithChildren<{ id: string; className: string }>>(
+  ({ id, children, className }) => (
+    <Text
+      size="S"
+      id={id}
+      weight="bold"
+      color="TEXT_GREY"
+      leading="NONE"
+      className={className}
+      as="div"
+    >
+      {children}
+    </Text>
+  ),
 )
diff --git a/packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/stories/DropdownMenuGroup.stories.tsx b/packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/stories/DropdownMenuGroup.stories.tsx
index ee8ec1181a..da82f57d4b 100644
--- a/packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/stories/DropdownMenuGroup.stories.tsx
+++ b/packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/stories/DropdownMenuGroup.stories.tsx
@@ -12,9 +12,9 @@ export default {
       <DropdownMenuGroup {...args} name="グループ1">
         <Button>操作1</Button>
         <Button>操作2</Button>
+        <Button>操作3</Button>
+        <Button>操作4</Button>
       </DropdownMenuGroup>
-      <Button>操作3</Button>
-      <Button>操作4</Button>
       <DropdownMenuGroup {...args} name="グループ2">
         <Button>操作5</Button>
         <Button>操作6</Button>
diff --git a/packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/stories/VRTDropdownMenuButton.stories.tsx b/packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/stories/VRTDropdownMenuButton.stories.tsx
index 60b0e2db14..7bade544e0 100644
--- a/packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/stories/VRTDropdownMenuButton.stories.tsx
+++ b/packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/stories/VRTDropdownMenuButton.stories.tsx
@@ -43,10 +43,10 @@ export default {
             <Button disabled disabledDetail={{ message: '非推奨な理由' }}>
               操作2
             </Button>
+            <RemoteDialogTrigger targetId="remote-dialog">
+              <Button>操作3</Button>
+            </RemoteDialogTrigger>
           </DropdownMenuGroup>
-          <RemoteDialogTrigger targetId="remote-dialog">
-            <Button>操作3</Button>
-          </RemoteDialogTrigger>
           <DropdownMenuGroup name="グループ2">
             <AnchorButton href="#">操作4</AnchorButton>
             <AnchorButton href="#">操作5</AnchorButton>


Reviews:
user: login: AtsushiM
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-04T00:00:42Z
user: login: uknmr
body: わからなくはないけど定義リストではないなぁ、と思います。
一度内部で認識をあわせたい。
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T09:09:08Z
user: login: AtsushiM
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-09T05:21:14Z
user: login: schktjm
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T05:58:47Z
user: login: schktjm
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T06:00:06Z
user: login: AtsushiM
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-19T06:18:23Z
user: login: AtsushiM
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-19T06:18:44Z
user: login: AtsushiM
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-19T06:20:06Z
user: login: AtsushiM
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-19T06:20:37Z
user: login: AtsushiM
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-19T06:22:46Z
user: login: schktjm
body: HTML構造とVOでチェックしてよさそうに感じました！
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T08:47:17Z
user: login: AtsushiM
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-20T23:11:15Z

Review comments:
diff_hunk: @@ -50,17 +51,22 @@ export const DropdownMenuGroup: FC<Props & ElementProps> = ({ name, children, cl
 
   return (
     <li className={classNames.group}>
-      <NameText className={classNames.groupName}>{name}</NameText>
-      <ul>{renderButtonList(children)}</ul>
+      {name ? (
+        <dl>
+          <NameDefinitionTerm className={classNames.groupName}>{name}</NameDefinitionTerm>
+          {renderButtonList(children, 'dd')}
+        </dl>
+      ) : (
+        <ul>{renderButtonList(children)}</ul>
+      )}
     </li>
   )
 }
 
-const NameText = memo<PropsWithChildren<{ className: string }>>(
-  ({ children, className }) =>
-    children && (
-      <Text as="p" size="S" weight="bold" color="TEXT_GREY" leading="NONE" className={className}>
-        {children}
-      </Text>
-    ),
+const NameDefinitionTerm = memo<PropsWithChildren<{ className: string }>>(
+  ({ children, className }) => (
+    <Text size="S" weight="bold" color="TEXT_GREY" leading="NONE" className={className} as="dt">
path: packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/DropdownMenuGroup.tsx
user: login: AtsushiM
body: `p` ではグループの名称なのか説明なのかわからないため、dl > dt, dd 構造にして関連付けを明確にしました。

```
// nameが存在する場合
<li>
  <dl>
    <dt>group 1</dt>
    <dd>item 1</dd>
    <dd>item 2</dd>
  </dl>
  ...
</li>
```

```
// nameが存在しない場合
<li>
  <ul>
    <li>item 1</li>
    <li>item 2</li>
    ...
  </ul>
</li>
```
created_at: 2025-04-04T00:00:42Z
author_association: MEMBER
diff_hunk: @@ -107,7 +107,7 @@ export const DropdownMenuButton: FC<Props & ElementProps> = ({
         buttonStyle={classNames.triggerButton}
       />
       <DropdownContent>
-        <menu ref={containerRef} className={classNames.actionList}>
+        <menu ref={containerRef} role="menu" className={classNames.actionList}>
path: packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/DropdownMenuButton.tsx
user: login: AtsushiM
body: menu要素ですが、現状のChromeだとroleがlistになってしまうため、role="menu"を指定しています。
created_at: 2025-04-09T05:18:48Z
author_association: MEMBER
diff_hunk: @@ -173,7 +173,7 @@ export const renderButtonList = (children: Actions) =>
     }
 
     return (
-      <li>
+      <li role="menuitem">
path: packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/DropdownMenuButton.tsx
user: login: AtsushiM
body: こちらも明示的にmenuitemになるよう修正しています
created_at: 2025-04-09T05:19:01Z
author_association: MEMBER
diff_hunk: @@ -35,32 +37,56 @@ const classNameGenerator = tv({
       ],
     ],
     groupName: 'shr-px-1 shr-py-0.5',
+    subMenu: 'shr-list-none',
   },
 })
 
-export const DropdownMenuGroup: FC<Props & ElementProps> = ({ name, children, className }) => {
+export const DropdownMenuGroup: FC<Props> = ({ name, children, className }) => {
+  const subMenuId = useId()
   const classNames = useMemo(() => {
-    const { group, groupName } = classNameGenerator()
+    const { group, groupName, subMenu } = classNameGenerator()
 
     return {
       group: group({ className }),
       groupName: groupName(),
+      subMenu: subMenu(),
     }
   }, [className])
 
+  const subMenu = (
+    <menu role="menu" className={classNames.subMenu} aria-labelledby={subMenuId}>
+      {renderButtonList(children)}
+    </menu>
+  )
+
   return (
-    <li className={classNames.group}>
-      <NameText className={classNames.groupName}>{name}</NameText>
-      <ul>{renderButtonList(children)}</ul>
+    <li role="presentation" className={classNames.group}>
path: packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/DropdownMenuGroup.tsx
user: login: AtsushiM
body: 意味づけの階層的に

```
menu > menuitem > menu
```

ではなく

```
menu > menu
```

になるよう、role="presentation"を指定しています。
created_at: 2025-04-09T05:19:51Z
author_association: MEMBER
diff_hunk: @@ -35,32 +37,56 @@ const classNameGenerator = tv({
       ],
     ],
     groupName: 'shr-px-1 shr-py-0.5',
+    subMenu: 'shr-list-none',
   },
 })
 
-export const DropdownMenuGroup: FC<Props & ElementProps> = ({ name, children, className }) => {
+export const DropdownMenuGroup: FC<Props> = ({ name, children, className }) => {
+  const subMenuId = useId()
   const classNames = useMemo(() => {
-    const { group, groupName } = classNameGenerator()
+    const { group, groupName, subMenu } = classNameGenerator()
 
     return {
       group: group({ className }),
       groupName: groupName(),
+      subMenu: subMenu(),
     }
   }, [className])
 
+  const subMenu = (
+    <menu role="menu" className={classNames.subMenu} aria-labelledby={subMenuId}>
+      {renderButtonList(children)}
+    </menu>
+  )
+
   return (
-    <li className={classNames.group}>
-      <NameText className={classNames.groupName}>{name}</NameText>
-      <ul>{renderButtonList(children)}</ul>
+    <li role="presentation" className={classNames.group}>
+      {name ? (
+        <>
+          <NameText id={subMenuId} className={classNames.groupName}>
+            {name}
+          </NameText>
+          {subMenu}
+        </>
+      ) : (
+        subMenu
+      )}
     </li>
   )
 }
 
-const NameText = memo<PropsWithChildren<{ className: string }>>(
-  ({ children, className }) =>
-    children && (
-      <Text as="p" size="S" weight="bold" color="TEXT_GREY" leading="NONE" className={className}>
-        {children}
-      </Text>
-    ),
+const NameText = memo<PropsWithChildren<{ id: string; className: string }>>(
+  ({ id, children, className }) => (
+    <Text
+      size="S"
+      id={id}
+      aria-hidden={true}
path: packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/DropdownMenuGroup.tsx
user: login: AtsushiM
body: このコンポーネント自体がSubMenuのタイトルとしての役割しかなく、SubMenuとはaria-labelledbyで紐づけられているため、aria-hiddenでスクリーンリーダーからは非表示にしています。
これにより、二重に読み上げられることがなくなります
created_at: 2025-04-09T05:21:09Z
author_association: MEMBER
diff_hunk: @@ -35,32 +37,57 @@ const classNameGenerator = tv({
       ],
     ],
     groupName: 'shr-px-1 shr-py-0.5',
+    subMenu: 'shr-list-none',
   },
 })
 
-export const DropdownMenuGroup: FC<Props & ElementProps> = ({ name, children, className }) => {
+export const DropdownMenuGroup: FC<Props> = ({ name, children, className }) => {
+  const subMenuId = useId()
   const classNames = useMemo(() => {
-    const { group, groupName } = classNameGenerator()
+    const { group, groupName, subMenu } = classNameGenerator()
 
     return {
       group: group({ className }),
       groupName: groupName(),
+      subMenu: subMenu(),
     }
   }, [className])
 
+  const subMenu = (
+    <menu role="menu" aria-labelledby={subMenuId} className={classNames.subMenu}>
+      {renderButtonList(children)}
+    </menu>
path: packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/DropdownMenuGroup.tsx
user: login: schktjm
body: 今回の形でいうと menuitem のまとまりなので `role="group"` のほうが良さそうです！

参考↓
https://w3c.github.io/aria/#menuitem
created_at: 2025-05-19T05:58:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -35,32 +37,57 @@ const classNameGenerator = tv({
       ],
     ],
     groupName: 'shr-px-1 shr-py-0.5',
+    subMenu: 'shr-list-none',
   },
 })
 
-export const DropdownMenuGroup: FC<Props & ElementProps> = ({ name, children, className }) => {
+export const DropdownMenuGroup: FC<Props> = ({ name, children, className }) => {
+  const subMenuId = useId()
   const classNames = useMemo(() => {
-    const { group, groupName } = classNameGenerator()
+    const { group, groupName, subMenu } = classNameGenerator()
 
     return {
       group: group({ className }),
       groupName: groupName(),
+      subMenu: subMenu(),
     }
   }, [className])
 
+  const subMenu = (
+    <menu role="menu" aria-labelledby={subMenuId} className={classNames.subMenu}>
+      {renderButtonList(children)}
+    </menu>
path: packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/DropdownMenuGroup.tsx
user: login: schktjm
body: 逆にどんな時にmenuになるのかというと、 menuitem をクリックしたら更に子どものメニューがポップアップで出てくるみたいなパターンは、サブメニューのwrapperは `role="menu"` で良さそうです！
created_at: 2025-05-19T06:00:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -201,11 +201,12 @@ export const renderButtonList = (children: Actions) =>
     }
 
     return (
-      <li role="menuitem">
+      <li role="presentation">
path: packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/DropdownMenuButton.tsx
user: login: AtsushiM
body: `menuitem` はfocus可能な要素設定することが想定されているようなので、buttonに設定するようにします。
menuはulのaliasのため、li要素自体はもとのまま残します
created_at: 2025-05-19T06:18:23Z
author_association: MEMBER
diff_hunk: @@ -201,11 +201,12 @@ export const renderButtonList = (children: Actions) =>
     }
 
     return (
-      <li role="menuitem">
+      <li role="presentation">
         <DropdownCloser>
           {cloneElement(item as ReactElement, {
             variant: 'text',
             wide: true,
+            role: 'menuitem',
path: packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/DropdownMenuButton.tsx
user: login: AtsushiM
body: https://github.com/kufu/smarthr-ui/pull/5522/commits/ba1190ea5ebd333a8829d66f13304141104c0987#r2094915876 のため、button自体にrole="menuitem"を設定します
created_at: 2025-05-19T06:18:44Z
author_association: MEMBER
diff_hunk: @@ -54,7 +54,11 @@ export const DropdownMenuGroup: FC<Props> = ({ name, children, className }) => {
   }, [className])
 
   const subMenu = (
-    <menu role="menu" aria-labelledby={subMenuId} className={classNames.subMenu}>
+    <menu
+      role="group"
path: packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/DropdownMenuGroup.tsx
user: login: AtsushiM
body: 名前の通り、DropdownMenuGroupはサブメニューというよりグルーピングのため、role="group"にしています
(例えばサブメニューを開くトリガーがあるようなUIの場合はrole="menu"が最適になる)
created_at: 2025-05-19T06:20:06Z
author_association: MEMBER
diff_hunk: @@ -54,7 +54,11 @@ export const DropdownMenuGroup: FC<Props> = ({ name, children, className }) => {
   }, [className])
 
   const subMenu = (
-    <menu role="menu" aria-labelledby={subMenuId} className={classNames.subMenu}>
+    <menu
+      role="group"
+      aria-labelledby={name ? subMenuId : undefined}
path: packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/DropdownMenuGroup.tsx
user: login: AtsushiM
body: 対象となるメニュー名の要素が存在しない場合、aria-labelledbyが無い方が適切のため、条件分岐しています
created_at: 2025-05-19T06:20:37Z
author_association: MEMBER
diff_hunk: @@ -80,7 +84,6 @@ const NameText = memo<PropsWithChildren<{ id: string; className: string }>>(
     <Text
       size="S"
       id={id}
-      aria-hidden={true}
path: packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/DropdownMenuGroup.tsx
user: login: AtsushiM
body: fieldset > legend 構成などでも、fieldsetとlegendでそれぞれa11y nameが読み上げられることは一般的にある挙動のため、明確にaria-hiddenは設定しません。
また、aria-hiddenを設定していると、ラベルとなる要素自体がコード上のどこにあるかが不明確になってしまうため、aria-hiddenの設定のメリットがなく、デメリットしか無い状態でした。
created_at: 2025-05-19T06:22:46Z
author_association: MEMBER
path: packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/DropdownMenuGroup.tsx
user: login: schktjm
body: [imo] このファイル関係ないです！

個人的な意見になるんですが、できれば Story のこの操作3, 4はグループ1にいれるか消したいなという気持ちです！

何故かというと、視覚的にグループ1に入っているのにHTML的には入っていない状態が良くないパターンだなと思っているからです！パターン的に表示しているのはわかるんですが、Storybook にあるから許されてると思われるのは嫌だなと考えてます！

![Storybook: DropdownMenuGroupのグループ1を選択している状態のキャプチャ](https://github.com/user-attachments/assets/5e02e9fa-570d-405e-afbb-5e355129efcd)

created_at: 2025-05-19T08:43:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -12,9 +12,9 @@ export default {
       <DropdownMenuGroup {...args} name="グループ1">
         <Button>操作1</Button>
         <Button>操作2</Button>
+        <Button>操作3</Button>
+        <Button>操作4</Button>
       </DropdownMenuGroup>
-      <Button>操作3</Button>
-      <Button>操作4</Button>
path: packages/smarthr-ui/src/components/Dropdown/DropdownMenuButton/stories/DropdownMenuGroup.stories.tsx
user: login: AtsushiM
body: このパターンは問題なく使えはしますが、実際には意図していない利用方法になります
（DropdownMenuの中でグループを使うなら全ていずれかのグループに属してほしい）

storybookにあることで `許容されているパターンなのだな` と考えられることを避けるため、調整しています
created_at: 2025-05-20T23:11:15Z
author_association: MEMBER

PR comments:
user: login: pkg-pr-new[bot]
created_at: 2025-04-04T00:04:10Z
body: 
[Open in StackBlitz](https://pkg.pr.new/template/9f407b03-0095-4bf1-a403-21514e97655a)


```
npm i https://pkg.pr.new/kufu/smarthr-ui@5522
```


_commit: <a href="https://github.com/kufu/smarthr-ui/runs/42593314673"><code>a03d1a2</code></a>_

author_association: NONE
user: login: AtsushiM
created_at: 2025-04-06T23:00:34Z
body: > わからなくはないけど定義リストではないなぁ、と思います。

https://developer.mozilla.org/ja/docs/Web/HTML/Element/dl

以前の定義リストの考え方ならそうかも知れませんが、現在は説明リスト、という概念になっているはずなので dt に対して dd で説明を行っていれば基本的に内容の制約はないはずです。
厳密に、定義リストとして使うことになるとDefinitionListコンポーネントは(少なくとも弊社サービスにおいて)ほとんどの場合で使えなくなってしまうはずです。
例えばSmartHR内のサービスにおいて、従業員情報などはdl>dt,dd構造になっている場合が多いですが、従業員情報の場合と今回の違いは何でしょうか？


author_association: MEMBER
user: login: uknmr
created_at: 2025-04-07T04:53:30Z
body: DefinitionList は用語とその説明というかキー（名前）と値のセットなので dl で違和感ないですが、DropdownMenuGroup はグルーピングした結果まとめたものに名前をつけるのでキーと値のセットのような関係性とは異なります。
また以下のような HTML で十分に情報としては伝わるように感じます。

```html
<nav>
  <h2>hoge</h2>
  <ul>
    <li>hoho</li>
    <li>
      <h3>fuga</h3>
      <ul>
        <li>puyo</li>
        <li>jojo</li>
      </ul>
    </li>
  </ul>
</nav>
```
author_association: CONTRIBUTOR
user: login: AtsushiM
created_at: 2025-04-08T23:11:18Z
body: > DefinitionList は用語とその説明というかキー（名前）と値のセットなので dl で違和感ないですが、DropdownMenuGroup はグルーピングした結果まとめたものに名前をつけるのでキーと値のセットのような関係性とは異なります。

うーんなるほど...
uknmrさん案のnavも検討していてそちらでも良いかなぁと考えていたんですが、DropdownMenu自体はナビゲーションというよりは操作 (CRUDなど) を含める場合が多く、その場合ははたしてナビゲーションと言えるのか？あたりが気になりdlを採用してみた... という思考の流れでした。

個人的にはnav>heading案、dl>dt案のどちらかが採用されれば、意味づけとしては十分になると思うので、開発者内で議論してみますか？

あとnav案の場合、navに対して名称を設定したほうが良いかも。
(Pagination、SideNavなどと併用された場合、それらと区別できるように設定できるとベターなはず)
uknmrさんの場合は `hoge` の部分が VisuallyHideenになるイメージであっていますか？
author_association: MEMBER
user: login: AtsushiM
created_at: 2025-04-10T02:57:37Z
body: https://github.com/kufu/smarthr-ui/pull/5542 の merge待ち
author_association: MEMBER
user: login: AtsushiM
created_at: 2025-05-09T00:18:54Z
body: 改善しているか、チェックを依頼
https://kufuinc.slack.com/archives/C06L25PMFMG/p1746749877220149
author_association: MEMBER
user: login: AtsushiM
created_at: 2025-05-19T06:27:25Z
body: 📝 menuにaria-labell (aria-labelledby) を設定するメリット

- メニューだけにfocusを当てるようなジャンプ機能を使う場合などにメリットになる
- 基本的な動作ではテキスト読み上げ順により、ラベルとしての紐づけが行われていなくても致命的な問題にはなりづらい

上記から

- コード的に発見されたら修正自体はした方が良い
  - 明確にメリットは有る
- が、積極的に修正を促すほどではないため、他の修正のついでに対応していくくらいの温度感で問題はなさそう

という状態
author_association: MEMBER