Repository: gitify-app/gitify
Pull request #947 (2024-04-02 13:39:04)

feat: redesign settings ux
Redesign the *Settings* screen so that it supports our growing configuration options (ie: #945).

Main changes include
* fixed header
* fixed footer
* scrollable settings section
* group settings into three distinct buckets (appearance, notification, system)
* rename `settings.appearance` -> `settings.theme` 

![Screenshot 2024-04-02 at 9 38 39 AM](https://github.com/gitify-app/gitify/assets/386277/282a132c-be61-419d-9137-f7706127dce1)
![Screenshot 2024-04-02 at 9 38 45 AM](https://github.com/gitify-app/gitify/assets/386277/c565b1a1-ab1b-45ff-ac06-a419b7af481e)







bmulholland: Looks great! Nice work :)

Patch:
diff --git a/src/__mocks__/mock-state.ts b/src/__mocks__/mock-state.ts
index 8175a7f1..bb8b7a3c 100644
--- a/src/__mocks__/mock-state.ts
+++ b/src/__mocks__/mock-state.ts
@@ -1,4 +1,4 @@
-import { Appearance, AuthState, SettingsState } from '../types';
+import { Theme, AuthState, SettingsState } from '../types';
 import { mockedUser, mockedEnterpriseAccounts } from './mockedData';
 
 export const mockAccounts: AuthState = {
@@ -13,7 +13,7 @@ export const mockSettings: SettingsState = {
   showNotifications: true,
   showBots: true,
   openAtStartup: false,
-  appearance: Appearance.SYSTEM,
+  theme: Theme.SYSTEM,
   colors: false,
   markAsDoneOnOpen: false,
 };
diff --git a/src/components/fields/Checkbox.tsx b/src/components/fields/Checkbox.tsx
index 4f4bc658..1197278a 100644
--- a/src/components/fields/Checkbox.tsx
+++ b/src/components/fields/Checkbox.tsx
@@ -34,9 +34,9 @@ export const FieldCheckbox = (props: IFieldCheckbox) => {
           {props.label}
         </label>
         {props.placeholder && (
-          <p className="text-gray-500 dark:text-gray-300">
+          <div className="italic text-gray-500 dark:text-gray-300">
             {props.placeholder}
-          </p>
+          </div>
         )}
       </div>
     </div>
diff --git a/src/components/fields/RadioGroup.tsx b/src/components/fields/RadioGroup.tsx
index f61bf79e..cc21327d 100644
--- a/src/components/fields/RadioGroup.tsx
+++ b/src/components/fields/RadioGroup.tsx
@@ -17,23 +17,31 @@ export const FieldRadioGroup = ({
   value: string;
 }) => {
   return (
-    <fieldset id={name} className="mb-1">
-      <div>
-        <legend className="mb-1 text-base font-medium dark:text-white">
+    <div className="flex items-start mt-1 mb-3">
+      <div className="mr-3 text-sm py-1">
+        <label
+          htmlFor={name}
+          className="font-medium text-gray-700 dark:text-gray-200 "
+        >
           {label}
-        </legend>
-        {placeholder && <p className="text-sm text-gray-500">{placeholder}</p>}
+        </label>
+
+        {placeholder && (
+          <div className="italic text-gray-500 dark:text-gray-300">
+            {placeholder}
+          </div>
+        )}
       </div>
 
       <div
-        className="flex items-center space-x-4 py-2"
+        className="flex items-center space-x-4"
         role="group"
         aria-labelledby={name}
       >
         {options.map((item) => {
           return (
             <div
-              className="flex items-center mt-1"
+              className="flex mt-1"
               key={`radio_item_${item.value.toLowerCase()}`}
             >
               <input
@@ -55,6 +63,6 @@ export const FieldRadioGroup = ({
           );
         })}
       </div>
-    </fieldset>
+    </div>
   );
 };
diff --git a/src/context/App.tsx b/src/context/App.tsx
index 3da7ba37..6fe2e758 100644
--- a/src/context/App.tsx
+++ b/src/context/App.tsx
@@ -10,14 +10,14 @@ import { useInterval } from '../hooks/useInterval';
 import { useNotifications } from '../hooks/useNotifications';
 import {
   AccountNotifications,
-  Appearance,
+  Theme,
   AuthOptions,
   AuthState,
   AuthTokenOptions,
   SettingsState,
 } from '../types';
 import { apiRequestAuth } from '../utils/api-requests';
-import { setAppearance } from '../utils/appearance';
+import { setTheme } from '../utils/theme';
 import { addAccount, authGitHub, getToken, getUserData } from '../utils/auth';
 import { setAutoLaunch } from '../utils/comms';
 import Constants from '../utils/constants';
@@ -36,7 +36,7 @@ export const defaultSettings: SettingsState = {
   showNotifications: true,
   showBots: true,
   openAtStartup: false,
-  appearance: Appearance.SYSTEM,
+  theme: Theme.SYSTEM,
   colors: null,
   markAsDoneOnOpen: false,
 };
@@ -87,8 +87,8 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
   }, []);
 
   useEffect(() => {
-    setAppearance(settings.appearance as Appearance);
-  }, [settings.appearance]);
+    setTheme(settings.theme as Theme);
+  }, [settings.theme]);
 
   useEffect(() => {
     fetchNotifications(accounts, settings);
@@ -103,7 +103,7 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
   }, 60000);
 
   const updateSetting = useCallback(
-    (name: keyof SettingsState, value: boolean | Appearance) => {
+    (name: keyof SettingsState, value: boolean | Theme) => {
       if (name === 'openAtStartup') {
         setAutoLaunch(value as boolean);
       }
diff --git a/src/routes/Settings.tsx b/src/routes/Settings.tsx
index 1c218bac..0fa76eb9 100644
--- a/src/routes/Settings.tsx
+++ b/src/routes/Settings.tsx
@@ -17,9 +17,9 @@ import { useNavigate } from 'react-router-dom';
 import { FieldCheckbox } from '../components/fields/Checkbox';
 import { FieldRadioGroup } from '../components/fields/RadioGroup';
 import { AppContext } from '../context/App';
-import { Appearance } from '../types';
+import { Theme } from '../types';
 import { apiRequestAuth } from '../utils/api-requests';
-import { setAppearance } from '../utils/appearance';
+import { setTheme } from '../utils/theme';
 import { openExternalLink, updateTrayIcon } from '../utils/comms';
 import Constants from '../utils/constants';
 import { generateGitHubAPIUrl } from '../utils/helpers';
@@ -61,9 +61,9 @@ export const SettingsRoute: React.FC = () => {
     })();
   }, [accounts.token]);
 
-  ipcRenderer.on('update-native-theme', (_, updatedAppearance: Appearance) => {
-    if (settings.appearance === Appearance.SYSTEM) {
-      setAppearance(updatedAppearance);
+  ipcRenderer.on('update-native-theme', (_, updatedTheme: Theme) => {
+    if (settings.theme === Theme.SYSTEM) {
+      setTheme(updatedTheme);
     }
   });
 
@@ -86,7 +86,7 @@ export const SettingsRoute: React.FC = () => {
 
   return (
     <div
-      className="flex flex-1 flex-col dark:bg-gray-dark dark:text-white"
+      className="flex flex-1 flex-col h-screen dark:bg-gray-dark dark:text-white"
       data-testid="settings"
     >
       <div className="flex justify-between items-center mt-2 py-2 mx-8">
@@ -105,77 +105,95 @@ export const SettingsRoute: React.FC = () => {
         <h3 className="text-lg font-semibold">Settings</h3>
       </div>
 
-      <div className="flex-1 px-8">
-        <FieldRadioGroup
-          name="appearance"
-          label="Appearance"
-          value={settings.appearance}
-          options={[
-            { label: 'System', value: Appearance.SYSTEM },
-            { label: 'Light', value: Appearance.LIGHT },
-            { label: 'Dark', value: Appearance.DARK },
-          ]}
-          onChange={(evt) => {
-            updateSetting('appearance', evt.target.value);
-          }}
-        />
-
-        <FieldCheckbox
-          name="colors"
-          label={`Use GitHub-like state colors${
-            !colorScope ? ' (requires repo scope)' : ''
-          }`}
-          checked={colorScope && settings.colors}
-          onChange={(evt) =>
-            colorScope && updateSetting('colors', evt.target.checked)
-          }
-          disabled={!colorScope}
-        />
-        <FieldCheckbox
-          name="showOnlyParticipating"
-          label="Show only participating"
-          checked={settings.participating}
-          onChange={(evt) => updateSetting('participating', evt.target.checked)}
-        />
-
-        <FieldCheckbox
-          name="showNotifications"
-          label="Show notifications"
-          checked={settings.showNotifications}
-          onChange={(evt) =>
-            updateSetting('showNotifications', evt.target.checked)
-          }
-        />
-        <FieldCheckbox
-          name="showBots"
-          label="Show notifications from Bot accounts"
-          checked={settings.showBots}
-          onChange={(evt) => updateSetting('showBots', evt.target.checked)}
-        />
-        <FieldCheckbox
-          name="markAsDoneOnOpen"
-          label="Mark as done on open"
-          checked={settings.markAsDoneOnOpen}
-          onChange={(evt) =>
-            updateSetting('markAsDoneOnOpen', evt.target.checked)
-          }
-        />
-        <FieldCheckbox
-          name="playSound"
-          label="Play sound"
-          checked={settings.playSound}
-          onChange={(evt) => updateSetting('playSound', evt.target.checked)}
-        />
-        {!isLinux && (
+      <div className="flex-grow overflow-x-auto px-8">
+        <fieldset className="mb-3">
+          <legend id="appearance" className="font-semibold mt-2 mb-1">
+            Appearance
+          </legend>
+          <FieldRadioGroup
+            name="theme"
+            label="Theme:"
+            value={settings.theme}
+            options={[
+              { label: 'System', value: Theme.SYSTEM },
+              { label: 'Light', value: Theme.LIGHT },
+              { label: 'Dark', value: Theme.DARK },
+            ]}
+            onChange={(evt) => {
+              updateSetting('theme', evt.target.value);
+            }}
+          />
+          <FieldCheckbox
+            name="colors"
+            label={`Use GitHub-like state colors${
+              !colorScope ? ' (requires repo scope)' : ''
+            }`}
+            checked={colorScope && settings.colors}
+            onChange={(evt) =>
+              colorScope && updateSetting('colors', evt.target.checked)
+            }
+            disabled={!colorScope}
+          />
+        </fieldset>
+
+        <fieldset className="mb-3">
+          <legend id="notifications" className="font-semibold  mt-2 mb-1">
+            Notifications
+          </legend>
+          <FieldCheckbox
+            name="showOnlyParticipating"
+            label="Show only participating"
+            checked={settings.participating}
+            onChange={(evt) =>
+              updateSetting('participating', evt.target.checked)
+            }
+          />
+
           <FieldCheckbox
-            name="openAtStartUp"
-            label="Open at startup"
-            checked={settings.openAtStartup}
+            name="showNotifications"
+            label="Show notifications"
+            checked={settings.showNotifications}
             onChange={(evt) =>
-              updateSetting('openAtStartup', evt.target.checked)
+              updateSetting('showNotifications', evt.target.checked)
             }
           />
-        )}
+          <FieldCheckbox
+            name="showBots"
+            label="Show notifications from Bot accounts"
+            checked={settings.showBots}
+            onChange={(evt) => updateSetting('showBots', evt.target.checked)}
+          />
+          <FieldCheckbox
+            name="markAsDoneOnOpen"
+            label="Mark as done on open"
+            checked={settings.markAsDoneOnOpen}
+            onChange={(evt) =>
+              updateSetting('markAsDoneOnOpen', evt.target.checked)
+            }
+          />
+        </fieldset>
+
+        <fieldset className="mb-3">
+          <legend id="system" className="font-semibold  mt-2 mb-1">
+            System
+          </legend>
+          <FieldCheckbox
+            name="playSound"
+            label="Play sound"
+            checked={settings.playSound}
+            onChange={(evt) => updateSetting('playSound', evt.target.checked)}
+          />
+          {!isLinux && (
+            <FieldCheckbox
+              name="openAtStartUp"
+              label="Open at startup"
+              checked={settings.openAtStartup}
+              onChange={(evt) =>
+                updateSetting('openAtStartup', evt.target.checked)
+              }
+            />
+          )}
+        </fieldset>
       </div>
 
       <div className="flex justify-between items-center bg-gray-200 dark:bg-gray-darker py-1 px-8">
diff --git a/src/types.ts b/src/types.ts
index a3147c0f..9fcc6299 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -6,18 +6,28 @@ export interface AuthState {
   user: GitifyUser | null;
 }
 
-export interface SettingsState {
+export type SettingsState = AppearanceSettingsState &
+  NotificationSettingsState &
+  SystemSettingsState;
+
+interface AppearanceSettingsState {
+  theme: Theme;
+  colors: boolean | null;
+}
+
+interface NotificationSettingsState {
   participating: boolean;
-  playSound: boolean;
   showNotifications: boolean;
   showBots: boolean;
-  openAtStartup: boolean;
-  appearance: Appearance;
-  colors: boolean | null;
   markAsDoneOnOpen: boolean;
 }
 
-export enum Appearance {
+interface SystemSettingsState {
+  playSound: boolean;
+  openAtStartup: boolean;
+}
+
+export enum Theme {
   SYSTEM = 'SYSTEM',
   LIGHT = 'LIGHT',
   DARK = 'DARK',
diff --git a/src/utils/github-api.ts b/src/utils/github-api.ts
index f153d6ed..21b2fd7d 100644
--- a/src/utils/github-api.ts
+++ b/src/utils/github-api.ts
@@ -24,8 +24,8 @@ import {
   XIcon,
 } from '@primer/octicons-react';
 import { Reason, Subject } from '../typesGithub';
-import { Appearance } from '../types';
-import { getAppearance } from './appearance';
+import { Theme } from '../types';
+import { getTheme } from './theme';
 
 // prettier-ignore
 const DESCRIPTIONS = {
@@ -171,8 +171,8 @@ export function getNotificationTypeIconColor(subject: Subject): string {
     case 'merged':
       return 'text-purple-500';
     default:
-      const appearance = getAppearance();
-      if (appearance == Appearance.DARK) {
+      const theme = getTheme();
+      if (theme == Theme.DARK) {
         return 'text-gray-300';
       }
       return 'text-gray-500';
diff --git a/src/utils/appearance.ts b/src/utils/theme.ts
similarity index 69%
rename from src/utils/appearance.ts
rename to src/utils/theme.ts
index bce783d5..57f90202 100644
--- a/src/utils/appearance.ts
+++ b/src/utils/theme.ts
@@ -1,11 +1,11 @@
-import { Appearance } from '../types';
+import { Theme } from '../types';
 
-export function getAppearance(): Appearance {
+export function getTheme(): Theme {
   if (document.querySelector('html').classList.contains('dark')) {
-    return Appearance.DARK;
+    return Theme.DARK;
   }
 
-  return Appearance.LIGHT;
+  return Theme.LIGHT;
 }
 
 export const setLightMode = () =>
@@ -14,13 +14,13 @@ export const setLightMode = () =>
 export const setDarkMode = () =>
   document.querySelector('html').classList.add('dark');
 
-export const setAppearance = (mode?: Appearance) => {
+export const setTheme = (mode?: Theme) => {
   switch (mode) {
-    case Appearance.LIGHT:
+    case Theme.LIGHT:
       setLightMode();
       break;
 
-    case Appearance.DARK:
+    case Theme.DARK:
       setDarkMode();
       break;
 


Test patch:
diff --git a/src/components/fields/__snapshots__/RadioGroup.test.tsx.snap b/src/components/fields/__snapshots__/RadioGroup.test.tsx.snap
index 37025914..70e146f0 100644
--- a/src/components/fields/__snapshots__/RadioGroup.test.tsx.snap
+++ b/src/components/fields/__snapshots__/RadioGroup.test.tsx.snap
@@ -1,29 +1,31 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`components/fields/radiogroup.tsx should render  1`] = `
-<fieldset
-  className="mb-1"
-  id="appearance"
+<div
+  className="flex items-start mt-1 mb-3"
 >
-  <div>
-    <legend
-      className="mb-1 text-base font-medium dark:text-white"
+  <div
+    className="mr-3 text-sm py-1"
+  >
+    <label
+      className="font-medium text-gray-700 dark:text-gray-200 "
+      htmlFor="appearance"
     >
       Appearance
-    </legend>
-    <p
-      className="text-sm text-gray-500"
+    </label>
+    <div
+      className="italic text-gray-500 dark:text-gray-300"
     >
       This is some helper text
-    </p>
+    </div>
   </div>
   <div
     aria-labelledby="appearance"
-    className="flex items-center space-x-4 py-2"
+    className="flex items-center space-x-4"
     role="group"
   >
     <div
-      className="flex items-center mt-1"
+      className="flex mt-1"
     >
       <input
         checked={false}
@@ -42,7 +44,7 @@ exports[`components/fields/radiogroup.tsx should render  1`] = `
       </label>
     </div>
     <div
-      className="flex items-center mt-1"
+      className="flex mt-1"
     >
       <input
         checked={true}
@@ -61,5 +63,5 @@ exports[`components/fields/radiogroup.tsx should render  1`] = `
       </label>
     </div>
   </div>
-</fieldset>
+</div>
 `;
diff --git a/src/context/App.test.tsx b/src/context/App.test.tsx
index 63d0e8bf..d31f2463 100644
--- a/src/context/App.test.tsx
+++ b/src/context/App.test.tsx
@@ -285,7 +285,7 @@ describe('context/App.tsx', () => {
     expect(saveStateMock).toHaveBeenCalledWith(
       { enterpriseAccounts: [], token: null, user: null },
       {
-        appearance: 'SYSTEM',
+        theme: 'SYSTEM',
         openAtStartup: false,
         participating: true,
         playSound: true,
@@ -324,7 +324,7 @@ describe('context/App.tsx', () => {
     expect(saveStateMock).toHaveBeenCalledWith(
       { enterpriseAccounts: [], token: null, user: null },
       {
-        appearance: 'SYSTEM',
+        theme: 'SYSTEM',
         openAtStartup: true,
         participating: false,
         playSound: true,
diff --git a/src/routes/Settings.test.tsx b/src/routes/Settings.test.tsx
index 3e36c9da..6394ec30 100644
--- a/src/routes/Settings.test.tsx
+++ b/src/routes/Settings.test.tsx
@@ -267,7 +267,7 @@ describe('routes/Settings.tsx', () => {
     expect(updateSetting).toHaveBeenCalledWith('openAtStartup', false);
   });
 
-  it('should change the appearance radio group', async () => {
+  it('should change the theme radio group', async () => {
     let getByLabelText;
 
     await act(async () => {
@@ -290,7 +290,7 @@ describe('routes/Settings.tsx', () => {
     fireEvent.click(getByLabelText('Light'));
 
     expect(updateSetting).toHaveBeenCalledTimes(1);
-    expect(updateSetting).toHaveBeenCalledWith('appearance', 'LIGHT');
+    expect(updateSetting).toHaveBeenCalledWith('theme', 'LIGHT');
   });
 
   it('should go to the enterprise login route', async () => {
diff --git a/src/routes/__snapshots__/Settings.test.tsx.snap b/src/routes/__snapshots__/Settings.test.tsx.snap
index 2fa0d6d3..73583eb5 100644
--- a/src/routes/__snapshots__/Settings.test.tsx.snap
+++ b/src/routes/__snapshots__/Settings.test.tsx.snap
@@ -30,7 +30,7 @@ exports[`routes/Settings.tsx should not be able to enable colors due to missing
 
 exports[`routes/Settings.tsx should render itself & its children 1`] = `
 <div
-  class="flex flex-1 flex-col dark:bg-gray-dark dark:text-white"
+  class="flex flex-1 flex-col h-screen dark:bg-gray-dark dark:text-white"
   data-testid="settings"
 >
   <div
@@ -63,243 +63,275 @@ exports[`routes/Settings.tsx should render itself & its children 1`] = `
     </h3>
   </div>
   <div
-    class="flex-1 px-8"
+    class="flex-grow overflow-x-auto px-8"
   >
     <fieldset
-      class="mb-1"
-      id="appearance"
+      class="mb-3"
     >
-      <div>
-        <legend
-          class="mb-1 text-base font-medium dark:text-white"
-        >
-          Appearance
-        </legend>
-      </div>
+      <legend
+        class="font-semibold mt-2 mb-1"
+        id="appearance"
+      >
+        Appearance
+      </legend>
       <div
-        aria-labelledby="appearance"
-        class="flex items-center space-x-4 py-2"
-        role="group"
+        class="flex items-start mt-1 mb-3"
       >
         <div
-          class="flex items-center mt-1"
+          class="mr-3 text-sm py-1"
         >
-          <input
-            checked=""
-            class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300"
-            id="appearance_system"
-            name="appearance"
-            type="radio"
-            value="SYSTEM"
-          />
           <label
-            class="ml-3 block text-sm font-medium text-gray-700 dark:text-white"
-            for="appearance_system"
+            class="font-medium text-gray-700 dark:text-gray-200 "
+            for="theme"
           >
-            System
+            Theme:
           </label>
         </div>
         <div
-          class="flex items-center mt-1"
+          aria-labelledby="theme"
+          class="flex items-center space-x-4"
+          role="group"
         >
-          <input
-            class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300"
-            id="appearance_light"
-            name="appearance"
-            type="radio"
-            value="LIGHT"
-          />
-          <label
-            class="ml-3 block text-sm font-medium text-gray-700 dark:text-white"
-            for="appearance_light"
+          <div
+            class="flex mt-1"
           >
-            Light
-          </label>
+            <input
+              checked=""
+              class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300"
+              id="theme_system"
+              name="theme"
+              type="radio"
+              value="SYSTEM"
+            />
+            <label
+              class="ml-3 block text-sm font-medium text-gray-700 dark:text-white"
+              for="theme_system"
+            >
+              System
+            </label>
+          </div>
+          <div
+            class="flex mt-1"
+          >
+            <input
+              class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300"
+              id="theme_light"
+              name="theme"
+              type="radio"
+              value="LIGHT"
+            />
+            <label
+              class="ml-3 block text-sm font-medium text-gray-700 dark:text-white"
+              for="theme_light"
+            >
+              Light
+            </label>
+          </div>
+          <div
+            class="flex mt-1"
+          >
+            <input
+              class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300"
+              id="theme_dark"
+              name="theme"
+              type="radio"
+              value="DARK"
+            />
+            <label
+              class="ml-3 block text-sm font-medium text-gray-700 dark:text-white"
+              for="theme_dark"
+            >
+              Dark
+            </label>
+          </div>
         </div>
+      </div>
+      <div
+        class="flex items-start mt-1 mb-3"
+      >
         <div
-          class="flex items-center mt-1"
+          class="flex items-center h-5"
         >
           <input
-            class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300"
-            id="appearance_dark"
-            name="appearance"
-            type="radio"
-            value="DARK"
+            class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
+            id="colors"
+            type="checkbox"
           />
+        </div>
+        <div
+          class="ml-3 text-sm"
+        >
           <label
-            class="ml-3 block text-sm font-medium text-gray-700 dark:text-white"
-            for="appearance_dark"
+            class="font-medium text-gray-700 dark:text-gray-200"
+            for="colors"
+            style=""
           >
-            Dark
+            Use GitHub-like state colors
           </label>
         </div>
       </div>
     </fieldset>
-    <div
-      class="flex items-start mt-1 mb-3"
+    <fieldset
+      class="mb-3"
     >
-      <div
-        class="flex items-center h-5"
+      <legend
+        class="font-semibold  mt-2 mb-1"
+        id="notifications"
       >
-        <input
-          class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
-          id="colors"
-          type="checkbox"
-        />
-      </div>
+        Notifications
+      </legend>
       <div
-        class="ml-3 text-sm"
+        class="flex items-start mt-1 mb-3"
       >
-        <label
-          class="font-medium text-gray-700 dark:text-gray-200"
-          for="colors"
-          style=""
+        <div
+          class="flex items-center h-5"
         >
-          Use GitHub-like state colors
-        </label>
-      </div>
-    </div>
-    <div
-      class="flex items-start mt-1 mb-3"
-    >
-      <div
-        class="flex items-center h-5"
-      >
-        <input
-          class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
-          id="showOnlyParticipating"
-          type="checkbox"
-        />
-      </div>
-      <div
-        class="ml-3 text-sm"
-      >
-        <label
-          class="font-medium text-gray-700 dark:text-gray-200"
-          for="showOnlyParticipating"
+          <input
+            class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
+            id="showOnlyParticipating"
+            type="checkbox"
+          />
+        </div>
+        <div
+          class="ml-3 text-sm"
         >
-          Show only participating
-        </label>
-      </div>
-    </div>
-    <div
-      class="flex items-start mt-1 mb-3"
-    >
-      <div
-        class="flex items-center h-5"
-      >
-        <input
-          checked=""
-          class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
-          id="showNotifications"
-          type="checkbox"
-        />
+          <label
+            class="font-medium text-gray-700 dark:text-gray-200"
+            for="showOnlyParticipating"
+          >
+            Show only participating
+          </label>
+        </div>
       </div>
       <div
-        class="ml-3 text-sm"
+        class="flex items-start mt-1 mb-3"
       >
-        <label
-          class="font-medium text-gray-700 dark:text-gray-200"
-          for="showNotifications"
+        <div
+          class="flex items-center h-5"
         >
-          Show notifications
-        </label>
-      </div>
-    </div>
-    <div
-      class="flex items-start mt-1 mb-3"
-    >
-      <div
-        class="flex items-center h-5"
-      >
-        <input
-          checked=""
-          class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
-          id="showBots"
-          type="checkbox"
-        />
-      </div>
-      <div
-        class="ml-3 text-sm"
-      >
-        <label
-          class="font-medium text-gray-700 dark:text-gray-200"
-          for="showBots"
+          <input
+            checked=""
+            class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
+            id="showNotifications"
+            type="checkbox"
+          />
+        </div>
+        <div
+          class="ml-3 text-sm"
         >
-          Show notifications from Bot accounts
-        </label>
+          <label
+            class="font-medium text-gray-700 dark:text-gray-200"
+            for="showNotifications"
+          >
+            Show notifications
+          </label>
+        </div>
       </div>
-    </div>
-    <div
-      class="flex items-start mt-1 mb-3"
-    >
       <div
-        class="flex items-center h-5"
+        class="flex items-start mt-1 mb-3"
       >
-        <input
-          class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
-          id="markAsDoneOnOpen"
-          type="checkbox"
-        />
+        <div
+          class="flex items-center h-5"
+        >
+          <input
+            checked=""
+            class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
+            id="showBots"
+            type="checkbox"
+          />
+        </div>
+        <div
+          class="ml-3 text-sm"
+        >
+          <label
+            class="font-medium text-gray-700 dark:text-gray-200"
+            for="showBots"
+          >
+            Show notifications from Bot accounts
+          </label>
+        </div>
       </div>
       <div
-        class="ml-3 text-sm"
+        class="flex items-start mt-1 mb-3"
       >
-        <label
-          class="font-medium text-gray-700 dark:text-gray-200"
-          for="markAsDoneOnOpen"
+        <div
+          class="flex items-center h-5"
+        >
+          <input
+            class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
+            id="markAsDoneOnOpen"
+            type="checkbox"
+          />
+        </div>
+        <div
+          class="ml-3 text-sm"
         >
-          Mark as done on open
-        </label>
+          <label
+            class="font-medium text-gray-700 dark:text-gray-200"
+            for="markAsDoneOnOpen"
+          >
+            Mark as done on open
+          </label>
+        </div>
       </div>
-    </div>
-    <div
-      class="flex items-start mt-1 mb-3"
+    </fieldset>
+    <fieldset
+      class="mb-3"
     >
-      <div
-        class="flex items-center h-5"
+      <legend
+        class="font-semibold  mt-2 mb-1"
+        id="system"
       >
-        <input
-          checked=""
-          class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
-          id="playSound"
-          type="checkbox"
-        />
-      </div>
+        System
+      </legend>
       <div
-        class="ml-3 text-sm"
+        class="flex items-start mt-1 mb-3"
       >
-        <label
-          class="font-medium text-gray-700 dark:text-gray-200"
-          for="playSound"
+        <div
+          class="flex items-center h-5"
         >
-          Play sound
-        </label>
-      </div>
-    </div>
-    <div
-      class="flex items-start mt-1 mb-3"
-    >
-      <div
-        class="flex items-center h-5"
-      >
-        <input
-          class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
-          id="openAtStartUp"
-          type="checkbox"
-        />
+          <input
+            checked=""
+            class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
+            id="playSound"
+            type="checkbox"
+          />
+        </div>
+        <div
+          class="ml-3 text-sm"
+        >
+          <label
+            class="font-medium text-gray-700 dark:text-gray-200"
+            for="playSound"
+          >
+            Play sound
+          </label>
+        </div>
       </div>
       <div
-        class="ml-3 text-sm"
+        class="flex items-start mt-1 mb-3"
       >
-        <label
-          class="font-medium text-gray-700 dark:text-gray-200"
-          for="openAtStartUp"
+        <div
+          class="flex items-center h-5"
         >
-          Open at startup
-        </label>
+          <input
+            class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
+            id="openAtStartUp"
+            type="checkbox"
+          />
+        </div>
+        <div
+          class="ml-3 text-sm"
+        >
+          <label
+            class="font-medium text-gray-700 dark:text-gray-200"
+            for="openAtStartUp"
+          >
+            Open at startup
+          </label>
+        </div>
       </div>
-    </div>
+    </fieldset>
   </div>
   <div
     class="flex justify-between items-center bg-gray-200 dark:bg-gray-darker py-1 px-8"
diff --git a/src/utils/storage.test.ts b/src/utils/storage.test.ts
index 9240b34c..34e298a2 100644
--- a/src/utils/storage.test.ts
+++ b/src/utils/storage.test.ts
@@ -6,12 +6,12 @@ describe('utils/storage.ts', () => {
     jest.spyOn(localStorage.__proto__, 'getItem').mockReturnValueOnce(
       JSON.stringify({
         auth: { token: '123-456' },
-        settings: { appearance: 'DARK' },
+        settings: { theme: 'DARK' },
       }),
     );
     const result = loadState();
     expect(result.accounts.token).toBe('123-456');
-    expect(result.settings.appearance).toBe('DARK');
+    expect(result.settings.theme).toBe('DARK');
   });
 
   it('should load the state from localstorage - empty', () => {
diff --git a/src/utils/appearance.test.ts b/src/utils/theme.test.ts
similarity index 79%
rename from src/utils/appearance.test.ts
rename to src/utils/theme.test.ts
index a6f8005e..baa43188 100644
--- a/src/utils/appearance.test.ts
+++ b/src/utils/theme.test.ts
@@ -1,7 +1,7 @@
-import { Appearance } from '../types';
-import { setAppearance } from './appearance';
+import { Theme } from '../types';
+import { setTheme } from './theme';
 
-import * as appearanceHelpers from './appearance';
+import * as appearanceHelpers from './theme';
 
 describe('utils/appearance.ts', () => {
   beforeAll(() => {
@@ -17,17 +17,17 @@ describe('utils/appearance.ts', () => {
   });
 
   it('should change to light mode', () => {
-    setAppearance(Appearance.LIGHT);
+    setTheme(Theme.LIGHT);
     expect(appearanceHelpers.setLightMode).toHaveBeenCalledTimes(1);
   });
 
   it('should change to dark mode', () => {
-    setAppearance(Appearance.DARK);
+    setTheme(Theme.DARK);
     expect(appearanceHelpers.setDarkMode).toHaveBeenCalledTimes(1);
   });
 
   it("should use the system's mode - light", () => {
-    setAppearance();
+    setTheme();
     expect(appearanceHelpers.setLightMode).toHaveBeenCalledTimes(1);
   });
 
@@ -38,7 +38,7 @@ describe('utils/appearance.ts', () => {
         matches: true,
       })),
     });
-    setAppearance();
+    setTheme();
     expect(appearanceHelpers.setDarkMode).toHaveBeenCalledTimes(1);
   });
 });