Repository: PostHog/posthog
Pull request #31768: fix(devex): removed href from ButtonPrimitive
Author: login: adamleithp
Created: 2025-04-30T13:35:37Z | Merged: 2025-04-30T17:57:10Z

## Problem
Some ButtonPrimitive links (with href/to) were not functioning properly... like on prod right now, the Project tree `<ProjectDropownMenu>`

## Why add MORE functionality to `<Link>`?
Button Primitive, our new Button architecture used in Project tree was trying to do too much by re-doing Linking.
LemonLink (or Link) does a LOT of magic stuff...
These two things should be separate. A link a link, a button is a button.

Need a link, but need our new button styles?

```
<Link
    buttonProps={{
        active: true,
        iconOnly: true,
        tooltip: `View settings for project: ${currentTeam.name}`,
        tooltipPlacement: 'right',
    }}
    to={urls.project(currentTeam.id, urls.settings('project'))}
>
    <IconGear className="text-tertiary" />
</Link>
```

### Problem example:
![2025-04-30 14 33 33](https://github.com/user-attachments/assets/d5f69017-b516-4a7e-9f4d-5c48e8860b24)

Fixed:
![2025-04-30 14 32 16](https://github.com/user-attachments/assets/ba6b6753-2f8d-4024-aa7b-f5b2d54ec4c3)



## Changes
* Replace all instances of `<ButtonPrimitive>` being used a `<a>`, and use `<Link>` instead
* Add `buttonProps` to `<Link>`, enabling us to use the new styles on the fly
* Add `tooltip` and other tooltip props, `role`, and `tabindex` to `<Link>` component
* Reworked `<ButtonPrimitive>` and moved lots of tailwind over to the CSS file... the HTML was getting insane.
* Finally, while I'm there, add a `empty folder` icon `<IconCircleDashed>`

## How did you test this code?
Clicking loads of stuff, making sure it all worked, including tooltips, tooltip doc links, trying out the LemonTree etc.



Diff:
diff --git a/frontend/__snapshots__/lemon-ui-lemon-tree--default--dark.png b/frontend/__snapshots__/lemon-ui-lemon-tree--default--dark.png
index ba349a194018..c262f96ec86d 100644
Binary files a/frontend/__snapshots__/lemon-ui-lemon-tree--default--dark.png and b/frontend/__snapshots__/lemon-ui-lemon-tree--default--dark.png differ
diff --git a/frontend/__snapshots__/lemon-ui-lemon-tree--default--light.png b/frontend/__snapshots__/lemon-ui-lemon-tree--default--light.png
index 49d60efc1574..0f9287923b3b 100644
Binary files a/frontend/__snapshots__/lemon-ui-lemon-tree--default--light.png and b/frontend/__snapshots__/lemon-ui-lemon-tree--default--light.png differ
diff --git a/frontend/__snapshots__/ui-buttonprimitive--default--dark.png b/frontend/__snapshots__/ui-buttonprimitive--default--dark.png
index 53904675b800..8e66576fdb34 100644
Binary files a/frontend/__snapshots__/ui-buttonprimitive--default--dark.png and b/frontend/__snapshots__/ui-buttonprimitive--default--dark.png differ
diff --git a/frontend/__snapshots__/ui-buttonprimitive--default--light.png b/frontend/__snapshots__/ui-buttonprimitive--default--light.png
index bf492a55f160..009085d2c332 100644
Binary files a/frontend/__snapshots__/ui-buttonprimitive--default--light.png and b/frontend/__snapshots__/ui-buttonprimitive--default--light.png differ
diff --git a/frontend/src/layout/panel-layout/PanelLayoutNavBar.tsx b/frontend/src/layout/panel-layout/PanelLayoutNavBar.tsx
index 1b2871685a3e..2b1bff5067e8 100644
--- a/frontend/src/layout/panel-layout/PanelLayoutNavBar.tsx
+++ b/frontend/src/layout/panel-layout/PanelLayoutNavBar.tsx
@@ -11,6 +11,7 @@ import {
     IconSearch,
     IconToolbar,
 } from '@posthog/icons'
+import { Link } from '@posthog/lemon-ui'
 import { cva } from 'cva'
 import { useActions, useValues } from 'kea'
 import { router } from 'kea-router'
@@ -285,14 +286,16 @@ export function PanelLayoutNavBar({ children }: { children: React.ReactNode }):
                                                 }
                                             }}
                                         >
-                                            <ButtonPrimitive
-                                                menuItem={!isLayoutNavCollapsed}
-                                                active={item.id === 'Project' && isLayoutPanelVisible}
+                                            <Link
                                                 data-attr={`menu-item-${item.identifier.toString().toLowerCase()}`}
-                                                className="group"
-                                                href={item.to}
-                                                iconOnly={isLayoutNavCollapsed}
-                                                tooltip={isLayoutNavCollapsed ? item.tooltip : undefined}
+                                                buttonProps={{
+                                                    menuItem: !isLayoutNavCollapsed,
+                                                    active: item.id === 'Project' && isLayoutPanelVisible,
+                                                    className: 'group',
+                                                    iconOnly: isLayoutNavCollapsed,
+                                                }}
+                                                to={item.to}
+                                                tooltip={item.tooltip}
                                                 tooltipPlacement="right"
                                                 tooltipDocLink={item.tooltipDocLink}
                                             >
@@ -314,7 +317,7 @@ export function PanelLayoutNavBar({ children }: { children: React.ReactNode }):
                                                         )}
                                                     </>
                                                 )}
-                                            </ButtonPrimitive>
+                                            </Link>
                                         </ListBox.Item>
                                     ))}
                                 </div>
@@ -341,11 +344,7 @@ export function PanelLayoutNavBar({ children }: { children: React.ReactNode }):
                                                         item.featureFlag && !featureFlags[item.featureFlag]
 
                                                     return notEnabled ? null : (
-                                                        <ButtonGroupPrimitive
-                                                            menuItem
-                                                            fullWidth
-                                                            groupVariant="side-action-group"
-                                                        >
+                                                        <ButtonGroupPrimitive menuItem fullWidth>
                                                             <ListBox.Item
                                                                 asChild
                                                                 key={item.identifier}
@@ -364,19 +363,21 @@ export function PanelLayoutNavBar({ children }: { children: React.ReactNode }):
                                                                     }
                                                                 }}
                                                             >
-                                                                <ButtonPrimitive
-                                                                    menuItem={!isLayoutNavCollapsed}
-                                                                    href={'to' in item ? item.to : undefined}
+                                                                <Link
                                                                     data-attr={`menu-item-${item.identifier
                                                                         .toString()
                                                                         .toLowerCase()}`}
-                                                                    className="group data-[focused=true]:bg-fill-button-tertiary-hover"
-                                                                    sideActionLeft={
-                                                                        item.sideAction && !isLayoutNavCollapsed
-                                                                            ? true
-                                                                            : false
-                                                                    }
-                                                                    iconOnly={isLayoutNavCollapsed}
+                                                                    buttonProps={{
+                                                                        menuItem: !isLayoutNavCollapsed,
+                                                                        className:
+                                                                            'group data-[focused=true]:bg-fill-button-tertiary-hover',
+                                                                        hasSideActionRight:
+                                                                            item.sideAction && !isLayoutNavCollapsed
+                                                                                ? true
+                                                                                : false,
+                                                                        iconOnly: isLayoutNavCollapsed,
+                                                                    }}
+                                                                    to={'to' in item ? item.to : undefined}
                                                                     tooltip={
                                                                         isLayoutNavCollapsed ? item.label : undefined
                                                                     }
@@ -414,7 +415,7 @@ export function PanelLayoutNavBar({ children }: { children: React.ReactNode }):
                                                                             )}
                                                                         </>
                                                                     )}
-                                                                </ButtonPrimitive>
+                                                                </Link>
                                                             </ListBox.Item>
 
                                                             {!isLayoutNavCollapsed &&
@@ -438,13 +439,16 @@ export function PanelLayoutNavBar({ children }: { children: React.ReactNode }):
                                                                             }
                                                                         }}
                                                                     >
-                                                                        <ButtonPrimitive
-                                                                            sideActionRight
+                                                                        <Link
+                                                                            buttonProps={{
+                                                                                isSideActionRight: true,
+                                                                            }}
                                                                             tooltip={item.sideAction.tooltip}
-                                                                            href={urls.insightNew()}
+                                                                            tooltipPlacement="right"
+                                                                            to={urls.insightNew()}
                                                                         >
                                                                             {item.sideAction.icon}
-                                                                        </ButtonPrimitive>
+                                                                        </Link>
                                                                     </ListBox.Item>
                                                                 )}
 
@@ -473,7 +477,7 @@ export function PanelLayoutNavBar({ children }: { children: React.ReactNode }):
                                                                             }}
                                                                         >
                                                                             <ButtonPrimitive
-                                                                                sideActionRight
+                                                                                isSideActionRight
                                                                                 active={
                                                                                     visibleSideAction ===
                                                                                     item.identifier
@@ -532,18 +536,19 @@ export function PanelLayoutNavBar({ children }: { children: React.ReactNode }):
                                     {!isLayoutNavCollapsed && 'Quick start'}
                                 </ButtonPrimitive>
                             )}
-                            <ButtonPrimitive
-                                menuItem={!isLayoutNavCollapsed}
+                            <Link
+                                buttonProps={{
+                                    menuItem: !isLayoutNavCollapsed,
+                                    className: 'group',
+                                    iconOnly: isLayoutNavCollapsed,
+                                }}
+                                to={urls.toolbarLaunch()}
                                 onClick={() => {
                                     handleStaticNavbarItemClick(urls.toolbarLaunch(), true)
                                 }}
-                                href={urls.toolbarLaunch()}
-                                data-attr={Scene.ToolbarLaunch}
                                 tooltip={isLayoutNavCollapsed ? 'Toolbar' : undefined}
-                                tooltipPlacement="right"
-                                className="group"
-                                iconOnly={isLayoutNavCollapsed}
                                 tooltipDocLink="https://posthog.com/docs/toolbar"
