diff --git a/.changeset/ui-menu-item-style-refactor.md b/.changeset/ui-menu-item-style-refactor.md new file mode 100644 index 0000000000..7ff7b28c9e --- /dev/null +++ b/.changeset/ui-menu-item-style-refactor.md @@ -0,0 +1,7 @@ +--- +'@backstage/ui': patch +--- + +Simplified the `Menu` component's item structure by removing the inner wrapper element and applying styles directly to the menu item, improving DOM clarity. + +**Affected components:** Menu diff --git a/packages/ui/src/components/Menu/Menu.module.css b/packages/ui/src/components/Menu/Menu.module.css index 9acedd106e..02446eea5b 100644 --- a/packages/ui/src/components/Menu/Menu.module.css +++ b/packages/ui/src/components/Menu/Menu.module.css @@ -69,51 +69,11 @@ box-sizing: border-box; overflow: auto; min-width: 150px; - box-sizing: border-box; outline: none; padding-block: var(--bui-space-1); } .bui-MenuItem { - padding-inline: var(--bui-space-1); - display: block; - - &[data-focus-visible] { - outline: none; - } - - &[data-focus-visible] { - outline: 2px solid var(--bui-ring); - outline-offset: -2px; - } - - &[data-focused] .bui-MenuItemWrapper { - background: var(--bui-bg-neutral-2); - color: var(--bui-fg-primary); - } - - &[data-open] .bui-MenuItemWrapper { - background: var(--bui-bg-neutral-2); - color: var(--bui-fg-primary); - } - - &[data-color='danger'] .bui-MenuItemWrapper { - color: var(--bui-fg-danger); - } - - &[data-color='danger'][data-focused] .bui-MenuItemWrapper { - background: var(--bui-bg-danger); - color: var(--bui-fg-danger); - } - - &[data-has-submenu] { - & > .bui-MenuItemWrapper > .bui-MenuItemArrow { - display: block; - } - } - } - - .bui-MenuItemWrapper { display: flex; align-items: center; justify-content: space-between; @@ -121,17 +81,61 @@ padding-inline: var(--bui-space-2); border-radius: var(--bui-radius-2); outline: none; - cursor: default; color: var(--bui-fg-primary); font-size: var(--bui-font-size-3); gap: var(--bui-space-6); + cursor: pointer; + margin-inline: var(--bui-space-1); + + &[data-focus-visible] { + outline: 2px solid var(--bui-ring); + outline-offset: -2px; + } + + &[data-focused] { + background: var(--bui-bg-neutral-2); + color: var(--bui-fg-primary); + } + + &[data-open] { + background: var(--bui-bg-neutral-2); + color: var(--bui-fg-primary); + } + + &[data-color='danger'] { + color: var(--bui-fg-danger); + } + + &[data-color='danger'][data-focused] { + background: var(--bui-bg-danger); + color: var(--bui-fg-danger); + } + + &[data-has-submenu] { + & > .bui-MenuItemArrow { + display: block; + } + } } .bui-MenuItemListBox { - padding-inline: var(--bui-space-1); - display: block; + display: flex; + align-items: center; + height: 2rem; + padding-inline: var(--bui-space-2); + border-radius: var(--bui-radius-2); + outline: none; + color: var(--bui-fg-primary); + font-size: var(--bui-font-size-3); + cursor: pointer; - &:hover .bui-MenuItemWrapper { + &[data-focus-visible] { + outline: 2px solid var(--bui-ring); + outline-offset: -2px; + } + + &[data-hovered], + &[data-focused] { background: var(--bui-bg-neutral-2); color: var(--bui-fg-primary); } diff --git a/packages/ui/src/components/Menu/Menu.tsx b/packages/ui/src/components/Menu/Menu.tsx index cb6028c45a..8008a071bb 100644 --- a/packages/ui/src/components/Menu/Menu.tsx +++ b/packages/ui/src/components/Menu/Menu.tsx @@ -308,63 +308,34 @@ export const MenuItem = (props: MenuItemProps) => { ); const { classes, iconStart, children, href } = ownProps; - const handleAction = () => { - if (href) { - const text = - restProps['aria-label'] ?? getNodeText(children) ?? String(href); - analytics.captureEvent('click', text, { - attributes: { to: String(href) }, - }); - } - }; - - // External links open in new tab via window.open instead of client-side routing - if (href && !isInternalLink(href)) { - return ( - { - restProps.onAction?.(); - handleAction(); - window.open(href, '_blank', 'noopener,noreferrer'); - }} - > -
-
- {iconStart} - {children} -
-
- -
-
-
- ); - } + const isExternal = href && !isInternalLink(href); return ( { restProps.onAction?.(); - handleAction(); + if (href) { + const text = + restProps['aria-label'] ?? getNodeText(children) ?? String(href); + analytics.captureEvent('click', text, { + attributes: { to: String(href) }, + }); + } }} > -
-
- {iconStart} - {children} -
-
- -
+
+ {iconStart} + {children} +
+
+
); @@ -384,13 +355,11 @@ export const MenuListBoxItem = (props: MenuListBoxItemProps) => { className={classes.root} {...restProps} > -
-
-
- -
- {children} +
+
+
+ {children}
); diff --git a/packages/ui/src/components/Menu/definition.ts b/packages/ui/src/components/Menu/definition.ts index 076e9ce3ae..63c46a24a3 100644 --- a/packages/ui/src/components/Menu/definition.ts +++ b/packages/ui/src/components/Menu/definition.ts @@ -100,7 +100,6 @@ export const MenuItemDefinition = defineComponent()({ styles, classNames: { root: 'bui-MenuItem', - itemWrapper: 'bui-MenuItemWrapper', itemContent: 'bui-MenuItemContent', itemArrow: 'bui-MenuItemArrow', }, @@ -121,7 +120,6 @@ export const MenuListBoxItemDefinition = styles, classNames: { root: 'bui-MenuItemListBox', - itemWrapper: 'bui-MenuItemWrapper', itemContent: 'bui-MenuItemContent', check: 'bui-MenuItemListBoxCheck', },