diff --git a/packages/desktop-client/.gitignore b/packages/desktop-client/.gitignore
index 08fad3425bdb40c078f5480fe8783f153e5508c7..f635ae3caaa6f9d9d8a8bacf5108fcda8e54d539 100644
--- a/packages/desktop-client/.gitignore
+++ b/packages/desktop-client/.gitignore
@@ -6,6 +6,7 @@ node_modules
 # testing
 coverage
 test-results
+playwright-report
 
 # production
 build
diff --git a/packages/desktop-client/e2e/page-models/mobile-accounts-page.js b/packages/desktop-client/e2e/page-models/mobile-accounts-page.js
index f91bb147fd36626d49dab901ad4ade5157da0c0e..2a64c3e55316b6c0931ba4b454756a131762a17d 100644
--- a/packages/desktop-client/e2e/page-models/mobile-accounts-page.js
+++ b/packages/desktop-client/e2e/page-models/mobile-accounts-page.js
@@ -23,7 +23,7 @@ export class MobileAccountsPage {
    * Click on the n-th account to open it up
    */
   async openNthAccount(idx) {
-    await this.accounts.nth(idx).getByRole('button').click();
+    await this.accounts.nth(idx).click();
 
     return new MobileAccountPage(this.page);
   }
diff --git a/packages/desktop-client/playwright.config.js b/packages/desktop-client/playwright.config.js
index f8ba4d8f2fe7e67282be2b3cf3e58cedcb517d58..1bf7e2cf40af7d693867c8b0e94487199db383a6 100644
--- a/packages/desktop-client/playwright.config.js
+++ b/packages/desktop-client/playwright.config.js
@@ -57,9 +57,7 @@ export default defineConfig({
   timeout: 20000, // 20 seconds
   retries: 1,
   testDir: 'e2e/',
-  reporter: !process.env.CI
-    ? [['html', { open: 'never', outputFolder: 'test-results/html' }]]
-    : undefined,
+  reporter: !process.env.CI ? [['html', { open: 'never' }]] : undefined,
   use: {
     userAgent: 'playwright',
     screenshot: 'on',
diff --git a/packages/desktop-client/src/components/Titlebar.tsx b/packages/desktop-client/src/components/Titlebar.tsx
index 8179cb8854262b414df47780a54a455482f56a2e..c283886eaa1d02a687de46063efe1443910cc30c 100644
--- a/packages/desktop-client/src/components/Titlebar.tsx
+++ b/packages/desktop-client/src/components/Titlebar.tsx
@@ -44,7 +44,7 @@ function UncategorizedButton() {
   return (
     <Link
       variant="button"
-      type="bare"
+      buttonVariant="bare"
       to="/accounts/uncategorized"
       style={{
         color: theme.errorText,
diff --git a/packages/desktop-client/src/components/common/Link.tsx b/packages/desktop-client/src/components/common/Link.tsx
index 4795ccf218e8a7144c67a1c0a7b8400f94e9204f..a945e95570b0c09dbe26ab749e9c6240aef09aa3 100644
--- a/packages/desktop-client/src/components/common/Link.tsx
+++ b/packages/desktop-client/src/components/common/Link.tsx
@@ -13,7 +13,7 @@ import { useNavigate } from '../../hooks/useNavigate';
 import { type CSSProperties, styles } from '../../style';
 import { theme } from '../../style/theme';
 
-import { Button } from './Button';
+import { Button } from './Button2';
 import { Text } from './Text';
 
 type TextLinkProps = {
@@ -22,7 +22,8 @@ type TextLinkProps = {
   children?: ReactNode;
 };
 
-type ButtonLinkProps = ComponentProps<typeof Button> & {
+type ButtonLinkProps = Omit<ComponentProps<typeof Button>, 'variant'> & {
+  buttonVariant?: ComponentProps<typeof Button>['variant'];
   to?: string;
   activeStyle?: CSSProperties;
 };
@@ -94,17 +95,15 @@ const ButtonLink = ({ to, style, activeStyle, ...props }: ButtonLinkProps) => {
   const match = useMatch({ path });
   return (
     <Button
-      style={{
+      style={({ isPressed }) => ({
         ...style,
-        ...(match ? activeStyle : {}),
-      }}
-      activeStyle={activeStyle}
+        ...(match || isPressed ? activeStyle : {}),
+      })}
       {...props}
-      onClick={e => {
-        props.onClick?.(e);
-        if (!e.defaultPrevented) {
-          navigate(path);
-        }
+      variant={props.buttonVariant}
+      onPress={e => {
+        props.onPress?.(e);
+        navigate(path);
       }}
     />
   );
@@ -136,28 +135,34 @@ const InternalLink = ({
 };
 
 type LinkProps =
-  | ({
-      variant: 'button';
-    } & ButtonLinkProps)
-  | ({ variant?: 'internal' } & InternalLinkProps)
-  | ({ variant?: 'external' } & ExternalLinkProps)
-  | ({ variant?: 'text' } & TextLinkProps);
-
-export function Link({ variant = 'internal', ...props }: LinkProps) {
-  switch (variant) {
-    case 'internal':
-      return <InternalLink {...props} />;
-
-    case 'external':
-      return <ExternalLink {...props} />;
-
-    case 'button':
-      return <ButtonLink {...props} />;
-
-    case 'text':
-      return <TextLink {...props} />;
+  | ({ variant: 'button' } & ButtonLinkProps)
+  | ({ variant: 'internal' } & InternalLinkProps)
+  | ({ variant: 'external' } & ExternalLinkProps)
+  | ({ variant: 'text' } & TextLinkProps);
+
+export function Link(props: LinkProps) {
+  switch (props.variant) {
+    case 'internal': {
+      const { variant: _, ...internalProps } = props;
+      return <InternalLink {...internalProps} />;
+    }
+
+    case 'external': {
+      const { variant: _, ...externalProps } = props;
+      return <ExternalLink {...externalProps} />;
+    }
+
+    case 'button': {
+      const { variant: _, ...buttonProps } = props;
+      return <ButtonLink {...buttonProps} />;
+    }
+
+    case 'text': {
+      const { variant: _, ...textProps } = props;
+      return <TextLink {...textProps} />;
+    }
 
     default:
-      throw new Error(`Unrecognised link type: ${variant}`);
+      throw new Error(`Unrecognised Link variant.`);
   }
 }
diff --git a/packages/desktop-client/src/components/common/Modal.tsx b/packages/desktop-client/src/components/common/Modal.tsx
index e3a65d694b2adef0384767acd945c5d114c287c6..2e7636e351ce9be42bb38767b69aa83a0c912f33 100644
--- a/packages/desktop-client/src/components/common/Modal.tsx
+++ b/packages/desktop-client/src/components/common/Modal.tsx
@@ -20,7 +20,7 @@ import { SvgDelete } from '../../icons/v0';
 import { type CSSProperties, styles, theme } from '../../style';
 import { tokens } from '../../tokens';
 
-import { Button } from './Button';
+import { Button } from './Button2';
 import { Input } from './Input';
 import { Text } from './Text';
 import { TextOneLine } from './TextOneLine';
@@ -202,7 +202,7 @@ export const Modal = ({
                   right: 0,
                 }}
               >
-                <CloseButtonComponent onClick={onClose} />
+                <CloseButtonComponent onPress={onClose} />
               </View>
             )}
           </View>
@@ -463,15 +463,15 @@ function ModalTitle({
 }
 
 type ModalCloseButtonProps = {
-  onClick: ComponentProps<typeof Button>['onClick'];
+  onPress: ComponentProps<typeof Button>['onPress'];
   style?: CSSProperties;
 };
 
-function ModalCloseButton({ onClick, style }: ModalCloseButtonProps) {
+function ModalCloseButton({ onPress, style }: ModalCloseButtonProps) {
   return (
     <Button
-      type="bare"
-      onClick={onClick}
+      variant="bare"
+      onPress={onPress}
       style={{ padding: '10px 10px' }}
       aria-label="Close"
     >
diff --git a/packages/desktop-client/src/components/common/Modal2.tsx b/packages/desktop-client/src/components/common/Modal2.tsx
index 2de38db98014f0300b5139f2fd25765cace79da3..c7e0cdc40bc2285e6732543218f9901a208cee20 100644
--- a/packages/desktop-client/src/components/common/Modal2.tsx
+++ b/packages/desktop-client/src/components/common/Modal2.tsx
@@ -24,7 +24,7 @@ import { SvgDelete } from '../../icons/v0';
 import { type CSSProperties, styles, theme } from '../../style';
 import { tokens } from '../../tokens';
 
-import { Button } from './Button';
+import { Button } from './Button2';
 import { Input } from './Input';
 import { Text } from './Text';
 import { TextOneLine } from './TextOneLine';
@@ -450,15 +450,15 @@ export function ModalTitle({
 }
 
 type ModalCloseButtonProps = {
-  onClick: ComponentPropsWithoutRef<typeof Button>['onClick'];
+  onClick: ComponentPropsWithoutRef<typeof Button>['onPress'];
   style?: CSSProperties;
 };
 
 export function ModalCloseButton({ onClick, style }: ModalCloseButtonProps) {
   return (
     <Button
-      type="bare"
-      onClick={onClick}
+      variant="bare"
+      onPress={onClick}
       style={{ padding: '10px 10px' }}
       aria-label="Close"
     >
diff --git a/packages/desktop-client/src/components/common/Search.tsx b/packages/desktop-client/src/components/common/Search.tsx
index 1cf98e8bf057ea75af9776b5a668890f5f970071..6f9c2c469f0423fb4139446542018ff21f718e7c 100644
--- a/packages/desktop-client/src/components/common/Search.tsx
+++ b/packages/desktop-client/src/components/common/Search.tsx
@@ -3,8 +3,9 @@ import { type Ref } from 'react';
 import { SvgRemove, SvgSearchAlternate } from '../../icons/v2';
 import { theme } from '../../style';
 
-import { Button } from './Button';
+import { Button } from './Button2';
 import { InputWithContent } from './InputWithContent';
+import { View } from './View';
 
 type SearchProps = {
   inputRef?: Ref<HTMLInputElement>;
@@ -54,14 +55,15 @@ export function Search({
       }
       rightContent={
         value && (
-          <Button
-            type="bare"
-            style={{ padding: 8 }}
-            onClick={() => onChange('')}
-            title="Clear search term"
-          >
-            <SvgRemove style={{ width: 8, height: 8 }} />
-          </Button>
+          <View title="Clear search term">
+            <Button
+              variant="bare"
+              style={{ padding: 8 }}
+              onPress={() => onChange('')}
+            >
+              <SvgRemove style={{ width: 8, height: 8 }} />
+            </Button>
+          </View>
         )
       }
       inputStyle={{
diff --git a/packages/desktop-client/src/components/common/Select.tsx b/packages/desktop-client/src/components/common/Select.tsx
index 09db132168378d1f770ff77ab4c2458c7c566adc..27b6747914a54bd80956b6905198114534664d12 100644
--- a/packages/desktop-client/src/components/common/Select.tsx
+++ b/packages/desktop-client/src/components/common/Select.tsx
@@ -3,7 +3,7 @@ import { useRef, useState } from 'react';
 import { SvgExpandArrow } from '../../icons/v0';
 import { type CSSProperties } from '../../style';
 
-import { Button } from './Button';
+import { Button } from './Button2';
 import { Menu } from './Menu';
 import { Popover } from './Popover';
 import { View } from './View';
@@ -64,16 +64,17 @@ export function Select<const Value = string>({
       <Button
         ref={triggerRef}
         id={id}
-        type={bare ? 'bare' : 'normal'}
-        disabled={disabled}
-        onClick={() => {
+        variant={bare ? 'bare' : 'normal'}
+        isDisabled={disabled}
+        onPress={() => {
           setIsOpen(true);
         }}
-        style={buttonStyle}
-        hoveredStyle={{
-          backgroundColor: bare ? 'transparent' : undefined,
+        style={({ isHovered }) => ({
           ...buttonStyle,
-        }}
+          ...(isHovered
+            ? { backgroundColor: bare ? 'transparent' : undefined }
+            : {}),
+        })}
       >
         <View
           style={{
diff --git a/packages/desktop-client/src/components/manager/ServerURL.tsx b/packages/desktop-client/src/components/manager/ServerURL.tsx
index 89e3dfebd0cf39ac32d5ddbc9f2624dcba71ef8f..4979e8d19b442d8539eb51eaec74d5bf5bf17e60 100644
--- a/packages/desktop-client/src/components/manager/ServerURL.tsx
+++ b/packages/desktop-client/src/components/manager/ServerURL.tsx
@@ -30,7 +30,7 @@ export function ServerURL() {
           <strong>No server configured</strong>
         )}
       </Text>
-      <Link to="/config-server" style={{ marginLeft: 15 }}>
+      <Link variant="internal" to="/config-server" style={{ marginLeft: 15 }}>
         Change
       </Link>
     </View>
diff --git a/packages/desktop-client/src/components/manager/subscribe/Login.tsx b/packages/desktop-client/src/components/manager/subscribe/Login.tsx
index ba9546f796356eaee9d737ffb3f46836bf871f81..d81f9a52a8526ba0dd4952735a9a36d43967c56e 100644
--- a/packages/desktop-client/src/components/manager/subscribe/Login.tsx
+++ b/packages/desktop-client/src/components/manager/subscribe/Login.tsx
@@ -149,7 +149,7 @@ export function Login() {
           {error && (
             <Link
               variant="button"
-              type="primary"
+              buttonVariant="primary"
               style={{ fontSize: 15 }}
               to={'/login/password?error=' + error}
             >
diff --git a/packages/desktop-client/src/components/mobile/MobileBackButton.tsx b/packages/desktop-client/src/components/mobile/MobileBackButton.tsx
index cc4a93113d5c78656cae7a2bdf2e33a99fce3c1c..aa1604bb6a2bfaae847c2f3f289c38953c671c8a 100644
--- a/packages/desktop-client/src/components/mobile/MobileBackButton.tsx
+++ b/packages/desktop-client/src/components/mobile/MobileBackButton.tsx
@@ -1,35 +1,38 @@
-import React from 'react';
+import React, { type ComponentPropsWithoutRef } from 'react';
 
 import { useNavigate } from '../../hooks/useNavigate';
 import { SvgCheveronLeft } from '../../icons/v1';
-import { type CSSProperties, styles, theme } from '../../style';
-import { Button } from '../common/Button';
+import { styles, theme } from '../../style';
+import { Button } from '../common/Button2';
 import { Text } from '../common/Text';
 
-type MobileBackButtonProps = {
-  onClick?: () => void;
-  style?: CSSProperties;
-};
+type MobileBackButtonProps = ComponentPropsWithoutRef<typeof Button>;
 
-export function MobileBackButton({ onClick, style }: MobileBackButtonProps) {
+export function MobileBackButton({
+  onPress,
+  style,
+  ...props
+}: MobileBackButtonProps) {
   const navigate = useNavigate();
   return (
     <Button
-      type="bare"
-      aria-label="Back"
-      style={{
+      variant="bare"
+      style={({ isHovered }) => ({
         color: theme.mobileHeaderText,
         justifyContent: 'center',
         margin: 10,
         paddingLeft: 5,
         paddingRight: 3,
+        ...(isHovered
+          ? {
+              color: theme.mobileHeaderText,
+              background: theme.mobileHeaderTextHover,
+            }
+          : {}),
         ...style,
-      }}
-      hoveredStyle={{
-        color: theme.mobileHeaderText,
-        background: theme.mobileHeaderTextHover,
-      }}
-      onClick={onClick || (() => navigate(-1))}
+      })}
+      onPress={onPress || (() => navigate(-1))}
+      {...props}
     >
       <SvgCheveronLeft
         style={{ width: 30, height: 30, margin: -10, marginLeft: -5 }}
diff --git a/packages/desktop-client/src/components/mobile/accounts/Account.jsx b/packages/desktop-client/src/components/mobile/accounts/Account.jsx
index 129b0765980ecb865d077c69e27b8ba88d70e078..244ceb99b999c1a98daba6453bec3e98aab22d59 100644
--- a/packages/desktop-client/src/components/mobile/accounts/Account.jsx
+++ b/packages/desktop-client/src/components/mobile/accounts/Account.jsx
@@ -8,7 +8,7 @@ import { useNavigate } from '../../../hooks/useNavigate';
 import { useSetThemeColor } from '../../../hooks/useSetThemeColor';
 import { useSyncedPref } from '../../../hooks/useSyncedPref';
 import { theme, styles } from '../../../style';
-import { Button } from '../../common/Button';
+import { Button } from '../../common/Button2';
 import { Text } from '../../common/Text';
 import { View } from '../../common/View';
 
@@ -45,9 +45,9 @@ export function Account() {
           There is no Mobile View at the moment
         </Text>
         <Button
-          type="normal"
+          variant="normal"
           style={{ fontSize: 15, marginLeft: 10, marginTop: 10 }}
-          onClick={() => navigate('/accounts')}
+          onPress={() => navigate('/accounts')}
         >
           Go back to Mobile Accounts
         </Button>
diff --git a/packages/desktop-client/src/components/mobile/accounts/Accounts.jsx b/packages/desktop-client/src/components/mobile/accounts/Accounts.jsx
index a81c9cdd653f5d29925bb0d4369aae36b85d48ad..10fa6a20742a0eb3d23cfa05ff9cc4ce5e0d5a74 100644
--- a/packages/desktop-client/src/components/mobile/accounts/Accounts.jsx
+++ b/packages/desktop-client/src/components/mobile/accounts/Accounts.jsx
@@ -1,6 +1,8 @@
 import React from 'react';
 import { useDispatch, useSelector } from 'react-redux';
 
+import { css } from 'glamor';
+
 import { replaceModal, syncAndDownload } from 'loot-core/src/client/actions';
 import * as queries from 'loot-core/src/client/queries';
 
@@ -64,84 +66,67 @@ function AccountCard({
   onSelect,
 }) {
   return (
-    <View
+    <Button
+      onPress={() => onSelect(account.id)}
       style={{
-        flex: 1,
-        flexDirection: 'row',
-        backgroundColor: theme.tableBackground,
-        boxShadow: `0 1px 1px ${theme.mobileAccountShadow}`,
+        border: `1px solid ${theme.pillBorder}`,
         borderRadius: 6,
+        boxShadow: `0 1px 1px ${theme.mobileAccountShadow}`,
         marginTop: 10,
-        marginRight: 10,
-        width: '100%',
       }}
       data-testid="account"
     >
-      <Button
-        onPress={() => onSelect(account.id)}
-        style={({ isPressed }) => ({
-          flexDirection: 'row',
-          border: '1px solid ' + theme.pillBorder,
+      <View
+        style={{
           flex: 1,
-          alignItems: 'center',
-          borderRadius: 6,
-          ...(isPressed && {
-            opacity: 0.1,
-          }),
-        })}
+          margin: '10px 0',
+        }}
       >
         <View
           style={{
-            flex: 1,
-            margin: '10px 0',
+            flexDirection: 'row',
+            alignItems: 'center',
           }}
         >
-          <View
+          {account.bankId && (
+            <View
+              style={{
+                backgroundColor: pending
+                  ? theme.sidebarItemBackgroundPending
+                  : failed
+                    ? theme.sidebarItemBackgroundFailed
+                    : theme.sidebarItemBackgroundPositive,
+                marginRight: '8px',
+                width: 8,
+                flexShrink: 0,
+                height: 8,
+                borderRadius: 8,
+                opacity: connected ? 1 : 0,
+              }}
+            />
+          )}
+          <TextOneLine
             style={{
-              flexDirection: 'row',
-              alignItems: 'center',
+              ...styles.text,
+              fontSize: 17,
+              fontWeight: 600,
+              color: updated ? theme.mobileAccountText : theme.pillText,
+              paddingRight: 30,
             }}
+            data-testid="account-name"
           >
-            {account.bankId && (
-              <View
-                style={{
-                  backgroundColor: pending
-                    ? theme.sidebarItemBackgroundPending
-                    : failed
-                      ? theme.sidebarItemBackgroundFailed
-                      : theme.sidebarItemBackgroundPositive,
-                  marginRight: '8px',
-                  width: 8,
-                  flexShrink: 0,
-                  height: 8,
-                  borderRadius: 8,
-                  opacity: connected ? 1 : 0,
-                }}
-              />
-            )}
-            <TextOneLine
-              style={{
-                ...styles.text,
-                fontSize: 17,
-                fontWeight: 600,
-                color: updated ? theme.mobileAccountText : theme.pillText,
-                paddingRight: 30,
-              }}
-              data-testid="account-name"
-            >
-              {account.name}
-            </TextOneLine>
-          </View>
+            {account.name}
+          </TextOneLine>
         </View>
-        <CellValue
-          binding={getBalanceQuery(account)}
-          type="financial"
-          style={{ fontSize: 16, color: 'inherit' }}
-          getStyle={makeAmountFullStyle}
-          data-testid="account-balance"
-        />
-      </Button>
-    </View>
+      </View>
+      <CellValue
+        binding={getBalanceQuery(account)}
+        type="financial"
+        style={{ fontSize: 16, color: 'inherit' }}
+        getStyle={makeAmountFullStyle}
+        data-testid="account-balance"
+      />
+    </Button>
   );
 }
 
@@ -171,10 +156,6 @@ function AccountList({
   const syncingAccountIds = useSelector(state => state.account.accountsSyncing);
   const budgetedAccounts = accounts.filter(account => account.offbudget === 0);
   const offbudgetAccounts = accounts.filter(account => account.offbudget === 1);
-  const noBackgroundColorStyle = {
-    backgroundColor: 'transparent',
-    color: 'white',
-  };
 
   return (
     <Page
@@ -184,11 +165,18 @@ function AccountList({
           rightContent={
             <Button
               variant="bare"
-              style={({ isHovered, isPressed }) => ({
-                color: theme.mobileHeaderText,
-                margin: 10,
-                ...(isHovered || isPressed ? noBackgroundColorStyle : {}),
-              })}
+              aria-label="Add account"
+              className={String(
+                css({
+                  justifyContent: 'center',
+                  color: theme.mobileHeaderText,
+                  margin: 10,
+                  ':hover': {
+                    color: theme.mobileHeaderText,
+                    background: theme.mobileHeaderTextHover,
+                  },
+                }),
+              )}
               onPress={onAddAccount}
             >
               <SvgAdd width={20} height={20} />
diff --git a/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx b/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx
index 893ee2f56b4f016826b12bb82a0aeeca6be4c375..2961520b0ef2b92dac699b9915fbb1dba3e73ce3 100644
--- a/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx
+++ b/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx
@@ -1,4 +1,4 @@
-import React, { memo, useRef } from 'react';
+import React, { memo, useCallback, useRef } from 'react';
 import { useDispatch } from 'react-redux';
 
 import { AutoTextSize } from 'auto-text-size';
@@ -364,13 +364,16 @@ const ExpenseCategory = memo(function ExpenseCategory({
   const { list: categories } = useCategories();
   const categoriesById = groupById(categories);
 
-  const onCarryover = carryover => {
-    onBudgetAction(month, 'carryover', {
-      category: category.id,
-      flag: carryover,
-    });
-    dispatch(collapseModals(`${budgetType}-balance-menu`));
-  };
+  const onCarryover = useCallback(
+    carryover => {
+      onBudgetAction(month, 'carryover', {
+        category: category.id,
+        flag: carryover,
+      });
+      dispatch(collapseModals(`${budgetType}-balance-menu`));
+    },
+    [budgetType, category.id, dispatch, month, onBudgetAction],
+  );
 
   const catBalance = useSheetValue(
     type === 'rollover'
@@ -386,7 +389,7 @@ const ExpenseCategory = memo(function ExpenseCategory({
       : budgetedtmp
     : null;
 
-  const onTransfer = () => {
+  const onTransfer = useCallback(() => {
     dispatch(
       pushModal('transfer', {
         title: category.name,
@@ -406,9 +409,19 @@ const ExpenseCategory = memo(function ExpenseCategory({
         showToBeBudgeted: true,
       }),
     );
-  };
-
-  const onCover = () => {
+  }, [
+    budgetType,
+    catBalance,
+    categoriesById,
+    category.id,
+    category.name,
+    dispatch,
+    month,
+    onBudgetAction,
+    showUndoNotification,
+  ]);
+
+  const onCover = useCallback(() => {
     dispatch(
       pushModal('cover', {
         title: category.name,
@@ -426,9 +439,18 @@ const ExpenseCategory = memo(function ExpenseCategory({
         },
       }),
     );
-  };
-
-  const onOpenBalanceMenu = () => {
+  }, [
+    budgetType,
+    categoriesById,
+    category.id,
+    category.name,
+    dispatch,
+    month,
+    onBudgetAction,
+    showUndoNotification,
+  ]);
+
+  const onOpenBalanceMenu = useCallback(() => {
     dispatch(
       pushModal(`${budgetType}-balance-menu`, {
         categoryId: category.id,
@@ -437,14 +459,22 @@ const ExpenseCategory = memo(function ExpenseCategory({
         ...(budgetType === 'rollover' && { onTransfer, onCover }),
       }),
     );
-  };
+  }, [
+    budgetType,
+    category.id,
+    dispatch,
+    month,
+    onCarryover,
+    onCover,
+    onTransfer,
+  ]);
 
   const listItemRef = useRef();
   const format = useFormat();
   const navigate = useNavigate();
-  const onShowActivity = () => {
+  const onShowActivity = useCallback(() => {
     navigate(`/categories/${category.id}?month=${month}`);
-  };
+  }, [category.id, month, navigate]);
 
   const sidebarColumnWidth = getColumnWidth({ show3Cols, isSidebar: true });
   const columnWidth = getColumnWidth({ show3Cols });
diff --git a/packages/desktop-client/src/components/mobile/transactions/AddTransactionButton.tsx b/packages/desktop-client/src/components/mobile/transactions/AddTransactionButton.tsx
index a37822163f2b41c374ed6e171986ddb9eb438854..f77817a2e09057400dba04183cdfd76e82255524 100644
--- a/packages/desktop-client/src/components/mobile/transactions/AddTransactionButton.tsx
+++ b/packages/desktop-client/src/components/mobile/transactions/AddTransactionButton.tsx
@@ -1,9 +1,11 @@
 import React from 'react';
 
+import { css } from 'glamor';
+
 import { useNavigate } from '../../../hooks/useNavigate';
 import { SvgAdd } from '../../../icons/v1';
 import { theme } from '../../../style';
-import { Button } from '../../common/Button';
+import { Button } from '../../common/Button2';
 
 type AddTransactionButtonProps = {
   to: string;
@@ -19,18 +21,20 @@ export function AddTransactionButton({
   const navigate = useNavigate();
   return (
     <Button
-      type="bare"
+      variant="bare"
       aria-label="Add transaction"
-      style={{
-        justifyContent: 'center',
-        color: theme.mobileHeaderText,
-        margin: 10,
-      }}
-      hoveredStyle={{
-        color: theme.mobileHeaderText,
-        background: theme.mobileHeaderTextHover,
-      }}
-      onClick={() => {
+      className={String(
+        css({
+          justifyContent: 'center',
+          color: theme.mobileHeaderText,
+          margin: 10,
+          ':hover': {
+            color: theme.mobileHeaderText,
+            background: theme.mobileHeaderTextHover,
+          },
+        }),
+      )}
+      onPress={() => {
         navigate(to, { state: { accountId, categoryId } });
       }}
     >
diff --git a/packages/desktop-client/src/components/mobile/transactions/FocusableAmountInput.tsx b/packages/desktop-client/src/components/mobile/transactions/FocusableAmountInput.tsx
index ad89d0b0a126a46c05a288ab42ffc3f42cf96e9b..bc4b67b637dad45308b958c3cb00f2e60b756b62 100644
--- a/packages/desktop-client/src/components/mobile/transactions/FocusableAmountInput.tsx
+++ b/packages/desktop-client/src/components/mobile/transactions/FocusableAmountInput.tsx
@@ -8,6 +8,8 @@ import React, {
   useState,
 } from 'react';
 
+import { css } from 'glamor';
+
 import {
   amountToCurrency,
   appendDecimals,
@@ -18,7 +20,7 @@ import { useMergedRefs } from '../../../hooks/useMergedRefs';
 import { useSyncedPref } from '../../../hooks/useSyncedPref';
 import { type CSSProperties, theme } from '../../../style';
 import { makeAmountFullStyle } from '../../budget/util';
-import { Button } from '../../common/Button';
+import { Button } from '../../common/Button2';
 import { Text } from '../../common/Text';
 import { View } from '../../common/View';
 
@@ -240,24 +242,26 @@ export const FocusableAmountInput = memo(function FocusableAmountInput({
               right: 'calc(100% + 5px)',
               top: '8px',
             }}
-            onClick={toggleIsNegative}
+            onPress={toggleIsNegative}
           >
             {isNegative ? '-' : '+'}
           </Button>
         )}
         <Button
-          onClick={onFocus}
+          onPress={onFocus}
           // Defines how far touch can start away from the button
           // hitSlop={{ top: 5, bottom: 5, left: 5, right: 5 }}
           {...buttonProps}
-          style={{
-            ...(buttonProps && buttonProps.style),
-            ...(focused && { display: 'none' }),
-            ':hover': {
-              backgroundColor: 'transparent',
-            },
-          }}
-          type="bare"
+          className={String(
+            css({
+              ...(buttonProps && buttonProps.style),
+              ...(focused && { display: 'none' }),
+              ':hover': {
+                backgroundColor: 'transparent',
+              },
+            }),
+          )}
+          variant="bare"
         >
           <View
             style={{
diff --git a/packages/desktop-client/src/components/modals/CoverModal.tsx b/packages/desktop-client/src/components/modals/CoverModal.tsx
index 70ff82bd5a104d127adfa1b1a1e925c3c11dc6ce..cdc9404a1a44038dd3dd08c42aa3c974d1b880c3 100644
--- a/packages/desktop-client/src/components/modals/CoverModal.tsx
+++ b/packages/desktop-client/src/components/modals/CoverModal.tsx
@@ -1,4 +1,4 @@
-import React, { useCallback, useEffect, useMemo, useState } from 'react';
+import React, { useCallback, useMemo, useState } from 'react';
 import { useDispatch } from 'react-redux';
 
 import { pushModal } from 'loot-core/client/actions';
@@ -8,7 +8,6 @@ import {
 } from 'loot-core/src/types/models';
 
 import { useCategories } from '../../hooks/useCategories';
-import { useInitialMount } from '../../hooks/useInitialMount';
 import { styles } from '../../style';
 import { addToBeBudgetedGroup } from '../budget/util';
 import { Button } from '../common/Button2';
@@ -85,14 +84,6 @@ export function CoverModal({
     }
   };
 
-  const initialMount = useInitialMount();
-
-  useEffect(() => {
-    if (initialMount) {
-      onCategoryClick();
-    }
-  }, [initialMount, onCategoryClick]);
-
   const fromCategory = categories.find(c => c.id === fromCategoryId);
 
   return (
diff --git a/packages/desktop-client/src/components/reports/reports/CashFlow.tsx b/packages/desktop-client/src/components/reports/reports/CashFlow.tsx
index 0ac91664750d3ea5d58603c3895b518b3cb88097..4a105b042b90ce9b06b191a9360d4430d130419a 100644
--- a/packages/desktop-client/src/components/reports/reports/CashFlow.tsx
+++ b/packages/desktop-client/src/components/reports/reports/CashFlow.tsx
@@ -113,7 +113,7 @@ export function CashFlow() {
           <MobilePageHeader
             title="Cash Flow"
             leftContent={
-              <MobileBackButton onClick={() => navigate('/reports')} />
+              <MobileBackButton onPress={() => navigate('/reports')} />
             }
           />
         ) : (
diff --git a/packages/desktop-client/src/components/reports/reports/CustomReport.tsx b/packages/desktop-client/src/components/reports/reports/CustomReport.tsx
index df09f6eed2715d15e303e43fc6f9d65a5bf25904..97df6638daad552137036bcd2907082185f251eb 100644
--- a/packages/desktop-client/src/components/reports/reports/CustomReport.tsx
+++ b/packages/desktop-client/src/components/reports/reports/CustomReport.tsx
@@ -640,7 +640,7 @@ export function CustomReport() {
         isNarrowWidth ? (
           <MobilePageHeader
             title={`Custom Report: ${report.name || 'Unsaved report'}`}
-            leftContent={<MobileBackButton onClick={onBackClick} />}
+            leftContent={<MobileBackButton onPress={onBackClick} />}
           />
         ) : (
           <PageHeader
diff --git a/packages/desktop-client/src/components/reports/reports/NetWorth.jsx b/packages/desktop-client/src/components/reports/reports/NetWorth.jsx
index 73cdfec8952cf171682b00ce527ea87d53004296..fe36605e58a996b8e25d2fc376863fb3a912dee2 100644
--- a/packages/desktop-client/src/components/reports/reports/NetWorth.jsx
+++ b/packages/desktop-client/src/components/reports/reports/NetWorth.jsx
@@ -144,7 +144,7 @@ function NetWorthInner({ widget }) {
           <MobilePageHeader
             title={title}
             leftContent={
-              <MobileBackButton onClick={() => navigate('/reports')} />
+              <MobileBackButton onPress={() => navigate('/reports')} />
             }
           />
         ) : (
diff --git a/packages/desktop-client/src/components/reports/reports/Spending.tsx b/packages/desktop-client/src/components/reports/reports/Spending.tsx
index efbff45ed19d6e2dc55010ba3fac761b245c61a6..7301a109804312abdb96a0f7f3eb3b8af9081906 100644
--- a/packages/desktop-client/src/components/reports/reports/Spending.tsx
+++ b/packages/desktop-client/src/components/reports/reports/Spending.tsx
@@ -123,7 +123,7 @@ export function Spending() {
           <MobilePageHeader
             title="Monthly Spending"
             leftContent={
-              <MobileBackButton onClick={() => navigate('/reports')} />
+              <MobileBackButton onPress={() => navigate('/reports')} />
             }
           />
         ) : (
diff --git a/packages/desktop-client/src/components/sidebar/Account.tsx b/packages/desktop-client/src/components/sidebar/Account.tsx
index 28c4420a8aaabfe136ec65bab1f1c6babfae658f..96437fef47b92e87330ba3fcd669476aad654a69 100644
--- a/packages/desktop-client/src/components/sidebar/Account.tsx
+++ b/packages/desktop-client/src/components/sidebar/Account.tsx
@@ -96,6 +96,7 @@ export function Account<FieldName extends SheetFields<'account'>>({
         <DropHighlight pos={dropPos} />
         <View innerRef={dragRef}>
           <Link
+            variant="internal"
             to={to}
             style={{
               ...accountNameStyle,
diff --git a/packages/desktop-client/src/components/sidebar/Item.tsx b/packages/desktop-client/src/components/sidebar/Item.tsx
index 8a209bd346b88db523237a860c6b4a3d39a607fe..e75c3fa35959e7d464ffd21ff4a8d255deae700c 100644
--- a/packages/desktop-client/src/components/sidebar/Item.tsx
+++ b/packages/desktop-client/src/components/sidebar/Item.tsx
@@ -69,14 +69,14 @@ export function Item({
           ...(forceHover ? hoverStyle : {}),
           ':hover': hoverStyle,
         }}
-        to={to}
-        onClick={onClick}
+        forceActive={forceActive}
         activeStyle={{
           borderLeft: '4px solid ' + theme.sidebarItemTextSelected,
           paddingLeft: 19 + indent - 4,
           color: theme.sidebarItemTextSelected,
         }}
-        forceActive={forceActive}
+        to={to}
+        onClick={onClick}
       >
         {content}
       </ItemContent>
diff --git a/packages/desktop-client/src/components/sidebar/ItemContent.tsx b/packages/desktop-client/src/components/sidebar/ItemContent.tsx
index 2ba413baa77c32f53415d8c81439eb501d8f25a5..73ca838f5bbdbe80c1492b12c5893e4b5d1489d6 100644
--- a/packages/desktop-client/src/components/sidebar/ItemContent.tsx
+++ b/packages/desktop-client/src/components/sidebar/ItemContent.tsx
@@ -38,7 +38,7 @@ export function ItemContent({
       {children}
     </View>
   ) : (
-    <Link to={to} style={style} activeStyle={activeStyle}>
+    <Link variant="internal" to={to} style={style} activeStyle={activeStyle}>
       {children}
     </Link>
   );
diff --git a/packages/desktop-client/src/components/table.tsx b/packages/desktop-client/src/components/table.tsx
index 7b0a3ecc45f5608df009562157321c5e6d06b442..60f38a0ee7231cbf7f998c92e129c1f609e6a08f 100644
--- a/packages/desktop-client/src/components/table.tsx
+++ b/packages/desktop-client/src/components/table.tsx
@@ -29,7 +29,7 @@ import { SvgDelete, SvgExpandArrow } from '../icons/v0';
 import { SvgCheckmark } from '../icons/v1';
 import { type CSSProperties, styles, theme } from '../style';
 
-import { Button } from './common/Button';
+import { Button } from './common/Button2';
 import { Input } from './common/Input';
 import { Menu, type MenuItem } from './common/Menu';
 import { Popover } from './common/Popover';
@@ -843,9 +843,9 @@ export function SelectedItemsButton<T extends MenuItem = MenuItem>({
     <View style={{ marginLeft: 10, flexShrink: 0 }}>
       <Button
         ref={triggerRef}
-        type="bare"
+        variant="bare"
         style={{ color: theme.pageTextPositive }}
-        onClick={() => setMenuOpen(true)}
+        onPress={() => setMenuOpen(true)}
         data-testid={id + '-select-button'}
       >
         <SvgExpandArrow
diff --git a/upcoming-release-notes/3163.md b/upcoming-release-notes/3163.md
new file mode 100644
index 0000000000000000000000000000000000000000..f6c8d75db23a9a49141144eb200f53bf2e449ea3
--- /dev/null
+++ b/upcoming-release-notes/3163.md
@@ -0,0 +1,6 @@
+---
+category: Maintenance
+authors: [joel-jeremy]
+---
+
+Use new react-aria-components in common and mobile components.