+                                tooltipPlacement="right"
                             >
                                 <span
                                     className={`flex text-tertiary group-hover:text-primary ${
@@ -553,19 +558,21 @@ export function PanelLayoutNavBar({ children }: { children: React.ReactNode }):
                                     <IconToolbar />
                                 </span>
                                 {!isLayoutNavCollapsed && 'Toolbar'}
-                            </ButtonPrimitive>
+                            </Link>
 
-                            <ButtonPrimitive
-                                menuItem={!isLayoutNavCollapsed}
+                            <Link
+                                buttonProps={{
+                                    menuItem: !isLayoutNavCollapsed,
+                                    className: 'group',
+                                    iconOnly: isLayoutNavCollapsed,
+                                }}
+                                to={urls.settings('project')}
+                                data-attr={Scene.Settings}
                                 onClick={() => {
                                     handleStaticNavbarItemClick(urls.settings('project'), true)
                                 }}
-                                href={urls.settings('project')}
-                                data-attr={Scene.Settings}
                                 tooltip={isLayoutNavCollapsed ? 'Settings' : undefined}
                                 tooltipPlacement="right"
-                                className="group"
-                                iconOnly={isLayoutNavCollapsed}
                             >
                                 <span
                                     className={`flex text-tertiary group-hover:text-primary ${
@@ -575,7 +582,7 @@ export function PanelLayoutNavBar({ children }: { children: React.ReactNode }):
                                     <IconGear />
                                 </span>
                                 {!isLayoutNavCollapsed && 'Settings'}
-                            </ButtonPrimitive>
+                            </Link>
 
                             <Popover
                                 overlay={<AccountPopoverOverlay />}
diff --git a/frontend/src/layout/panel-layout/ProjectDropdownMenu.tsx b/frontend/src/layout/panel-layout/ProjectDropdownMenu.tsx
index fdb08750b38c..d865611ee268 100644
--- a/frontend/src/layout/panel-layout/ProjectDropdownMenu.tsx
+++ b/frontend/src/layout/panel-layout/ProjectDropdownMenu.tsx
@@ -1,5 +1,5 @@
 import { IconChevronRight, IconFolderOpen, IconGear, IconPlusSmall } from '@posthog/icons'
-import { LemonSnack } from '@posthog/lemon-ui'
+import { LemonSnack, Link } from '@posthog/lemon-ui'
 import { useActions, useValues } from 'kea'
 import { router } from 'kea-router'
 import { upgradeModalLogic } from 'lib/components/UpgradeModal/upgradeModalLogic'
@@ -41,27 +41,31 @@ function OtherProjectButton({ team }: { team: TeamBasicType }): JSX.Element {
     }, [location.pathname, team.id, team.project_id, currentTeam?.project_id])
 
     return (
-        <ButtonGroupPrimitive menuItem fullWidth groupVariant="side-action-group">
+        <ButtonGroupPrimitive menuItem fullWidth>
             <DropdownMenuItem asChild>
-                <ButtonPrimitive
-                    menuItem
-                    href={relativeOtherProjectPath}
-                    sideActionLeft
+                <Link
+                    buttonProps={{
+                        menuItem: true,
+                        hasSideActionRight: true,
+                    }}
                     tooltip={`Switch to project: ${team.name}`}
                     tooltipPlacement="right"
+                    to={relativeOtherProjectPath}
                 >
                     <ProjectName team={team} />
-                </ButtonPrimitive>
+                </Link>
             </DropdownMenuItem>
-            <ButtonPrimitive
-                href={urls.project(team.id, urls.settings('project'))}
-                iconOnly
-                sideActionRight
+            <Link
+                buttonProps={{
+                    iconOnly: true,
+                    isSideActionRight: true,
+                }}
                 tooltip={`View settings for project: ${team.name}`}
                 tooltipPlacement="right"
+                to={urls.project(team.id, urls.settings('project'))}
             >
                 <IconGear />
-            </ButtonPrimitive>
+            </Link>
         </ButtonGroupPrimitive>
     )
 }
