diff --git a/external/lang b/external/lang
index ec907eb606a3e1d5046bef503caa0585f6bcbc22..9bb62d1185f7e6f7a3821751797e30cb41e74bf8 160000
--- a/external/lang
+++ b/external/lang
@@ -1 +1 @@
-Subproject commit ec907eb606a3e1d5046bef503caa0585f6bcbc22
+Subproject commit 9bb62d1185f7e6f7a3821751797e30cb41e74bf8
diff --git a/package.json b/package.json
index a89609945ac03a43f1585f36420d845daee1d37d..595b73b7acdd6baa674907de7726b50d192c0015 100644
--- a/package.json
+++ b/package.json
@@ -78,7 +78,7 @@
     "react-router-dom": "^5.2.0",
     "react-scroll": "^1.8.2",
     "redux": "^4.1.0",
-    "revolt.js": "4.3.3-alpha.6",
+    "revolt.js": "4.3.3-alpha.7",
     "rimraf": "^3.0.2",
     "sass": "^1.35.1",
     "shade-blend-color": "^1.0.0",
diff --git a/src/components/ui/Button.tsx b/src/components/ui/Button.tsx
index 9c4b3a2bd6b240808e25c17ba6a35ccf8e3a8276..8582a19370252b93174386135a652c4b66a6e9ff 100644
--- a/src/components/ui/Button.tsx
+++ b/src/components/ui/Button.tsx
@@ -61,6 +61,7 @@ export default styled.button<Props>`
 
             &:hover {
                 filter: brightness(1.2);
+                background: var(--error);
             }
 
             &:disabled {
diff --git a/src/components/ui/Checkbox.tsx b/src/components/ui/Checkbox.tsx
index 32020bace5ba247b6a4e265d3342d4dab37fbbf1..bc87c5cc98ac323fc4609c5dd3f62363844a6540 100644
--- a/src/components/ui/Checkbox.tsx
+++ b/src/components/ui/Checkbox.tsx
@@ -31,6 +31,15 @@ const CheckboxBase = styled.label`
             background: var(--background);
         }
     }
+
+    &[disabled] {
+        opacity: 0.5;
+        cursor: unset;
+
+        &:hover {
+            background: unset;
+        }
+    }
 `;
 
 const CheckboxContent = styled.span`
