diff --git a/src/app/browser/components/button.tsx b/src/app/browser/components/button.tsx
new file mode 100644
index 0000000..480a2d3
--- /dev/null
+++ b/src/app/browser/components/button.tsx
@@ -0,0 +1,163 @@
+import React, { useCallback, useContext, useState } from 'react';
+import { Button as NativeButton, Platform, Pressable, StyleSheet, Text, View } from 'react-native';
+import { TEXT_COLOUR_LIGHT } from '../constants.js';
+import ipc from '../ipc.js';
+import { useAccentColour, useColourScheme, WindowFocusedContext } from '../util.js';
+
+function ButtonMac(props: {
+ title: string;
+ primary?: boolean;
+ color?: string;
+ autoFocus?: boolean;
+ onPress?: () => void;
+}) {
+ const styles = styles_mac;
+
+ const window_focused = useContext(WindowFocusedContext);
+ const accent_colour = useAccentColour();
+ const [pressed, setPressIn] = useState(false);
+ const onPressIn = useCallback(() => setPressIn(true), []);
+ const onPressOut = useCallback(() => setPressIn(false), []);
+
+ const active = window_focused && (props.primary || pressed);
+
+ return
+
+ {props.title}
+
+ ;
+}
+
+const styles_mac = StyleSheet.create({
+ button: {
+ cursor: 'default',
+ lineHeight: 19,
+ borderRadius: 3,
+ boxShadow: '#00000030 0px 0px 1px',
+ },
+
+ inner: {
+ paddingHorizontal: 14,
+ paddingVertical: 2,
+ borderRadius: 3,
+ backgroundColor: '#ffffff40',
+ borderTopWidth: 0.5,
+ borderTopColor: '#ffffff3a',
+ },
+ innerActive: {
+ backgroundColor: 'transparent',
+ backgroundImage: 'linear-gradient(0deg, #00000050, #00000040)',
+ },
+ innerPressed: {
+ backgroundColor: 'transparent',
+ backgroundImage: 'linear-gradient(0deg, #00000040, #00000020)',
+ },
+
+ text: {
+ fontSize: 13,
+ color: '#eaeaea',
+ },
+});
+
+function ButtonWindows(props: {
+ title: string;
+ primary?: boolean;
+ color?: string;
+ autoFocus?: boolean;
+ onPress?: () => void;
+}) {
+ const styles = styles_windows;
+
+ const window_focused = useContext(WindowFocusedContext);
+ const colour_scheme = useColourScheme();
+ const accent_colour = useAccentColour();
+
+ const [hovered, setMouseOver] = useState(false);
+ const onMouseOver = useCallback(() => setMouseOver(true), []);
+ const onMouseOut = useCallback(() => setMouseOver(false), []);
+
+ const [pressed, setPressIn] = useState(false);
+ const onPressIn = useCallback(() => setPressIn(true), []);
+ const onPressOut = useCallback(() => setPressIn(false), []);
+ const active = window_focused && (props.primary || pressed);
+
+ return
+ {props.title}
+ ;
+}
+
+const styles_windows = StyleSheet.create({
+ button: {
+ backgroundColor: '#ffffff20',
+ minWidth: 180,
+ paddingVertical: 8,
+ paddingHorizontal: 12,
+ justifyContent: 'center',
+ alignItems: 'center',
+ textAlign: 'center',
+ },
+ buttonLight: {
+ backgroundColor: '#00000020',
+ },
+ buttonHover: {
+ backgroundColor: '#ffffff30',
+ },
+ buttonLightHover: {
+ backgroundColor: '#00000030',
+ },
+
+ text: {
+ color: '#eaeaea',
+ },
+ textLight: {
+ color: TEXT_COLOUR_LIGHT,
+ },
+});
+
+function ButtonNative(props: {
+ title: string;
+ color?: string;
+ onPress?: () => void;
+}) {
+ const accent_colour = useAccentColour();
+
+ return ;
+}
+
+export default
+ Platform.OS === 'web' && ipc.platform === 'darwin' ? React.memo(ButtonMac) :
+ Platform.OS === 'web' && ipc.platform === 'win32' ? React.memo(ButtonWindows) :
+ React.memo(ButtonNative);
diff --git a/src/app/browser/components/index.ts b/src/app/browser/components/index.ts
new file mode 100644
index 0000000..c2779b6
--- /dev/null
+++ b/src/app/browser/components/index.ts
@@ -0,0 +1 @@
+export { default as Button } from './button.js';
diff --git a/src/app/browser/constants.ts b/src/app/browser/constants.ts
index bc133a6..c30ebb0 100644
--- a/src/app/browser/constants.ts
+++ b/src/app/browser/constants.ts
@@ -1,9 +1,11 @@
+import ipc from './ipc.js';
+
export const NSO_COLOUR = '#e60012';
export const NSO_COLOUR_DARK: `${typeof NSO_COLOUR}e0` = `${NSO_COLOUR}e0`;
export const DISCORD_COLOUR = '#5865f2';
-export const BACKGROUND_COLOUR_MAIN_LIGHT = '#ececec';
-export const BACKGROUND_COLOUR_MAIN_DARK = '#252424';
+export const BACKGROUND_COLOUR_MAIN_LIGHT = ipc.platform === 'win32' ? '#ffffff' : '#ececec';
+export const BACKGROUND_COLOUR_MAIN_DARK = ipc.platform === 'win32' ? '#000000' : '#252424';
export const BACKGROUND_COLOUR_SECONDARY_LIGHT = '#ffffff';
export const BACKGROUND_COLOUR_SECONDARY_DARK = '#353535';
@@ -15,6 +17,7 @@ export const HIGHLIGHT_COLOUR_DARK = '#ffffff20';
export const BORDER_COLOUR_LIGHT = '#00000020';
export const BORDER_COLOUR_DARK = '#00000080';
+export const BORDER_COLOUR_SECONDARY_DARK = '#ffffff20';
export const TEXT_COLOUR_LIGHT = '#212121';
export const TEXT_COLOUR_DARK = '#f5f5f5';
diff --git a/src/app/browser/discord/index.tsx b/src/app/browser/discord/index.tsx
index 246acf0..ffed42b 100644
--- a/src/app/browser/discord/index.tsx
+++ b/src/app/browser/discord/index.tsx
@@ -1,9 +1,10 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
-import { Button, StyleSheet, Text, TextInput, useColorScheme, View } from 'react-native';
+import { StyleSheet, Text, TextInput, useColorScheme, View } from 'react-native';
import { Picker } from 'react-native-web';
+import { Button } from '../components/index.js';
import { DEFAULT_ACCENT_COLOUR, HIGHLIGHT_COLOUR_DARK, HIGHLIGHT_COLOUR_LIGHT, TEXT_COLOUR_DARK, TEXT_COLOUR_LIGHT } from '../constants.js';
import ipc, { events } from '../ipc.js';
-import { getAccounts, RequestState, Root, useAsync, useDiscordPresenceSource, useEventListener, User } from '../util.js';
+import { getAccounts, RequestState, Root, useAsync, useDiscordPresenceSource, useEventListener } from '../util.js';
export interface DiscordSetupProps {
//
@@ -149,6 +150,7 @@ export default function DiscordSetup(props: DiscordSetupProps) {
diff --git a/src/app/browser/friend/index.tsx b/src/app/browser/friend/index.tsx
index 2515c33..4a8a1f1 100644
--- a/src/app/browser/friend/index.tsx
+++ b/src/app/browser/friend/index.tsx
@@ -1,10 +1,11 @@
///
import React, { useCallback, useEffect } from 'react';
-import { ActivityIndicator, Button, Image, StyleSheet, Text, TouchableOpacity, useColorScheme, View } from 'react-native';
+import { ActivityIndicator, Image, StyleSheet, Text, TouchableOpacity, useColorScheme, View } from 'react-native';
import { CheckBox } from 'react-native-web';
import { Game, Presence, PresencePermissions, PresenceState } from '../../../api/znc-types.js';
import { getTitleIdFromEcUrl, hrduration } from '../../../util/misc.js';
+import { Button } from '../components/index.js';
import { DEFAULT_ACCENT_COLOUR, TEXT_COLOUR_ACTIVE, TEXT_COLOUR_DARK, TEXT_COLOUR_LIGHT } from '../constants.js';
import ipc, { events } from '../ipc.js';
import { RequestState, Root, useAsync, useColourScheme, useDiscordPresenceSource, useEventListener } from '../util.js';
@@ -105,7 +106,8 @@ export default function Friend(props: FriendProps) {