@@ -105,27 +109,29 @@ export function ProjectDropdownMenu(): JSX.Element | null {
                 <DropdownMenuLabel>Projects</DropdownMenuLabel>
                 <DropdownMenuSeparator />
                 <DropdownMenuItem asChild>
-                    <ButtonGroupPrimitive fullWidth groupVariant="side-action-group">
+                    <ButtonGroupPrimitive fullWidth>
                         <ButtonPrimitive
                             menuItem
                             active
                             disabled
-                            sideActionLeft
+                            hasSideActionRight
                             tooltip={`Current project: ${currentTeam.name}`}
                             tooltipPlacement="right"
                         >
                             <ProjectName team={currentTeam} />
                         </ButtonPrimitive>
-                        <ButtonPrimitive
-                            active
-                            href={urls.project(currentTeam.id, urls.settings('project'))}
-                            iconOnly
-                            sideActionRight
+                        <Link
+                            buttonProps={{
+                                active: true,
+                                iconOnly: true,
+                                isSideActionRight: true,
+                            }}
                             tooltip={`View settings for project: ${currentTeam.name}`}
                             tooltipPlacement="right"
+                            to={urls.project(currentTeam.id, urls.settings('project'))}
                         >
                             <IconGear className="text-tertiary" />
-                        </ButtonPrimitive>
+                        </Link>
                     </ButtonGroupPrimitive>
                 </DropdownMenuItem>
 
diff --git a/frontend/src/lib/lemon-ui/LemonTree/LemonTree.tsx b/frontend/src/lib/lemon-ui/LemonTree/LemonTree.tsx
index 532c967de68f..c72cce4f6b35 100644
--- a/frontend/src/lib/lemon-ui/LemonTree/LemonTree.tsx
+++ b/frontend/src/lib/lemon-ui/LemonTree/LemonTree.tsx
@@ -19,6 +19,7 @@ import {
 
 import { ContextMenu, ContextMenuContent, ContextMenuTrigger } from '../../ui/ContextMenu/ContextMenu'
 import { SideAction } from '../LemonButton'
+import { Link } from '../Link/Link'
 import { Spinner } from '../Spinner/Spinner'
 import { Tooltip } from '../Tooltip/Tooltip'
 import {
@@ -313,6 +314,193 @@ const LemonTreeNode = forwardRef<HTMLDivElement, LemonTreeNodeProps>(
                             )
                         }
 
+                        let button = (
+                            <Link
+                                data-id={item.id}
+                                // When dragging, don't allow links to be clicked,
+                                // without this drag end would fire this href causing a reload
+                                to={isEmptyFolder ? '#' : item.record?.href || '#'}
+                                onClick={() => {
+                                    handleClick(item)
+                                }}
+                                disabled={isDragging}
+                                role="treeitem"
+                                buttonProps={{
+                                    active: getItemActiveState(item),
+                                    menuItem: true,
+                                    hasSideActionRight: true,
+                                    disabled: isEmptyFolder,
+                                    className: cn(
+                                        'group/lemon-tree-button',
+                                        'relative z-1 focus-visible:bg-fill-button-tertiary-hover h-[var(--button-height-base)] motion-safe:transition-[padding] duration-50',
+                                        {
+                                            'bg-fill-button-tertiary-hover':
+                                                (folderSelectMode && selectedId === item.id && !isEmptyFolder) ||
+                                                isContextMenuOpenForItem === item.id,
+                                            'bg-fill-button-tertiary-active': getItemActiveState(item),
+                                            'group-hover/lemon-tree-button-group:bg-fill-button-tertiary-hover cursor-pointer':
+                                                !isEmptyFolder,
+                                            'hover:bg-transparent opacity-50 cursor-default':
+                                                (folderSelectMode && !isFolder) || isEmptyFolder,
+                                        }
+                                    ),
+                                }}
+                                tabIndex={isEmptyFolder ? -1 : 0}
+                                aria-level={depth + 1}
+                                aria-setsize={data.length} // TODO: somehow get all loaded items length here in children
+                                aria-posinset={index + 1}
+                                aria-selected={selectedId === item.id}
+                                aria-disabled={!!item.disabledReason}
+                                aria-haspopup={!!itemContextMenu?.(item)}
+                                aria-roledescription="tree item"
+                                aria-rolemap={`item-${item.id}`}
+                                aria-label={ariaLabel}
+                                tooltip={isDragging || isEmptyFolder || mode === 'table' ? undefined : displayName}
+                                tooltipPlacement="right"
+                            >
+                                {/* Spacer to offset button padding */}
+                                <span
+                                    className="h-full bg-transparent pointer-events-none flex-shrink-0 transition-[width] duration-50"
+                                    // eslint-disable-next-line react/forbid-dom-props
+                                    style={{
+                                        width:
+                                            enableMultiSelection && !item.disableSelect
+                                                ? `${emptySpaceOffset + 26}px`
+                                                : `${emptySpaceOffset}px`,
+                                    }}
+                                />
+
+                                {/* Render contents */}
+                                <span
+                                    className={cn('relative truncate', {
+                                        'w-full h-full h-[var(--button-height-base)]': mode === 'table',
+                                    })}
+                                >
+                                    <span
+                                        className={cn('truncate text-left', {
+                                            'absolute h-[var(--button-height-base)] flex items-center':
+                                                mode === 'table',
+                                            'w-full': mode === 'tree',
+                                        })}
+                                        // eslint-disable-next-line react/forbid-dom-props
+                                        style={{
+                                            width: `${
+                                                tableColumnSizeAndPosition && tableColumnSizeAndPosition?.[0]?.width
+                                                    ? tableColumnSizeAndPosition?.[0]?.width - emptySpaceOffset
+                                                    : 0
+                                            }px`,
+                                        }}
+                                    >
+                                        {renderItem ? (
+                                            <>
+                                                {renderItem(
+                                                    item,
+                                                    <span
+                                                        className={cn({
+                                                            'font-semibold': isFolder,
+                                                        })}
+                                                    >
+                                                        <Tooltip
+                                                            title={mode === 'table' ? displayName : undefined}
+                                                            placement="top-start"
+                                                            className="w-fit"
+                                                        >
+                                                            <span>{displayName}</span>
+                                                        </Tooltip>
+                                                    </span>
+                                                )}
+                                            </>
+                                        ) : (
+                                            <span
+                                                className={cn('truncate', {
+                                                    'font-semibold': isFolder && !isEmptyFolder,
+                                                })}
+                                                // eslint-disable-next-line react/forbid-dom-props
+                                                style={{
+                                                    paddingRight: mode === 'table' ? `3px` : undefined,
+                                                }}
+                                            >
+                                                <Tooltip
+                                                    title={mode === 'table' ? displayName : undefined}
+                                                    placement="top-start"
+                                                    className="w-fit"
+                                                >
+                                                    <span>{displayName}</span>
+                                                </Tooltip>
+                                            </span>
+                                        )}
+
+                                        {/* Loading state */}
+                                        {item.record?.loading && <Spinner className="ml-1" />}
+
+                                        {/* Unapplied state */}
+                                        {item.record?.unapplied && <IconUpload className="ml-1 text-warning" />}
+                                    </span>
+
+                                    {mode === 'table' &&
+                                        tableViewKeys?.headers.slice(1).map((header, index) => {
+                                            const value = header.key
+                                                .split('.')
+                                                .reduce((obj, key) => (obj as any)?.[key], item)
+
+                                            return (
+                                                <span
+                                                    key={header.key}
+                                                    className="absolute truncate text-left flex items-center h-[var(--button-height-base)]"
+                                                    // eslint-disable-next-line react/forbid-dom-props
+                                                    style={{
+                                                        left: `${
+                                                            tableColumnSizeAndPosition &&
+                                                            tableColumnSizeAndPosition?.[index + 1]?.left
+                                                                ? tableColumnSizeAndPosition?.[index + 1]?.left -
+                                                                  10 -
+                                                                  emptySpaceOffset
+                                                                : 0
+                                                        }px`,
+                                                        width: `${tableColumnSizeAndPosition?.[index + 1]?.width}px`,
+                                                    }}
+                                                >
+                                                    <Tooltip
+                                                        title={
+                                                            typeof header.tooltip === 'function'
+                                                                ? header.tooltip(value)
+                                                                : header.tooltip
+                                                        }
+                                                        placement="top-start"
+                                                    >
+                                                        <span
+                                                            className={cn(
+                                                                'starting:opacity-0 opacity-100 delay-50 motion-safe:transition-opacity duration-100 font-normal',
+                                                                {
+                                                                    'font-normal': index > 1,
+                                                                    'opacity-0': index !== 1 && isEmptyFolder,
+                                                                }
+                                                            )}
+                                                        >
+                                                            {header.formatFunction
+                                                                ? header.formatFunction(value)
+                                                                : value}
+                                                        </span>
+                                                    </Tooltip>
+                                                </span>
+                                            )
+                                        })}
+                                </span>
+                            </Link>
+                        )
+
+                        if (enableDragAndDrop && isItemDraggable?.(item) && item.id) {
+                            button = (
+                                <TreeNodeDraggable
+                                    id={item.id}
+                                    enableDragging
+                                    className="h-[var(--button-height-base)]"
+                                >
+                                    {button}
+                                </TreeNodeDraggable>
+                            )
+                        }
+
                         const content = (
                             <AccordionPrimitive.Root
                                 type="multiple"
@@ -344,23 +532,21 @@ const LemonTreeNode = forwardRef<HTMLDivElement, LemonTreeNodeProps>(
                                                 <ButtonGroupPrimitive
                                                     fullWidth
                                                     className="group/lemon-tree-button-group relative h-[var(--button-height-base)]"
-                                                    groupVariant="side-action-group"
                                                 >
                                                     {/* The contents of this <TreeNodeDisplayIconWrapper> are positioned absolutely, so to give the effect it's inside the button */}
-                                                    {!isEmptyFolder && (
-                                                        <TreeNodeDisplayIconWrapper
-                                                            item={item}
-                                                            expandedItemIds={expandedItemIds}
-                                                            defaultNodeIcon={defaultNodeIcon}
-                                                            handleClick={handleClick}
-                                                            enableMultiSelection={enableMultiSelection}
-                                                            defaultOffset={iconWrapperOffset}
-                                                            multiSelectionOffset={iconWrapperOffsetMultiSelection}
-                                                            checkedItemCount={checkedItemCount}
-                                                            onItemChecked={onItemChecked}
-                                                            folderSelectMode={folderSelectMode}
-                                                        />
-                                                    )}
+                                                    <TreeNodeDisplayIconWrapper
+                                                        item={item}
+                                                        expandedItemIds={expandedItemIds}
+                                                        defaultNodeIcon={defaultNodeIcon}
+                                                        handleClick={handleClick}
+                                                        enableMultiSelection={enableMultiSelection}
+                                                        defaultOffset={iconWrapperOffset}
+                                                        multiSelectionOffset={iconWrapperOffsetMultiSelection}
+                                                        checkedItemCount={checkedItemCount}
+                                                        onItemChecked={onItemChecked}
+                                                        folderSelectMode={folderSelectMode}
+                                                        isEmptyFolder={isEmptyFolder}
+                                                    />
 
                                                     {isItemEditing?.(item) ? (
                                                         <InlineEditField
@@ -379,231 +565,7 @@ const LemonTreeNode = forwardRef<HTMLDivElement, LemonTreeNodeProps>(
                                                             }}
                                                         />
                                                     ) : (
-                                                        <ButtonPrimitive
-                                                            data-id={item.id}
-                                                            // When dragging, don't allow links to be clicked,
-                                                            // without this drag end would fire this href causing a reload
-                                                            href={isDragging ? undefined : item.record?.href}
-                                                            onClick={() => {
-                                                                handleClick(item)
-                                                            }}
-                                                            className={cn(
-                                                                'group/lemon-tree-button',
-                                                                'relative z-1 focus-visible:bg-fill-button-tertiary-hover h-[var(--button-height-base)] motion-safe:transition-[padding] duration-50',
-                                                                {
-                                                                    'bg-fill-button-tertiary-hover':
-                                                                        (folderSelectMode && selectedId === item.id) ||
-                                                                        isContextMenuOpenForItem === item.id,
-                                                                    'bg-fill-button-tertiary-active':
-                                                                        getItemActiveState(item),
-                                                                    'group-hover/lemon-tree-button-group:bg-fill-button-tertiary-hover cursor-pointer':
-                                                                        !isEmptyFolder,
-                                                                    'opacity-50 cursor-default':
-                                                                        folderSelectMode && !isFolder,
-                                                                }
-                                                            )}
-                                                            role="treeitem"
-                                                            active={getItemActiveState(item)}
-                                                            menuItem
-                                                            sideActionLeft
-                                                            tooltip={
-                                                                isDragging || isEmptyFolder || mode === 'table'
-                                                                    ? undefined
-                                                                    : displayName
-                                                            }
-                                                            tooltipPlacement="right"
-                                                            disabled={isEmptyFolder}
-                                                            tabIndex={isEmptyFolder ? -1 : 0}
-                                                            buttonWrapper={
-                                                                enableDragAndDrop && isItemDraggable?.(item) && item.id
-                                                                    ? (button) => (
-                                                                          <TreeNodeDraggable
-                                                                              id={item.id}
-                                                                              enableDragging
-                                                                              className="h-[var(--button-height-base)]"
-                                                                          >
-                                                                              {button}
-                                                                          </TreeNodeDraggable>
-                                                                      )
-                                                                    : undefined
-                                                            }
-                                                            aria-level={depth + 1}
-                                                            aria-setsize={data.length} // TODO: somehow get all loaded items length here in children
-                                                            aria-posinset={index + 1}
-                                                            aria-selected={selectedId === item.id}
-                                                            aria-disabled={!!item.disabledReason}
-                                                            aria-haspopup={!!itemContextMenu?.(item)}
-                                                            aria-roledescription="tree item"
-                                                            aria-rolemap={`item-${item.id}`}
-                                                            aria-label={ariaLabel}
-                                                        >
-                                                            {/* Spacer to offset button padding */}
-                                                            <div
-                                                                className="h-full bg-transparent pointer-events-none flex-shrink-0 transition-[width] duration-50"
-                                                                // eslint-disable-next-line react/forbid-dom-props
-                                                                style={{
-                                                                    width:
-                                                                        enableMultiSelection && !item.disableSelect
-                                                                            ? `${emptySpaceOffset + 26}px`
-                                                                            : `${emptySpaceOffset}px`,
-                                                                }}
-                                                            />
-
-                                                            {/* Render contents */}
-                                                            <span
-                                                                className={cn('relative truncate', {
-                                                                    'w-full h-full h-[var(--button-height-base)]':
-                                                                        mode === 'table',
-                                                                })}
-                                                            >
-                                                                <span
-                                                                    className={cn('truncate text-left', {
-                                                                        'absolute h-[var(--button-height-base)] flex items-center':
-                                                                            mode === 'table',
-                                                                        'w-full': mode === 'tree',
-                                                                    })}
-                                                                    // eslint-disable-next-line react/forbid-dom-props
-                                                                    style={{
-                                                                        width: `${
-                                                                            tableColumnSizeAndPosition &&
-                                                                            tableColumnSizeAndPosition?.[0]?.width
-                                                                                ? tableColumnSizeAndPosition?.[0]
-                                                                                      ?.width - emptySpaceOffset
-                                                                                : 0
-                                                                        }px`,
-                                                                    }}
-                                                                >
-                                                                    {renderItem ? (
-                                                                        <>
-                                                                            {renderItem(
-                                                                                item,
-                                                                                <span
-                                                                                    className={cn({
-                                                                                        'font-semibold': isFolder,
-                                                                                    })}
-                                                                                >
-                                                                                    <Tooltip
-                                                                                        title={
-                                                                                            mode === 'table'
-                                                                                                ? displayName
-                                                                                                : undefined
-                                                                                        }
-                                                                                        placement="top-start"
-                                                                                        className="w-fit"
-                                                                                    >
-                                                                                        <span>{displayName}</span>
-                                                                                    </Tooltip>
-                                                                                </span>
-                                                                            )}
-                                                                        </>
-                                                                    ) : (
-                                                                        <span
-                                                                            className={cn('truncate', {
-                                                                                'font-semibold':
-                                                                                    isFolder && !isEmptyFolder,
-                                                                            })}
-                                                                            // eslint-disable-next-line react/forbid-dom-props
-                                                                            style={{
-                                                                                paddingRight:
-                                                                                    mode === 'table'
-                                                                                        ? `3px`
-                                                                                        : undefined,
-                                                                            }}
-                                                                        >
-                                                                            <Tooltip
-                                                                                title={
-                                                                                    mode === 'table'
-                                                                                        ? displayName
-                                                                                        : undefined
-                                                                                }
-                                                                                placement="top-start"
-                                                                                className="w-fit"
-                                                                            >
-                                                                                <span>{displayName}</span>
-                                                                            </Tooltip>
-                                                                        </span>
-                                                                    )}
-
-                                                                    {/* Loading state */}
-                                                                    {item.record?.loading && (
-                                                                        <Spinner className="ml-1" />
-                                                                    )}
-
-                                                                    {/* Unapplied state */}
-                                                                    {item.record?.unapplied && (
-                                                                        <IconUpload className="ml-1 text-warning" />
-                                                                    )}
-                                                                </span>
-
-                                                                {mode === 'table' &&
-                                                                    tableViewKeys?.headers
-                                                                        .slice(1)
-                                                                        .map((header, index) => {
-                                                                            const value = header.key
-                                                                                .split('.')
-                                                                                .reduce(
-                                                                                    (obj, key) => (obj as any)?.[key],
-                                                                                    item
-                                                                                )
-
-                                                                            return (
-                                                                                <span
-                                                                                    key={header.key}
-                                                                                    className="absolute truncate text-left flex items-center h-[var(--button-height-base)]"
-                                                                                    // eslint-disable-next-line react/forbid-dom-props
-                                                                                    style={{
-                                                                                        left: `${
-                                                                                            tableColumnSizeAndPosition &&
-                                                                                            tableColumnSizeAndPosition?.[
-                                                                                                index + 1
-                                                                                            ]?.left
-                                                                                                ? tableColumnSizeAndPosition?.[
-                                                                                                      index + 1
-                                                                                                  ]?.left -
-                                                                                                  10 -
-                                                                                                  emptySpaceOffset
-                                                                                                : 0
-                                                                                        }px`,
-                                                                                        width: `${
-                                                                                            tableColumnSizeAndPosition?.[
-                                                                                                index + 1
-                                                                                            ]?.width
-                                                                                        }px`,
-                                                                                    }}
-                                                                                >
-                                                                                    <Tooltip
-                                                                                        title={
-                                                                                            typeof header.tooltip ===
-                                                                                            'function'
-                                                                                                ? header.tooltip(value)
-                                                                                                : header.tooltip
-                                                                                        }
-                                                                                        placement="top-start"
-                                                                                    >
-                                                                                        <span
-                                                                                            className={cn(
-                                                                                                'starting:opacity-0 opacity-100 delay-50 motion-safe:transition-opacity duration-100 font-normal',
-                                                                                                {
-                                                                                                    'font-normal':
-                                                                                                        index > 1,
-                                                                                                    'opacity-0':
-                                                                                                        index !== 1 &&
-                                                                                                        isEmptyFolder,
-                                                                                                }
-                                                                                            )}
-                                                                                        >
-                                                                                            {header.formatFunction
-                                                                                                ? header.formatFunction(
-                                                                                                      value
-                                                                                                  )
-                                                                                                : value}
-                                                                                        </span>
-                                                                                    </Tooltip>
-                                                                                </span>
-                                                                            )
-                                                                        })}
-                                                            </span>
-                                                        </ButtonPrimitive>
+                                                        button
                                                     )}
 
                                                     {itemSideAction && !isEmptyFolder && (
@@ -611,7 +573,7 @@ const LemonTreeNode = forwardRef<HTMLDivElement, LemonTreeNodeProps>(
                                                             <DropdownMenuTrigger asChild>
                                                                 <ButtonPrimitive
                                                                     iconOnly
-                                                                    sideActionRight
+                                                                    isSideActionRight
                                                                     className="z-2 shrink-0 motion-safe:transition-opacity duration-[50ms] group-hover/lemon-tree-button-group:opacity-100 aria-expanded:opacity-100"
                                                                 >
                                                                     <IconEllipsis className="size-3 text-tertiary" />
@@ -760,7 +722,7 @@ const LemonTree = forwardRef<LemonTreeRef, LemonTreeProps>(
             isFinishedBuildingTreeData,
             enableMultiSelection = false,
             onItemChecked,
-            checkedItemCount,
+            checkedItemCount = 0,
             tableViewKeys,
             emptySpaceContextMenu,
             folderSelectMode = false,
diff --git a/frontend/src/lib/lemon-ui/LemonTree/LemonTreeUtils.tsx b/frontend/src/lib/lemon-ui/LemonTree/LemonTreeUtils.tsx
index 273d91802392..7a6065128347 100644
--- a/frontend/src/lib/lemon-ui/LemonTree/LemonTreeUtils.tsx
+++ b/frontend/src/lib/lemon-ui/LemonTree/LemonTreeUtils.tsx
@@ -1,6 +1,6 @@
 import { useDraggable, useDroppable } from '@dnd-kit/core'
-import { IconChevronRight, IconDocument, IconFolder, IconFolderOpenFilled } from '@posthog/icons'
-import { buttonVariants } from 'lib/ui/Button/ButtonPrimitives'
+import { IconChevronRight, IconCircleDashed, IconDocument, IconFolder, IconFolderOpenFilled } from '@posthog/icons'
+import { buttonPrimitiveVariants } from 'lib/ui/Button/ButtonPrimitives'
 import { cn } from 'lib/utils/css-classes'
 import { CSSProperties, useEffect, useRef } from 'react'
 
@@ -20,6 +20,7 @@ type TreeNodeDisplayIconWrapperProps = {
     checkedItemCount?: number
     onItemChecked?: (id: string, checked: boolean, shift: boolean) => void
     folderSelectMode: boolean
+    isEmptyFolder: boolean
 }
 
 export const TreeNodeDisplayIconWrapper = ({
@@ -33,6 +34,7 @@ export const TreeNodeDisplayIconWrapper = ({
     defaultOffset,
     multiSelectionOffset,
     folderSelectMode,
+    isEmptyFolder,
 }: TreeNodeDisplayIconWrapperProps): JSX.Element => {
     return (
         <>
@@ -46,7 +48,8 @@ export const TreeNodeDisplayIconWrapper = ({
                     'absolute flex items-center justify-center bg-transparent flex-shrink-0 h-[var(--button-height-base)] z-3',
                     {
                         // Apply group class only when there are no checked items
-                        'group/lemon-tree-icon-wrapper': checkedItemCount === 0 && !folderSelectMode,
+                        'group/lemon-tree-icon-wrapper': checkedItemCount === 0 && !folderSelectMode && !isEmptyFolder,
+                        'cursor-default': isEmptyFolder,
                     }
                 )}
             >
@@ -155,6 +158,7 @@ export const TreeNodeDisplayIcon = ({
 }: TreeNodeDisplayIconProps): JSX.Element => {
     const isOpen = expandedItemIds.includes(item.id)
     const isFolder = item.record?.type === 'folder'
+    const isEmptyFolder = item.type === 'empty-folder'
     const isFile = item.record?.type === 'file'
     let iconElement: React.ReactNode = item.icon || defaultNodeIcon || <div />
 
@@ -162,6 +166,10 @@ export const TreeNodeDisplayIcon = ({
         iconElement = isOpen ? <IconFolderOpenFilled /> : <IconFolder />
     }
 
+    if (isEmptyFolder) {
+        iconElement = <IconCircleDashed />
+    }
+
     if (isFile) {
         iconElement = <IconDocument />
     }
@@ -314,7 +322,7 @@ export const InlineEditField = ({
         <form
             onSubmit={onSubmit}
             className={cn(
-                buttonVariants({ menuItem: true, size: 'base', sideActionLeft: true }),
+                buttonPrimitiveVariants({ menuItem: true, size: 'base', hasSideActionRight: true }),
                 className,
                 'bg-fill-button-tertiary-active'
             )}
diff --git a/frontend/src/lib/lemon-ui/Link/Link.tsx b/frontend/src/lib/lemon-ui/Link/Link.tsx
index eefdb5b001ae..6d5f2e3be6e8 100644
--- a/frontend/src/lib/lemon-ui/Link/Link.tsx
+++ b/frontend/src/lib/lemon-ui/Link/Link.tsx
@@ -1,9 +1,10 @@
 import './Link.scss'
 
 import { IconExternal, IconOpenSidebar } from '@posthog/icons'
-import clsx from 'clsx'
 import { router } from 'kea-router'
+import { ButtonPrimitiveProps, buttonPrimitiveVariants } from 'lib/ui/Button/ButtonPrimitives'
 import { isExternalLink } from 'lib/utils'
+import { cn } from 'lib/utils/css-classes'
 import { getCurrentTeamId } from 'lib/utils/getAppContext'
 import { addProjectIdIfMissing } from 'lib/utils/router-utils'
 import React, { useContext } from 'react'
@@ -12,7 +13,7 @@ import { useNotebookDrag } from 'scenes/notebooks/AddToNotebook/DraggableToNoteb
 import { sidePanelStateLogic, WithinSidePanelContext } from '~/layout/navigation-3000/sidepanel/sidePanelStateLogic'
 import { SidePanelTab } from '~/types'
 
-import { Tooltip } from '../Tooltip'
+import { Tooltip, TooltipProps } from '../Tooltip'
 
 type RoutePart = string | Record<string, any>
 
@@ -41,6 +42,26 @@ export type LinkProps = Pick<React.HTMLProps<HTMLAnchorElement>, 'target' | 'cla
     targetBlankIcon?: boolean
     /** If true, the default color will be as normal text with only a link color on hover */
     subtle?: boolean
+
+    /**
+     * Accessibility role of the link.
+     */
+    role?: string
+
+    /**
+     * Accessibility tab index of the link.
+     */
+    tabIndex?: number
+
+    /**
+     * Button props to pass to the button primitive.
+     * If provided, the link will be rendered as the "new" button primitive.
+     */
+    buttonProps?: Omit<ButtonPrimitiveProps, 'tooltip' | 'tooltipDocLink' | 'tooltipPlacement' | 'children'>
+
+    tooltip?: TooltipProps['title']
+    tooltipDocLink?: TooltipProps['docLink']
+    tooltipPlacement?: TooltipProps['placement']
 }
 
 const shouldForcePageLoad = (input: any): boolean => {
@@ -90,6 +111,12 @@ export const Link: React.FC<LinkProps & React.RefAttributes<HTMLElement>> = Reac
             disabled,
             disabledReason,
             targetBlankIcon = typeof children === 'string',
+            buttonProps,
+            tooltip,
+            tooltipDocLink,
+            tooltipPlacement,
+            role,
+            tabIndex,
             ...props
         },
         ref
@@ -163,15 +190,21 @@ export const Link: React.FC<LinkProps & React.RefAttributes<HTMLElement>> = Reac
                 : '#'
             : undefined
 
-        return to ? (
+        const elementClasses = buttonProps
+            ? buttonPrimitiveVariants(buttonProps)
+            : `Link ${subtle ? 'Link--subtle' : ''}`
+
+        let element = (
             // eslint-disable-next-line react/forbid-elements
             <a
                 ref={ref as any}
-                className={clsx('Link', subtle && 'Link--subtle', className)}
+                className={cn(elementClasses, className)}
                 onClick={onClick}
                 href={href}
                 target={target}
                 rel={target === '_blank' ? rel : undefined}
+                role={role}
+                tabIndex={tabIndex}
                 {...props}
                 {...draggableProps}
             >
@@ -180,25 +213,42 @@ export const Link: React.FC<LinkProps & React.RefAttributes<HTMLElement>> = Reac
                     (shouldOpenInDocsPanel && sidePanelStateLogic.isMounted() ? (
                         <IconOpenSidebar />
                     ) : target === '_blank' ? (
-                        <IconExternal />
+                        <IconExternal className={buttonProps ? 'size-3' : ''} />
                     ) : null)}
             </a>
-        ) : (
-            <Tooltip title={disabledReason ? <span className="italic">{disabledReason}</span> : undefined}>
-                <span>
-                    <button
-                        ref={ref as any}
-                        className={clsx('Link', subtle && 'Link--subtle', className)}
-                        onClick={onClick}
-                        type="button"
-                        disabled={disabled || !!disabledReason}
-                        {...props}
-                    >
-                        {children}
-                    </button>
-                </span>
-            </Tooltip>
         )
+
+        if (tooltip && to) {
+            element = (
+                <Tooltip title={tooltip} docLink={tooltipDocLink} placement={tooltipPlacement}>
+                    {element}
+                </Tooltip>
+            )
+        }
+
+        if (!to) {
+            element = (
+                <Tooltip
+                    title={disabledReason ? <span className="italic">{disabledReason}</span> : tooltip || undefined}
+                    placement={tooltipPlacement}
+                >
+                    <span>
+                        <button
+                            ref={ref as any}
+                            className={cn(elementClasses, className)}
+                            onClick={onClick}
+                            type="button"
+                            disabled={disabled || !!disabledReason}
+                            {...props}
+                        >
+                            {children}
+                        </button>
+                    </span>
+                </Tooltip>
+            )
+        }
+
+        return element
     }
 )
 Link.displayName = 'Link'
diff --git a/frontend/src/lib/ui/Button/ButtonPrimitives.css b/frontend/src/lib/ui/Button/ButtonPrimitives.css
deleted file mode 100644
index caa19fa6ca69..000000000000
--- a/frontend/src/lib/ui/Button/ButtonPrimitives.css
+++ /dev/null
@@ -1,69 +0,0 @@
-:root {
-    --button-padding-x-sm: 4px;
-    --button-padding-x-base: 6px;
-    --button-padding-x-lg: 8px;
-    --button-padding-y-sm: 3px;
-    --button-padding-y-base: 5px;
-    --button-padding-y-lg: 7px;
-    --button-height-sm: 26px;
-    --button-height-base: 30px;
-    --button-height-lg: 40px;
-}
-
-.button-primitive {
-    width: fit-content;
-}
-
-.button-primitive-height-sm {
-    height: var(--button-height-sm);
-
-    &.icon-only {
-        width: var(--button-height-sm);
-    }
-}
-
-.button-primitive-height-base {
-    height: var(--button-height-base);
-
-    &.icon-only {
-        width: var(--button-height-base);
-    }
-}
-
-.button-primitive-height-lg {
-    height: var(--button-height-lg);
-
-    &.icon-only {
-        width: var(--button-height-lg);
-    }
-}
-
-.button-primitive-size-sm {
-    padding-right: var(--button-padding-x-sm);
-    padding-left: var(--button-padding-x-sm);
-
-    svg {
-        width: 12px;
-        height: 12px;
-    }
-}
-
-.button-primitive-size-base {
-    padding-right: var(--button-padding-x-base);
-    padding-left: var(--button-padding-x-base);
-
-    svg {
-        width: 16px;
-        height: 16px;
-    }
-}
-
-.button-primitive-size-lg {
-    padding-right: var(--button-padding-x-lg);
-    padding-left: var(--button-padding-x-lg);
-
-    svg {
-        width: 20px;
-        height: 20px;
-    }
-}
diff --git a/frontend/src/lib/ui/Button/ButtonPrimitives.scss b/frontend/src/lib/ui/Button/ButtonPrimitives.scss
new file mode 100644
index 000000000000..bfd2f5482283
--- /dev/null
+++ b/frontend/src/lib/ui/Button/ButtonPrimitives.scss
@@ -0,0 +1,148 @@
+:root {
+    --button-padding-x-sm: 4px;
+    --button-padding-x-base: 6px;
+    --button-padding-x-lg: 8px;
+    --button-padding-y-sm: 3px;
+    --button-padding-y-base: 5px;
+    --button-padding-y-lg: 7px;
+    --button-height-sm: 26px;
+    --button-height-base: 30px;
+    --button-height-lg: 40px;
+}
+
+.button-primitive {
+    position: relative;
+    display: inline-flex;
+    align-items: center;
+    width: fit-content;
+    max-width: 100%;
+    font-weight: 400;
+    color: var(--text-primary);
+    cursor: pointer;
+    border-radius: var(--radius);
+
+    svg {
+        flex-shrink: 0;
+    }
+
+    &[aria-disabled='true'] {
+        cursor: not-allowed;
+        opacity: 0.5;
+    }
+}
+
+.button-primitive--variant-default {
+    border: 1px solid transparent;
+
+    &:not([aria-disabled='true']) {
+        &[data-active='true'],
+        &[data-current='true'],
+        &[data-state='open'],
+        &[data-state='checked'],
+        &[data-highlighted] {
+            background-color: var(--bg-fill-button-tertiary-active);
+        }
+
+        &[data-focused='true'],
+        &:hover {
+            background-color: var(--bg-fill-button-tertiary-hover);
+        }
+    }
+}
+
+.button-primitive--variant-outline {
+    border: 1px solid var(--border-secondary);
+
+    &:not([aria-disabled='true']) {
+        &[data-active='true'],
+        &[data-current='true'],
+        &[data-state='open'],
+        &[data-state='checked'],
+        &[data-highlighted] {
+            background-color: var(--bg-fill-button-tertiary-active);
+        }
+
+        &[data-focused='true'],
+        &:hover {
+            background-color: var(--bg-fill-button-tertiary-hover);
+        }
+    }
+}
+
+.button-primitive-group {
+    .button-primitive {
+        border-radius: 0;
+
+        &:first-child {
+            border-top-left-radius: var(--radius);
+            border-bottom-left-radius: var(--radius);
+        }
+
+        &:last-child {
+            border-top-right-radius: var(--radius);
+            border-bottom-right-radius: var(--radius);
+        }
+
+        &:not(:first-child) {
+            border-left-width: 0;
+        }
+    }
+
+    &:hover {
+        background-color: transparent !important;
+    }
+}
+
+.button-primitive--height-sm {
+    height: var(--button-height-sm);
+
+    &.icon-only {
+        width: var(--button-height-sm);
+    }
+}
+
+.button-primitive--height-base {
+    height: var(--button-height-base);
+
+    &.icon-only {
+        width: var(--button-height-base);
+    }
+}
+
+.button-primitive--height-lg {
+    height: var(--button-height-lg);
+
+    &.icon-only {
+        width: var(--button-height-lg);
+    }
+}
+
+.button-primitive--size-sm {
+    padding-right: var(--button-padding-x-sm);
+    padding-left: var(--button-padding-x-sm);
+
+    svg {
+        width: 12px;
+        height: 12px;
+    }
+}
+
+.button-primitive--size-base {
+    padding-right: var(--button-padding-x-base);
+    padding-left: var(--button-padding-x-base);
+
+    svg {
+        width: 16px;
+        height: 16px;
+    }
+}
+
+.button-primitive--size-lg {
+    padding-right: var(--button-padding-x-lg);
+    padding-left: var(--button-padding-x-lg);
+
+    svg {
+        width: 20px;
+        height: 20px;
+    }
+}
diff --git a/frontend/src/lib/ui/Button/ButtonPrimitives.stories.tsx b/frontend/src/lib/ui/Button/ButtonPrimitives.stories.tsx
index cb07b680f0e3..fcbcb21201e3 100644
--- a/frontend/src/lib/ui/Button/ButtonPrimitives.stories.tsx
+++ b/frontend/src/lib/ui/Button/ButtonPrimitives.stories.tsx
@@ -1,5 +1,6 @@
-import { IconSearch } from '@posthog/icons'
+import { IconGear, IconSearch } from '@posthog/icons'
 import type { Meta } from '@storybook/react'
+import { Link } from 'lib/lemon-ui/Link/Link'
 import {
     DropdownMenu,
     DropdownMenuContent,
@@ -27,12 +28,16 @@ export function Default(): JSX.Element {
                 Button base
             </ButtonPrimitive>
 
+            <ButtonPrimitive size="base" disabled>
+                Button base disabled
+            </ButtonPrimitive>
+
             <ButtonPrimitive variant="outline" size="base">
                 <IconSearch />
                 Button base
             </ButtonPrimitive>
 
-            <ButtonPrimitive variant="outline" size="base" className="max-w-[100px]">
+            <ButtonPrimitive variant="outline" size="base" className="max-w-[120px]">
                 <IconSearch />
                 <span className="truncate">Button base truncate</span>
             </ButtonPrimitive>
@@ -41,7 +46,7 @@ export function Default(): JSX.Element {
                 <IconSearch />
             </ButtonPrimitive>
 
-            <ButtonGroupPrimitive size="base" variant="outline">
+            <ButtonGroupPrimitive size="base" groupVariant="outline">
                 <ButtonPrimitive
                     onClick={() => {
                         alert('clicked')
@@ -50,9 +55,9 @@ export function Default(): JSX.Element {
                 >
                     Button1
                 </ButtonPrimitive>
-                <ButtonPrimitive href="#" tooltip="Tooltip">
+                <Link tooltip="Tooltip" to="https://google.com" target="_blank">
                     Link
-                </ButtonPrimitive>
+                </Link>
                 <ButtonPrimitive iconOnly tooltip="Tooltip">
                     <IconSearch />
                 </ButtonPrimitive>
@@ -61,13 +66,26 @@ export function Default(): JSX.Element {
                 </ButtonPrimitive>
             </ButtonGroupPrimitive>
 
-            <ButtonGroupPrimitive size="base" variant="outline" groupVariant="side-action-group">
-                <ButtonPrimitive href="#" sideActionLeft tooltip="Tooltip">
+            <ButtonGroupPrimitive size="base" groupVariant="outline">
+                <Link
+                    buttonProps={{
+                        hasSideActionRight: true,
+                    }}
+                    tooltip="Tooltip"
+                    to="#"
+                >
                     Side action group
-                </ButtonPrimitive>
-                <ButtonPrimitive iconOnly sideActionRight tooltip="Tooltip">
+                </Link>
+                <Link
+                    buttonProps={{
+                        iconOnly: true,
+                        isSideActionRight: true,
+                    }}
+                    tooltip="Tooltip"
+                    to="#"
+                >
                     <IconSearch />
-                </ButtonPrimitive>
+                </Link>
             </ButtonGroupPrimitive>
 
             <DropdownMenu>
@@ -91,16 +109,49 @@ export function Default(): JSX.Element {
                             Item 2
                         </ButtonPrimitive>
                     </DropdownMenuItem>
+                    <DropdownMenuLabel>Links</DropdownMenuLabel>
+                    <ButtonGroupPrimitive menuItem fullWidth>
+                        <DropdownMenuItem asChild>
+                            <Link
+                                buttonProps={{
+                                    menuItem: true,
+                                    hasSideActionRight: true,
+                                }}
+                                tooltip="go to google"
+                                tooltipPlacement="right"
+                                to="https://google.com"
+                            >
+                                Name
+                            </Link>
+                        </DropdownMenuItem>
+                        <Link
+                            buttonProps={{
+                                iconOnly: true,
+                                isSideActionRight: true,
+                            }}
+                            tooltip="go to bing"
+                            tooltipPlacement="right"
+                            to="https://bing.com"
+                        >
+                            <IconGear />
+                        </Link>
+                    </ButtonGroupPrimitive>
                 </DropdownMenuContent>
             </DropdownMenu>
 
             <DropdownMenu>
-                <ButtonGroupPrimitive size="base" variant="outline" fullWidth>
-                    <ButtonPrimitive href="#" menuItem>
+                <ButtonGroupPrimitive size="base" groupVariant="outline" fullWidth>
+                    <Link
+                        buttonProps={{
+                            menuItem: true,
+                            hasSideActionRight: true,
+                        }}
+                        to="#"
+                    >
                         Link here, dropdown on the right
-                    </ButtonPrimitive>
+                    </Link>
                     <DropdownMenuTrigger asChild>
-                        <ButtonPrimitive variant="outline" size="base" iconOnly>
+                        <ButtonPrimitive variant="outline" size="base" iconOnly isSideActionRight>
                             <IconSearch />
                         </ButtonPrimitive>
                     </DropdownMenuTrigger>
@@ -124,11 +175,17 @@ export function Default(): JSX.Element {
 
             <DropdownMenu>
                 <ButtonGroupPrimitive size="base" variant="default" fullWidth>
-                    <ButtonPrimitive href="#" menuItem>
+                    <Link
+                        buttonProps={{
+                            menuItem: true,
+                            hasSideActionRight: true,
+                        }}
+                        to="#"
+                    >
                         Link here, dropdown on the right
-                    </ButtonPrimitive>
+                    </Link>
                     <DropdownMenuTrigger asChild>
-                        <ButtonPrimitive variant="outline" size="base" iconOnly>
+                        <ButtonPrimitive variant="outline" size="base" iconOnly isSideActionRight>
                             <IconSearch />
                         </ButtonPrimitive>
                     </DropdownMenuTrigger>
diff --git a/frontend/src/lib/ui/Button/ButtonPrimitives.tsx b/frontend/src/lib/ui/Button/ButtonPrimitives.tsx
index d4b01fd55870..fbf8004578b5 100644
--- a/frontend/src/lib/ui/Button/ButtonPrimitives.tsx
+++ b/frontend/src/lib/ui/Button/ButtonPrimitives.tsx
@@ -1,4 +1,4 @@
-import './ButtonPrimitives.css'
+import './ButtonPrimitives.scss'
 
 import { cva, type VariantProps } from 'cva'
 import { Tooltip, TooltipProps } from 'lib/lemon-ui/Tooltip/Tooltip'
@@ -9,7 +9,7 @@ import React, { createContext, forwardRef, ReactNode, useContext } from 'react'
 /*                           Props & Contexts & Hooks                         */
 /* -------------------------------------------------------------------------- */
 
-type ButtonVariant = 'default' | 'outline' | 'default-group' | 'side-action-group'
+type ButtonVariant = 'default' | 'outline'
 
 export type ButtonSize = 'sm' | 'base' | 'lg' | 'fit'
 
@@ -29,19 +29,18 @@ type ButtonGroupProps = {
     children: ReactNode
     className?: string
     groupVariant?: ButtonVariant
-} & VariantProps<typeof buttonVariants>
+} & VariantProps<typeof buttonPrimitiveVariants>
 
 type ButtonBaseProps = {
     iconOnly?: boolean
     showDivider?: boolean
-    external?: boolean
     disabled?: boolean
     active?: boolean
     tooltip?: TooltipProps['title']
     tooltipDocLink?: TooltipProps['docLink']
     tooltipPlacement?: TooltipProps['placement']
     buttonWrapper?: (button: JSX.Element) => JSX.Element
-} & VariantProps<typeof buttonVariants>
+} & VariantProps<typeof buttonPrimitiveVariants>
 
 /* -------------------------------------------------------------------------- */
 /*                              Button Group Variants                         */
@@ -54,7 +53,7 @@ type ButtonBaseProps = {
 export const ButtonGroupPrimitive = forwardRef<HTMLDivElement, ButtonGroupProps>((props, ref) => {
     const {
         className,
-        groupVariant = 'default-group',
+        groupVariant = 'default',
         variant = 'default',
         size = 'base',
         fullWidth = false,
@@ -68,13 +67,13 @@ export const ButtonGroupPrimitive = forwardRef<HTMLDivElement, ButtonGroupProps>
         sizeContext: size,
     }
 
-    let buttonHeight = 'button-primitive-height-base'
+    let buttonHeight = 'button-primitive--height-base'
     switch (size) {
         case 'sm':
-            buttonHeight = 'button-primitive-height-sm'
+            buttonHeight = 'button-primitive--height-sm'
             break
         case 'lg':
-            buttonHeight = 'button-primitive-height-lg'
+            buttonHeight = 'button-primitive--height-lg'
             break
         case 'fit':
             buttonHeight = ''
@@ -85,7 +84,8 @@ export const ButtonGroupPrimitive = forwardRef<HTMLDivElement, ButtonGroupProps>
         <ButtonContext.Provider value={setContext}>
             <Comp
                 className={cn(
-                    buttonVariants({
+                    'button-primitive-group',
+                    buttonPrimitiveVariants({
                         size: 'fit',
                         variant: groupVariant,
                         fullWidth,
@@ -109,72 +109,21 @@ ButtonGroupPrimitive.displayName = 'ButtonGroupPrimitive'
 /*                              Button Base Component                         */
 /* -------------------------------------------------------------------------- */
 
-interface ButtonAsButtonProps extends ButtonBaseProps, React.ButtonHTMLAttributes<HTMLButtonElement> {
-    href?: never
-}
-
-interface ButtonAsAnchorProps extends ButtonBaseProps, Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {
-    href: string
-}
+export interface ButtonPrimitiveProps extends ButtonBaseProps, React.ButtonHTMLAttributes<HTMLButtonElement> {}
 
-type ButtonProps = ButtonAsButtonProps | ButtonAsAnchorProps
-
-export const buttonVariants = cva({
-    base: `
-        button-primitive
-        group/button-primitive
-        cursor-pointer
-        inline-flex
-        relative
-        items-center
-        rounded-md
-        font-normal
-        aria-disabled:cursor-not-allowed
-        aria-disabled:opacity-50
-        text-current
-        [&_svg]:shrink-0
-    `,
+export const buttonPrimitiveVariants = cva({
+    base: 'button-primitive group/button-primitive',
     variants: {
         variant: {
             // Bordereless variant (aka posthog tertiary button)
-            default: `
-                border border-transparent
-                text-primary 
-                max-w-full
-                not-disabled:hover:bg-fill-button-tertiary-hover 
-                not-disabled:data-[focused=true]:bg-fill-button-tertiary-hover 
-                not-disabled:data-[active=true]:bg-fill-button-tertiary-active 
-                not-disabled:data-[current=true]:bg-fill-button-tertiary-active 
-                not-disabled:data-[state=open]:bg-fill-button-tertiary-active 
-                not-disabled:data-[state=checked]:bg-fill-button-tertiary-active
-                not-disabled:data-highlighted:bg-fill-button-tertiary-active
-            `,
+            default: 'button-primitive--variant-default',
             // Outline variant (aka posthog secondary button)
-            outline: `
-                border border-secondary
-                not-disabled:hover:border-tertiary
-                hover:bg-fill-button-tertiary-active
-            `,
-            // Buttons next to each other
-            'default-group': `
-                border border-transparent
-                text-primary 
-                max-w-full
-                [&_.button-primitive]:rounded-none
-                [&_.button-primitive]:first:rounded-l-md
-                [&_.button-primitive]:last:rounded-r-md
-                [&_.button-primitive:not(:first-child)]:border-l-0
-            `,
-            'side-action-group': `
-                border border-transparent
-                text-primary 
-                max-w-full
-            `,
+            outline: 'button-primitive--variant-outline',
         },
         size: {
-            sm: `button-primitive-size-sm button-primitive-height-sm text-xs`,
-            base: `button-primitive-size-base button-primitive-height-base text-sm`,
-            lg: `button-primitive-size-lg button-primitive-height-lg text-base`,
+            sm: `button-primitive--size-sm button-primitive--height-sm text-xs`,
+            base: `button-primitive--size-base button-primitive--height-base text-sm`,
+            lg: `button-primitive--size-lg button-primitive--height-lg text-base`,
             fit: 'px-0',
         },
         iconOnly: {
@@ -201,11 +150,11 @@ export const buttonVariants = cva({
             true: 'disabled:pointer-events-none disabled:opacity-50',
             false: '',
         },
-        sideActionLeft: {
+        hasSideActionRight: {
             true: 'rounded-md',
             false: '',
         },
-        sideActionRight: {
+        isSideActionRight: {
             true: 'absolute right-0 -top-px -bottom-px rounded-l-none',
         },
     },
@@ -218,51 +167,43 @@ export const buttonVariants = cva({
     },
     compoundVariants: [
         {
-            sideActionLeft: true,
+            hasSideActionRight: true,
             size: 'sm',
             className: `
                 pr-[calc(var(--button-height-sm)+var(--button-padding-x-sm))]
             `,
         },
         {
-            sideActionLeft: true,
+            hasSideActionRight: true,
             size: 'base',
             className: `
                 pr-[calc(var(--button-height-base)+var(--button-padding-x-base))]
             `,
         },
         {
-            sideActionLeft: true,
+            hasSideActionRight: true,
             size: 'lg',
             className: `
                 pr-[calc(var(--button-height-lg)+var(--button-padding-x-lg))]
             `,
         },
-        // {
-        //     sideActionRight: true,
-        //     className: `
-        //         rounded-l-none
-        //     `,
-        // },
     ],
 })
 
-export const ButtonPrimitive = forwardRef<HTMLButtonElement | HTMLAnchorElement, ButtonProps>((props, ref) => {
+export const ButtonPrimitive = forwardRef<HTMLButtonElement, ButtonPrimitiveProps>((props, ref) => {
     const {
         className,
         variant,
         size,
         fullWidth,
-        href,
-        external,
         children,
         iconOnly,
         menuItem,
         disabled,
         active,
         buttonWrapper,
-        sideActionLeft,
-        sideActionRight,
+        hasSideActionRight,
+        isSideActionRight,
         tooltip,
         tooltipPlacement,
         tooltipDocLink,
@@ -272,44 +213,25 @@ export const ButtonPrimitive = forwardRef<HTMLButtonElement | HTMLAnchorElement,
     const context = useButtonGroupContext()
     const effectiveSize = context?.sizeContext || size
     const effectiveVariant = context?.variantContext || variant
-    // Determine the component type
-    const Comp = href ? 'a' : 'button'
-    // Determine the external props
-    const externalProps = external && href ? { target: '_blank' } : {}
-    // Determine the element props
-    const elementProps = href ? { href, ...rest } : rest
-
-    // If the button is an anchor and has an onClick, and is not external, prevent the default action and call the onClick
-    if (href && rest.onClick && !external) {
-        const handleClick = (e: React.MouseEvent): void => {
-            // Allow command/ctrl click to open in new tab
-            if (!e.metaKey && !e.ctrlKey) {
-                e.preventDefault()
-            }
-            rest?.onClick?.(e as any)
-        }
-        elementProps.onClick = handleClick
-    }
 
     let buttonComponent: JSX.Element = React.createElement(
-        Comp,
+        'button',
         {
             className: cn(
-                buttonVariants({
+                buttonPrimitiveVariants({
                     variant: effectiveVariant,
                     size: effectiveSize,
                     fullWidth,
                     iconOnly,
                     menuItem,
                     disabled,
-                    sideActionLeft,
-                    sideActionRight,
+                    hasSideActionRight,
+                    isSideActionRight,
                     className,
                 })
             ),
             ref,
-            ...externalProps,
-            ...elementProps,
+            ...rest,
             'aria-disabled': disabled,
             'data-active': active,
         },
diff --git a/frontend/src/lib/ui/DropdownMenu/DropdownMenu.stories.tsx b/frontend/src/lib/ui/DropdownMenu/DropdownMenu.stories.tsx
index 9ae6ef9bdd9e..5311278d5e82 100644
--- a/frontend/src/lib/ui/DropdownMenu/DropdownMenu.stories.tsx
+++ b/frontend/src/lib/ui/DropdownMenu/DropdownMenu.stories.tsx
@@ -1,6 +1,7 @@
 import { IconChevronRight, IconSearch } from '@posthog/icons'
 import { DropdownMenuCheckboxItemProps } from '@radix-ui/react-dropdown-menu'
 import type { Meta } from '@storybook/react'
+import { Link } from 'lib/lemon-ui/Link/Link'
 import {
     DropdownMenu,
     DropdownMenuCheckboxItem,
@@ -51,38 +52,69 @@ export function Default(): JSX.Element {
                     <DropdownMenuLabel>Projects</DropdownMenuLabel>
                     <DropdownMenuSeparator />
                     <DropdownMenuItem asChild disabled>
-                        <ButtonPrimitive menuItem href="/" disabled>
+                        <Link
+                            to="/"
+                            buttonProps={{
+                                menuItem: true,
+                                disabled: true,
+                            }}
+                        >
                             Link 1 (disabled)
-                        </ButtonPrimitive>
+                        </Link>
                     </DropdownMenuItem>
                     <DropdownMenuItem asChild>
-                        <ButtonPrimitive menuItem href="/">
+                        <Link
+                            to="/"
+                            buttonProps={{
+                                menuItem: true,
+                            }}
+                        >
                             Link 2
-                        </ButtonPrimitive>
+                        </Link>
                     </DropdownMenuItem>
                     <DropdownMenuSeparator />
                     <DropdownMenuSub>
                         <DropdownMenuSubTrigger asChild>
-                            <ButtonPrimitive menuItem href="/">
+                            <Link
+                                to="/"
+                                buttonProps={{
+                                    menuItem: true,
+                                }}
+                            >
                                 More options
                                 <IconChevronRight className="group-data-[state=open]/button-root:rotate-180" />
-                            </ButtonPrimitive>
+                            </Link>
                         </DropdownMenuSubTrigger>
                         <DropdownMenuSubContent>
                             <DropdownMenuItem asChild>
-                                <ButtonPrimitive menuItem href="/">
+                                <Link
+                                    to="/"
+                                    buttonProps={{
+                                        menuItem: true,
+                                    }}
+                                >
                                     Sub link 1
-                                </ButtonPrimitive>
+                                </Link>
                             </DropdownMenuItem>
                             <DropdownMenuItem asChild>
-                                <ButtonPrimitive menuItem href="/">
+                                <Link
+                                    to="/"
+                                    buttonProps={{
+                                        menuItem: true,
+                                    }}
+                                >
                                     Sub link 2
-                                </ButtonPrimitive>
+                                </Link>
                             </DropdownMenuItem>
                             <DropdownMenuItem asChild>
-                                <ButtonPrimitive menuItem href="/">
+                                <Link
+                                    to="/"
+                                    buttonProps={{
+                                        menuItem: true,
+                                    }}
+                                >
                                     Sub link 3
-                                </ButtonPrimitive>
+                                </Link>
                             </DropdownMenuItem>
                         </DropdownMenuSubContent>
                     </DropdownMenuSub>


Reviews:
user: login: greptile-apps[bot]
body: 
## PR Summary

This PR improves component architecture by separating button and link functionalities, focusing on fixing issues where ButtonPrimitive was incorrectly used as a link component.

- Removed href/link functionality from ButtonPrimitive and moved to Link component, fixing navigation issues in ProjectDropdownMenu
- Added buttonProps to Link component to maintain consistent button styling when needed
- Renamed CSS classes to follow BEM style and moved styles from .css to .scss
- Added IconCircleDashed for empty folder states in tree components
- Simplified ButtonVariant types by removing redundant group variants and renamed side action props for clarity

<sub>10 file(s) reviewed, 13 comment(s)</sub>
<sub>[Edit PR Review Bot Settings](https://app.greptile.com/review/github) | [Greptile](https://greptile.com?utm_source=greptile_expert&utm_medium=github&utm_campaign=code_reviews&utm_content=posthog_31768)</sub>
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T13:37:44Z
user: login: mariusandra
body: Looks a lot cleaner! 🙌 

I clicked and dragged around and what I tried worked, so 👍 
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-30T15:40:18Z
user: login: graphite-app[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T16:03:22Z

Review comments:
diff_hunk: @@ -41,27 +41,31 @@ function OtherProjectButton({ team }: { team: TeamBasicType }): JSX.Element {
     }, [location.pathname, team.id, team.project_id, currentTeam?.project_id])
 
     return (
-        <ButtonGroupPrimitive menuItem fullWidth groupVariant="side-action-group">
+        <ButtonGroupPrimitive menuItem fullWidth>
             <DropdownMenuItem asChild>
-                <ButtonPrimitive
-                    menuItem
-                    href={relativeOtherProjectPath}
-                    sideActionLeft
-                    tooltip={`Switch to project: ${team.name}`}
-                    tooltipPlacement="right"
+                <Link
+                    buttonProps={{
+                        menuItem: true,
+                        hasSideActionRight: true,
+                        tooltip: `Switch to project: ${team.name}`,
path: frontend/src/layout/panel-layout/ProjectDropdownMenu.tsx
user: login: greptile-apps[bot]
body: logic: hasSideActionRight is used here but the prop name is inconsistent with line 61 which uses isSideActionRight
created_at: 2025-04-30T13:36:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -313,6 +314,193 @@ const LemonTreeNode = forwardRef<HTMLDivElement, LemonTreeNodeProps>(
                             )
                         }
 
+                        let button = (
+                            <Link
+                                data-id={item.id}
+                                // When dragging, don't allow links to be clicked,
+                                // without this drag end would fire this href causing a reload
+                                to={isEmptyFolder ? '#' : item.record?.href || '#'}
path: frontend/src/lib/lemon-ui/LemonTree/LemonTree.tsx
user: login: greptile-apps[bot]
body: style: Using '#' as fallback href might cause page jumps - consider using 'javascript:void(0)' or preventing default in onClick
created_at: 2025-04-30T13:36:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -364,19 +363,21 @@ export function PanelLayoutNavBar({ children }: { children: React.ReactNode }):
                                                                     }
                                                                 }}
                                                             >
-                                                                <ButtonPrimitive
-                                                                    menuItem={!isLayoutNavCollapsed}
-                                                                    href={'to' in item ? item.to : undefined}
+                                                                <Link
                                                                     data-attr={`menu-item-${item.identifier
                                                                         .toString()
                                                                         .toLowerCase()}`}
-                                                                    className="group data-[focused=true]:bg-fill-button-tertiary-hover"
-                                                                    sideActionLeft={
-                                                                        item.sideAction && !isLayoutNavCollapsed
-                                                                            ? true
-                                                                            : false
-                                                                    }
-                                                                    iconOnly={isLayoutNavCollapsed}
+                                                                    buttonProps={{
+                                                                        menuItem: !isLayoutNavCollapsed,
+                                                                        className:
+                                                                            'group data-[focused=true]:bg-fill-button-tertiary-hover',
+                                                                        hasSideActionRight:
+                                                                            item.sideAction && !isLayoutNavCollapsed
+                                                                                ? true
+                                                                                : false,
+                                                                        iconOnly: isLayoutNavCollapsed,
path: frontend/src/layout/panel-layout/PanelLayoutNavBar.tsx
user: login: greptile-apps[bot]
body: style: hasSideActionRight is used here but isSideActionRight is used elsewhere in the codebase. Naming should be consistent.
created_at: 2025-04-30T13:36:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -553,19 +557,21 @@ export function PanelLayoutNavBar({ children }: { children: React.ReactNode }):
                                     <IconToolbar />
                                 </span>
                                 {!isLayoutNavCollapsed && 'Toolbar'}
-                            </ButtonPrimitive>
+                            </Link>
 
-                            <ButtonPrimitive
-                                menuItem={!isLayoutNavCollapsed}
+                            <Link
+                                buttonProps={{
+                                    menuItem: !isLayoutNavCollapsed,
+                                    className: 'group',
+                                    iconOnly: isLayoutNavCollapsed,
+                                }}
+                                to={urls.settings('project')}
+                                data-attr={Scene.Settings}
path: frontend/src/layout/panel-layout/PanelLayoutNavBar.tsx
user: login: greptile-apps[bot]
body: style: data-attr prop should be passed through buttonProps for consistency with other Link usages
created_at: 2025-04-30T13:36:20Z
author_association: CONTRIBUTOR
path: frontend/src/lib/ui/Button/ButtonPrimitives.css
user: login: greptile-apps[bot]
body: logic: These CSS variables are used across the codebase. Ensure they are still accessible to components that may have been importing them from this file.
created_at: 2025-04-30T13:36:50Z
author_association: CONTRIBUTOR
path: frontend/src/lib/ui/Button/ButtonPrimitives.css
user: login: greptile-apps[bot]
body: style: The .button-primitive class defines critical width behavior. Verify that moving this to SCSS doesn't affect any components that rely on this specific CSS cascade order.
created_at: 2025-04-30T13:36:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -124,11 +181,17 @@ export function Default(): JSX.Element {
 
             <DropdownMenu>
                 <ButtonGroupPrimitive size="base" variant="default" fullWidth>
path: frontend/src/lib/ui/Button/ButtonPrimitives.stories.tsx
user: login: greptile-apps[bot]
body: logic: groupVariant prop should be used instead of variant for ButtonGroupPrimitive to match other instances

```suggestion
                <ButtonGroupPrimitive size="base" groupVariant="default" fullWidth>
```
created_at: 2025-04-30T13:36:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,148 @@
+:root {
+    --button-padding-x-sm: 4px;
+    --button-padding-x-base: 6px;
+    --button-padding-x-lg: 8px;
+    --button-padding-y-sm: 3px;
+    --button-padding-y-base: 5px;
+    --button-padding-y-lg: 7px;
path: frontend/src/lib/ui/Button/ButtonPrimitives.scss
user: login: greptile-apps[bot]
body: style: Y-padding variables defined but never used in the styles. Consider removing or implementing them.
created_at: 2025-04-30T13:37:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,148 @@
+:root {
+    --button-padding-x-sm: 4px;
+    --button-padding-x-base: 6px;
+    --button-padding-x-lg: 8px;
+    --button-padding-y-sm: 3px;
+    --button-padding-y-base: 5px;
+    --button-padding-y-lg: 7px;
+    --button-height-sm: 26px;
+    --button-height-base: 30px;
+    --button-height-lg: 40px;
+}
+
+.button-primitive {
+    position: relative;
+    display: inline-flex;
+    align-items: center;
+    width: fit-content;
+    max-width: 100%;
+    font-weight: 400;
+    color: var(--text-primary);
+    cursor: pointer;
+    border-radius: var(--radius);
+
+    svg {
+        flex-shrink: 0;
+    }
+
+    &[aria-disabled='true'] {
+        cursor: not-allowed;
+        opacity: 0.5;
+    }
+}
+
+.button-primitive--variant-default {
+    border: 1px solid transparent;
+
+    &:not([aria-disabled='true']) {
+        &[data-active='true'],
+        &[data-current='true'],
+        &[data-state='open'],
+        &[data-state='checked'],
+        &[data-highlighted] {
+            background-color: var(--bg-fill-button-tertiary-active);
+        }
+
+        &[data-focused='true'],
+        &:hover {
+            background-color: var(--bg-fill-button-tertiary-hover);
+        }
+    }
+}
+
+.button-primitive--variant-outline {
+    border: 1px solid var(--border-secondary);
+
+    &:not([aria-disabled='true']) {
+        &[data-active='true'],
+        &[data-current='true'],
+        &[data-state='open'],
+        &[data-state='checked'],
+        &[data-highlighted] {
+            background-color: var(--bg-fill-button-tertiary-active);
+        }
+
+        &[data-focused='true'],
+        &:hover {
+            background-color: var(--bg-fill-button-tertiary-hover);
+        }
+    }
+}
+
+.button-primitive-group {
+    .button-primitive {
+        border-radius: 0;
+
+        &:first-child {
+            border-top-left-radius: var(--radius);
+            border-bottom-left-radius: var(--radius);
+        }
+
+        &:last-child {
+            border-top-right-radius: var(--radius);
+            border-bottom-right-radius: var(--radius);
+        }
+
+        &:not(:first-child) {
+            border-left-width: 0;
+        }
+    }
+
+    &:hover {
+        background-color: transparent !important;
+    }
path: frontend/src/lib/ui/Button/ButtonPrimitives.scss
user: login: greptile-apps[bot]
body: style: Using !important on hover background-color could make future style overrides difficult. Consider restructuring the specificity instead.
created_at: 2025-04-30T13:37:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,148 @@
+:root {
+    --button-padding-x-sm: 4px;
+    --button-padding-x-base: 6px;
+    --button-padding-x-lg: 8px;
+    --button-padding-y-sm: 3px;
+    --button-padding-y-base: 5px;
+    --button-padding-y-lg: 7px;
+    --button-height-sm: 26px;
+    --button-height-base: 30px;
+    --button-height-lg: 40px;
+}
+
+.button-primitive {
+    position: relative;
+    display: inline-flex;
+    align-items: center;
+    width: fit-content;
+    max-width: 100%;
+    font-weight: 400;
+    color: var(--text-primary);
+    cursor: pointer;
+    border-radius: var(--radius);
+
+    svg {
+        flex-shrink: 0;
+    }
+
+    &[aria-disabled='true'] {
+        cursor: not-allowed;
+        opacity: 0.5;
+    }
+}
+
+.button-primitive--variant-default {
+    border: 1px solid transparent;
+
+    &:not([aria-disabled='true']) {
+        &[data-active='true'],
+        &[data-current='true'],
+        &[data-state='open'],
+        &[data-state='checked'],
+        &[data-highlighted] {
+            background-color: var(--bg-fill-button-tertiary-active);
+        }
+
+        &[data-focused='true'],
+        &:hover {
+            background-color: var(--bg-fill-button-tertiary-hover);
+        }
+    }
+}
path: frontend/src/lib/ui/Button/ButtonPrimitives.scss
user: login: greptile-apps[bot]
body: style: Default variant styles are duplicated in outline variant. Consider creating a shared mixin or utility class.
created_at: 2025-04-30T13:37:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -180,25 +213,42 @@ export const Link: React.FC<LinkProps & React.RefAttributes<HTMLElement>> = Reac
                     (shouldOpenInDocsPanel && sidePanelStateLogic.isMounted() ? (
                         <IconOpenSidebar />
                     ) : target === '_blank' ? (
-                        <IconExternal />
+                        <IconExternal className={buttonProps ? 'size-3' : ''} />
                     ) : null)}
             </a>
-        ) : (
-            <Tooltip title={disabledReason ? <span className="italic">{disabledReason}</span> : undefined}>
-                <span>
-                    <button
-                        ref={ref as any}
-                        className={clsx('Link', subtle && 'Link--subtle', className)}
-                        onClick={onClick}
-                        type="button"
-                        disabled={disabled || !!disabledReason}
-                        {...props}
-                    >
-                        {children}
-                    </button>
-                </span>
-            </Tooltip>
         )
+
+        if (tooltip) {
+            element = (
+                <Tooltip title={tooltip} docLink={tooltipDocLink} placement={tooltipPlacement}>
+                    {element}
+                </Tooltip>
+            )
+        }
+
+        if (!to) {
path: frontend/src/lib/lemon-ui/Link/Link.tsx
user: login: greptile-apps[bot]
body: logic: The tooltip is rendered twice for non-link buttons - once in the outer wrapper and once in the inner tooltip component.
created_at: 2025-04-30T13:37:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -109,72 +109,25 @@ ButtonGroupPrimitive.displayName = 'ButtonGroupPrimitive'
 /*                              Button Base Component                         */
 /* -------------------------------------------------------------------------- */
 
-interface ButtonAsButtonProps extends ButtonBaseProps, React.ButtonHTMLAttributes<HTMLButtonElement> {
-    href?: never
-}
-
-interface ButtonAsAnchorProps extends ButtonBaseProps, Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {
-    href: string
-}
+export interface ButtonPrimitiveProps extends ButtonBaseProps, React.ButtonHTMLAttributes<HTMLButtonElement> {}
 
-type ButtonProps = ButtonAsButtonProps | ButtonAsAnchorProps
-
-export const buttonVariants = cva({
+export const buttonPrimitiveVariants = cva({
     base: `
         button-primitive
         group/button-primitive
-        cursor-pointer
-        inline-flex
-        relative
-        items-center
-        rounded-md
-        font-normal
-        aria-disabled:cursor-not-allowed
-        aria-disabled:opacity-50
-        text-current
-        [&_svg]:shrink-0
+
     `,
path: frontend/src/lib/ui/Button/ButtonPrimitives.tsx
user: login: greptile-apps[bot]
body: style: unnecessary empty lines in template literal
created_at: 2025-04-30T13:37:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -201,11 +154,11 @@ export const buttonVariants = cva({
             true: 'disabled:pointer-events-none disabled:opacity-50',
             false: '',
         },
-        sideActionLeft: {
+        hasSideActionRight: {
             true: 'rounded-md',
             false: '',
         },
-        sideActionRight: {
+        isSideActionRight: {
             true: 'absolute right-0 -top-px -bottom-px rounded-l-none',
path: frontend/src/lib/ui/Button/ButtonPrimitives.tsx
user: login: greptile-apps[bot]
body: logic: missing false case in isSideActionRight variant

```suggestion
            true: 'absolute right-0 -top-px -bottom-px rounded-l-none',
            false: '',
```
created_at: 2025-04-30T13:37:23Z
author_association: CONTRIBUTOR
path: frontend/__snapshots__/ui-buttonprimitive--default--light.png
user: login: mariusandra
body: I see some lines disappear. On purpose or accidentally? Both ways are fine with me at the end of the day
created_at: 2025-04-30T15:32:19Z
author_association: COLLABORATOR
diff_hunk: @@ -532,18 +536,19 @@ export function PanelLayoutNavBar({ children }: { children: React.ReactNode }):
                                     {!isLayoutNavCollapsed && 'Quick start'}
                                 </ButtonPrimitive>
                             )}
-                            <ButtonPrimitive
-                                menuItem={!isLayoutNavCollapsed}
+                            <Link
+                                buttonProps={{
+                                    menuItem: !isLayoutNavCollapsed,
+                                    className: 'group',
+                                    iconOnly: isLayoutNavCollapsed,
+                                }}
+                                to={urls.toolbarLaunch()}
                                 onClick={() => {
                                     handleStaticNavbarItemClick(urls.toolbarLaunch(), true)
                                 }}
-                                href={urls.toolbarLaunch()}
-                                data-attr={Scene.ToolbarLaunch}
                                 tooltip={isLayoutNavCollapsed ? 'Toolbar' : undefined}
path: frontend/src/layout/panel-layout/PanelLayoutNavBar.tsx
user: login: graphite-app[bot]
body: The `data-attr={Scene.ToolbarLaunch}` attribute is present in the original `ButtonPrimitive` implementation but missing in this new `Link` component. This attribute should be added to maintain analytics tracking and automated testing functionality. The Settings link below correctly includes its data-attr, suggesting this was an oversight during conversion.
```suggestion
                            <Link
                                buttonProps={{
                                    menuItem: !isLayoutNavCollapsed,
                                    className: 'group',
                                    iconOnly: isLayoutNavCollapsed,
                                }}
                                to={urls.toolbarLaunch()}
                                data-attr={Scene.ToolbarLaunch}
                                onClick={() => {
                                    handleStaticNavbarItemClick(urls.toolbarLaunch(), true)
                                }}
                                tooltip={isLayoutNavCollapsed ? 'Toolbar' : undefined}
```
  

*Spotted by [Diamond](https://app.graphite.dev/diamond/?org=PostHog&ref=ai-review-comment)*<i class='graphite__hidden'><br /><br />Is this helpful? React 👍 or 👎 to let us know.</i>
created_at: 2025-04-30T16:03:21Z
author_association: CONTRIBUTOR

PR comments:
user: login: posthog-bot
created_at: 2025-04-30T13:45:58Z
body: ## 📸 UI snapshots have been updated

**4** snapshot changes in total. **0** added, **4** modified, **0** deleted:

- **`chromium`**: **0** added, **4** modified, **0** deleted ([diff for shard 3](https://github.com/PostHog/posthog/pull/31768/commits/e8be4267dfe52c7ad3dbff04992f14b9377e8843), [diff for shard 9](https://github.com/PostHog/posthog/pull/31768/commits/9985792ad21c54785ff14950362b1090b4bd2086))
- `webkit`: **0** added, **0** modified, **0** deleted

Triggered by [this commit](https://github.com/PostHog/posthog/pull/31768/commits/6f84a6415a7ab2e9e1ed9ba32bd5543c852e8c55).

👉 **[Review this PR's diff of snapshots.](https://github.com/PostHog/posthog/pull/31768/files#:~:text=frontend/__snapshots__/)**
author_association: CONTRIBUTOR
user: login: posthog-bot
created_at: 2025-04-30T15:13:17Z
body: ## 📸 UI snapshots have been updated

**2** snapshot changes in total. **0** added, **2** modified, **0** deleted:

- **`chromium`**: **0** added, **2** modified, **0** deleted ([diff for shard 3](https://github.com/PostHog/posthog/pull/31768/commits/2798e2f257353fab5f4e992c8414ea8039a49e19))
- `webkit`: **0** added, **0** modified, **0** deleted

Triggered by [this commit](https://github.com/PostHog/posthog/pull/31768/commits/769aa5b797176277ab9b5c6650b6f84b11e35b1f).

👉 **[Review this PR's diff of snapshots.](https://github.com/PostHog/posthog/pull/31768/files#:~:text=frontend/__snapshots__/)**
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-04-30T15:24:07Z
body: **Size Change:** -1.5 kB (-0.01%) 

**Total Size:** 13.7 MB

| Filename | Size | Change |
| :--- | :---: | :---: |
| `frontend/dist/toolbar.js` | 13.7 MB | -1.5 kB (-0.01%) |

<a href="https://github.com/preactjs/compressed-size-action"><sub>compressed-size-action</sub></a>
author_association: CONTRIBUTOR
user: login: posthog-bot
created_at: 2025-04-30T15:35:06Z
body: ## 📸 UI snapshots have been updated

**2** snapshot changes in total. **0** added, **2** modified, **0** deleted:

- **`chromium`**: **0** added, **2** modified, **0** deleted ([diff for shard 3](https://github.com/PostHog/posthog/pull/31768/commits/1406858ce15ff985aaeb2dbed46cafdc49c19278))
- `webkit`: **0** added, **0** modified, **0** deleted

Triggered by [this commit](https://github.com/PostHog/posthog/pull/31768/commits/71fa35bae744b2e7645559544c9c987fd57a2f30).

👉 **[Review this PR's diff of snapshots.](https://github.com/PostHog/posthog/pull/31768/files#:~:text=frontend/__snapshots__/)**
author_association: CONTRIBUTOR
user: login: posthog-bot
created_at: 2025-04-30T16:19:14Z
body: ## 📸 UI snapshots have been updated

**2** snapshot changes in total. **0** added, **2** modified, **0** deleted:

- **`chromium`**: **0** added, **2** modified, **0** deleted ([diff for shard 9](https://github.com/PostHog/posthog/pull/31768/commits/e23b7cc044325f0a2f9e847d742de1158a5f95c5))
- `webkit`: **0** added, **0** modified, **0** deleted

_Triggered by [this commit](https://github.com/PostHog/posthog/pull/31768/commits/30f99621b46e6201189949ac5661060b3b91593f)._

👉 **[Review this PR's diff of snapshots.](https://github.com/PostHog/posthog/pull/31768/files#:~:text=frontend/__snapshots__/)**
author_association: CONTRIBUTOR