@@ -52,6 +61,7 @@ const Checkmark = styled.div<{ checked: boolean }>`
     width: 24px;
     height: 24px;
     display: grid;
+    flex-shrink: 0;
     border-radius: 4px;
     place-items: center;
     transition: 0.2s ease all;
diff --git a/src/context/intermediate/Intermediate.tsx b/src/context/intermediate/Intermediate.tsx
index 8929806479e35ab5e067bec8e8976fc9b60fbafc..0b2929aed192b95d00fa620dad7fd4fc16db196e 100644
--- a/src/context/intermediate/Intermediate.tsx
+++ b/src/context/intermediate/Intermediate.tsx
@@ -31,7 +31,8 @@ export type Screen =
     { type: "create_channel", target: Servers.Server }
 )) |
 ({ id: "special_input" } & (
-    { type: "create_group" | "create_server" | "set_custom_status" | "add_friend" }
+    { type: "create_group" | "create_server" | "set_custom_status" | "add_friend" } |
+    { type: "create_role", server: string, callback: (id: string) => void }
 ))
 | {
       id: "_input";
diff --git a/src/context/intermediate/modals/Input.tsx b/src/context/intermediate/modals/Input.tsx
index f25b992189ef5d19fbd63d863ea467e98d42d799..d57ba8d81539a2a80e4e63217b5b0e572bc49e64 100644
--- a/src/context/intermediate/modals/Input.tsx
+++ b/src/context/intermediate/modals/Input.tsx
@@ -68,7 +68,8 @@ export function InputModal({
 }
 
 type SpecialProps = { onClose: () => void } & (
-    { type: "create_group" | "create_server" | "set_custom_status" | "add_friend" }
+    { type: "create_group" | "create_server" | "set_custom_status" | "add_friend" } |
+    { type: "create_role", server: string, callback: (id: string) => void }
 )
 
 export function SpecialInputModal(props: SpecialProps) {
@@ -112,6 +113,17 @@ export function SpecialInputModal(props: SpecialProps) {
                 }}
             />;
         }
+        case "create_role": {
+            return <InputModal
+                onClose={onClose}
+                question={<Text id="app.settings.permissions.create_role" />}
+                field={<Text id="app.settings.permissions.role_name" />}
+                callback={async name => {
+                    const role = await client.servers.createRole(props.server, name);
+                    props.callback(role.id);
+                }}
+            />;
+        }
         case "set_custom_status": {
             return <InputModal
                 onClose={onClose}
diff --git a/src/pages/settings/server/Panes.module.scss b/src/pages/settings/server/Panes.module.scss
index fe16fd40db95b2a469f1c9e9a2b4bed33cd306dc..8c1a2091dfb5f162bbd2d69d79b28eed73ffb9b8 100644
--- a/src/pages/settings/server/Panes.module.scss
+++ b/src/pages/settings/server/Panes.module.scss
@@ -57,7 +57,6 @@
 }
 
 .members {
-
     .subtitle {
         display: flex;
         justify-content: space-between;
@@ -84,15 +83,43 @@
 
     .list {
         width: 160px;
+        flex-shrink: 0;
         overflow-y: scroll;
     }
 
     .permissions {
         flex-grow: 1;
+        padding: 0 8px;
         overflow-y: scroll;
+        
+        section {
+            margin-bottom: 1em;
+        }
+    }
+
+    .title {
+        gap: 8px;
+        display: flex;
+        margin-bottom: 1em;
+        align-items: center;
+
+        h1, h2 {
+            margin: 0;
+            min-width: 0;
+            flex-grow: 1;
+            overflow: hidden;
+            white-space: nowrap;
+            text-overflow: ellipsis;
+        }
+
+        svg {
+            cursor: pointer;
+        }
     }
 
-    h2 {
-        margin: 8px 0;
+    .actions {
+        gap: 8px;
+        display: flex;
+        padding: 8px 0;
     }
 }
\ No newline at end of file
diff --git a/src/pages/settings/server/Roles.tsx b/src/pages/settings/server/Roles.tsx
index a4118092006e690d551fe94b11daa23ce9b30b33..db3887c3e650e8d1958d461b140d1135425f0420 100644
--- a/src/pages/settings/server/Roles.tsx
+++ b/src/pages/settings/server/Roles.tsx
@@ -1,101 +1,119 @@
 import { Text } from "preact-i18n";
 import styles from './Panes.module.scss';
 import Button from "../../../components/ui/Button";
+import Overline from "../../../components/ui/Overline";
 import { Servers } from "revolt.js/dist/api/objects";
-import InputBox from "../../../components/ui/InputBox";
 import Checkbox from "../../../components/ui/Checkbox";
 import { useContext, useEffect, useState } from "preact/hooks";
 import { AppContext } from "../../../context/revoltjs/RevoltClient";
 import { ChannelPermission, ServerPermission } from "revolt.js/dist/api/permissions";
 import Tip from "../../../components/ui/Tip";
+import IconButton from "../../../components/ui/IconButton";
+import ButtonItem from "../../../components/navigation/items/ButtonItem";
+import isEqual from 'lodash.isequal';
+import InputBox from "../../../components/ui/InputBox";
+import { Plus } from "@styled-icons/boxicons-regular";
+import { useIntermediate } from "../../../context/intermediate/Intermediate";
 
 interface Props {
     server: Servers.Server;
 }
 
+const I32ToU32 = (arr: number[]) => arr.map(x => x >>> 0);
+
 // ! FIXME: bad code :)
 export function Roles({ server }: Props) {
-    const [ selected, setSelected ] = useState('default');
+    const [ role, setRole ] = useState('default');
+    const { openScreen } = useIntermediate();
     const client = useContext(AppContext);
-
     const roles = server.roles ?? {};
-    const keys = [ 'default', ...Object.keys(roles) ];
-
-    const defaultRole = { name: 'Default', permissions: server.default_permissions };
-    const selectedRole: Servers.Role = selected === 'default' ? defaultRole : roles[selected];
 
-    if (!selectedRole) {
-        useEffect(() => setSelected('default'), [ ]);
-        return null;
+    if (role !== 'default' && typeof roles[role] === 'undefined') {
+        useEffect(() => setRole('default'));
+        return;
     }
 
-    const [ p, setPerm ] = useState([
-        selectedRole.permissions[0] >>> 0,
-        selectedRole.permissions[1] >>> 0,
-    ]);
-
-    useEffect(() => {
-        setPerm([
-            selectedRole.permissions[0] >>> 0,
-            selectedRole.permissions[1] >>> 0,
-        ]);
-    }, [ selected, selectedRole.permissions ]);
+    const v = (id: string) => I32ToU32(id === 'default' ? server.default_permissions : roles[id].permissions)
+    const [ perm, setPerm ] = useState(v(role));
+    useEffect(() => setPerm(v(role)), [ role, roles[role]?.permissions ]);
 
-    const [ name, setName ] = useState('');
+    const modified = !isEqual(perm, v(role));
+    const save = () => client.servers.setPermissions(server._id, role, { server: perm[0], channel: perm[1] });
+    const deleteRole = () => {
+        setRole('default');
+        client.servers.deleteRole(server._id, role);
+    };
     
     return (
         <div className={styles.roles}>
-            <Tip warning>This section is under construction.</Tip>
             <div className={styles.list}>
-                <h1><Text id="app.settings.server_pages.roles.title" /></h1>
-                { keys
+                <div className={styles.title}>
+                    <h1><Text id="app.settings.server_pages.roles.title" /></h1>
+                    <Plus size={16} onClick={() =>
+                        openScreen({ id: 'special_input', type: 'create_role', server: server._id, callback: id => setRole(id) })} />
+                </div>
+                { [ 'default', ...Object.keys(roles) ]
                     .map(id => {
-                        let role: Servers.Role = id === 'default' ? defaultRole : roles[id];
-
-                        return (
-                            <Checkbox checked={selected === id} onChange={selected => selected && setSelected(id)}>
-                                { role.name }
-                            </Checkbox>
-                        )
+                        if (id === 'default') {
+                            return (
+                                <ButtonItem active={role === 'default'} onClick={() => setRole('default')}>
+                                    <Text id="app.settings.permissions.default_role" />
+                                </ButtonItem>
+                            )
+                        } else {
+                            return (
+                                <ButtonItem active={role === id} onClick={() => setRole(id)}>
+                                    { roles[id].name }
+                                </ButtonItem>
+                            )
+                        }
                     })
                 }
-                <Button disabled={selected === 'default'} error onClick={() => {
-                    setSelected('default');
-                    client.servers.deleteRole(server._id, selected);
-                }}>delete role</Button><br/>
-                <InputBox placeholder="role name" value={name} onChange={e => setName(e.currentTarget.value)} />
-                <Button contrast onClick={() => {
-                    client.servers.createRole(server._id, name);
-                }}>create</Button>
             </div>
             <div className={styles.permissions}>
-                <h2>{ selectedRole.name }</h2>
-                { Object.keys(ServerPermission)
-                    .map(perm => {
-                        let value = ServerPermission[perm as keyof typeof ServerPermission];
+                <div className={styles.title}>
+                    <h2>{ role === 'default' ? <Text id="app.settings.permissions.default_role" /> : roles[role].name }</h2>
+                    <Button contrast disabled={!modified} onClick={save}>Save</Button>
+                </div>
+                <section>
+                    <Overline type="subtle"><Text id="app.settings.permissions.server" /></Overline>
+                    { Object.keys(ServerPermission)
+                        .map(key => {
+                            if (key === 'View') return;
+                            let value = ServerPermission[key as keyof typeof ServerPermission];
 
-                        return (
-                            <Checkbox checked={(p[0] & value) > 0} onChange={c => setPerm([ c ? (p[0] | value) : (p[0] ^ value), p[1] ])}>
-                                { perm }
-                            </Checkbox>
-                        )
-                    })
-                }
-                <h2>channel permmissions</h2>
-                { Object.keys(ChannelPermission)
-                    .map(perm => {
-                        let value = ChannelPermission[perm as keyof typeof ChannelPermission];
+                            return (
+                                <Checkbox checked={(perm[0] & value) > 0}
+                                    onChange={() => setPerm([ perm[0] ^ value, perm[1] ])}
+                                    description={<Text id={`permissions.server.${key}.d`} />}>
+                                    <Text id={`permissions.server.${key}.t`} />
+                                </Checkbox>
+                            )
+                        })
+                    }
+                </section>
+                <section>
+                    <Overline type="subtle"><Text id="app.settings.permissions.channel" /></Overline>
+                    { Object.keys(ChannelPermission)
+                        .map(key => {
+                            if (key === 'ManageChannel') return;
+                            let value = ChannelPermission[key as keyof typeof ChannelPermission];
 
-                        return (
-                            <Checkbox checked={((p[1] >>> 0) & value) > 0} onChange={c => setPerm([ p[0], c ? (p[1] | value) : (p[1] ^ value) ])}>
-                                { perm }
-                            </Checkbox>
-                        )
-                    })
-                }
-                <Button contrast onClick={() => {
-                    client.servers.setPermissions(server._id, selected, { server: p[0], channel: p[1] });
-                }}>click here to save permissions for role</Button>
+                            return (
+                                <Checkbox checked={((perm[1] >>> 0) & value) > 0}
+                                    onChange={() => setPerm([ perm[0], perm[1] ^ value ])}
+                                    disabled={key === 'View'}
+                                    description={<Text id={`permissions.channel.${key}.d`} />}>
+                                    <Text id={`permissions.channel.${key}.t`} />
+                                </Checkbox>
+                            )
+                        })
+                    }
+                </section>
+                <div className={styles.actions}>
+                    <Button contrast disabled={!modified} onClick={save}>Save</Button>
+                    { role !== 'default' && <Button contrast error onClick={deleteRole}>Delete</Button> }
+                </div>
             </div>
         </div>
     );
diff --git a/yarn.lock b/yarn.lock
index 1a95434bc21aa725e1bf33fe3b815d17b8b8622e..292ec6d867db8b148850660cb26625e625efe509 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3420,10 +3420,10 @@ reusify@^1.0.4:
   resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
   integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
 
-revolt.js@4.3.3-alpha.6:
-  version "4.3.3-alpha.6"
-  resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-4.3.3-alpha.6.tgz#054e685a5c0dac2c7ae3e2aa454d1965218cb2b0"
-  integrity sha512-u1/xf+YSQr8DbKsO0raym+F05R75bqYadrPWaIie3m2s2p7ZWeamHlfWIKJlmDO5AL+Lg3xoZWoLwuRHrD1K/Q==
+revolt.js@4.3.3-alpha.7:
+  version "4.3.3-alpha.7"
+  resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-4.3.3-alpha.7.tgz#de6ecef444e8368aac3753761e2e10f516f50712"
+  integrity sha512-oi76A+EIxrD+tVRTU8s2LISFBpvMf0kpinw5rdukoc1VWpl0bCC6Kko26yC7lhVkWGLTZxHMOKaUkgbOgy0flA==
   dependencies:
     "@insertish/mutable" "1.1.0"
     axios "^0.19.2"