diff --git a/onlylegs/api.py b/onlylegs/api.py
index 44c187c..0c68f82 100644
--- a/onlylegs/api.py
+++ b/onlylegs/api.py
@@ -3,7 +3,6 @@ Onlylegs - API endpoints
 """
 import os
 import pathlib
-import re
 import logging
 from uuid import uuid4
 
@@ -23,88 +22,13 @@ from flask_login import login_required, current_user
 from colorthief import ColorThief
 
 from onlylegs.extensions import db
-from onlylegs.models import Users, Pictures, Exif
+from onlylegs.models import Pictures, Exif
 from onlylegs.utils.generate_image import generate_thumbnail
 
 
 blueprint = Blueprint("api", __name__, url_prefix="/api")
 
 
-@blueprint.route("/account/picture/<int:user_id>", methods=["POST"])
-@login_required
-def account_picture(user_id):
-    """
-    Returns the profile of a user
-    """
-    user = db.get_or_404(Users, user_id)
-    file = request.files.get("file", None)
-
-    # If no image is uploaded, return 404 error
-    if not file:
-        return jsonify({"error": "No file uploaded"}), 400
-    if user.id != current_user.id:
-        return jsonify({"error": "You are not allowed to do this, go away"}), 403
-
-    # Get file extension, generate random name and set file path
-    img_ext = pathlib.Path(file.filename).suffix.replace(".", "").lower()
-    img_name = str(user.id)
-    img_path = os.path.join(current_app.config["PFP_FOLDER"], img_name + "." + img_ext)
-
-    # Check if file extension is allowed
-    if img_ext not in current_app.config["ALLOWED_EXTENSIONS"].keys():
-        logging.info("File extension not allowed: %s", img_ext)
-        return jsonify({"error": "File extension not allowed"}), 403
-
-    if user.picture:
-        # Delete cached files and old image
-        os.remove(os.path.join(current_app.config["PFP_FOLDER"], user.picture))
-        cache_name = user.picture.rsplit(".")[0]
-        for cache_file in pathlib.Path(current_app.config["CACHE_FOLDER"]).glob(
-            cache_name + "*"
-        ):
-            os.remove(cache_file)
-
-    # Save file
-    try:
-        file.save(img_path)
-    except OSError as err:
-        logging.info("Error saving file %s because of %s", img_path, err)
-        return jsonify({"error": "Error saving file"}), 500
-
-    img_colors = ColorThief(img_path).get_color()
-
-    # Save to database
-    user.colour = img_colors
-    user.picture = str(img_name + "." + img_ext)
-    db.session.commit()
-
-    return jsonify({"message": "File uploaded"}), 200
-
-
-@blueprint.route("/account/username/<int:user_id>", methods=["POST"])
-@login_required
-def account_username(user_id):
-    """
-    Returns the profile of a user
-    """
-    user = db.get_or_404(Users, user_id)
-    new_name = request.form["name"]
-
-    username_regex = re.compile(r"\b[A-Za-z0-9._-]+\b")
-
-    # Validate the form
-    if not new_name or not username_regex.match(new_name):
-        return jsonify({"error": "Username is invalid"}), 400
-    if user.id != current_user.id:
-        return jsonify({"error": "You are not allowed to do this, go away"}), 403
-
-    # Save to database
-    user.username = new_name
-    db.session.commit()
-
-    return jsonify({"message": "Username changed"}), 200
-
-
 @blueprint.route("/media/<path:path>", methods=["GET"])
 def media(path):
     """
diff --git a/onlylegs/auth.py b/onlylegs/auth.py
index c3d0698..4394b31 100644
--- a/onlylegs/auth.py
+++ b/onlylegs/auth.py
@@ -105,5 +105,5 @@ def logout():
     Clear the current session, including the stored user id
     """
     logout_user()
-    flash(["Goodbye!!!", "4"])
+    flash("Goodbye!!!", "4")
     return redirect(url_for("gallery.index"))
diff --git a/onlylegs/config.py b/onlylegs/config.py
index 2b792d4..3300411 100644
--- a/onlylegs/config.py
+++ b/onlylegs/config.py
@@ -44,9 +44,10 @@ WEBSITE_CONF = conf["website"]
 
 # Directories
 UPLOAD_FOLDER = os.path.join(APPLICATION_ROOT, "media", "uploads")
+MEDIA_FOLDER = os.path.join(APPLICATION_ROOT, "media")
 CACHE_FOLDER = os.path.join(APPLICATION_ROOT, "media", "cache")
 PFP_FOLDER = os.path.join(APPLICATION_ROOT, "media", "pfp")
-MEDIA_FOLDER = os.path.join(APPLICATION_ROOT, "media")
+BANNER_FOLDER = os.path.join(APPLICATION_ROOT, "media", "banner")
 
 # Database
 INSTANCE_DIR = os.path.join(APPLICATION_ROOT, "instance")
diff --git a/onlylegs/filters.py b/onlylegs/filters.py
index bdecca5..1f4515b 100644
--- a/onlylegs/filters.py
+++ b/onlylegs/filters.py
@@ -18,7 +18,11 @@ def colour_contrast(colour):
     "color: var(--fg-white);" or "color: var(--fg-black);"
     """
     colour_obj = colour_utils.Colour(colour)
-    return "rgb(var(--fg-black));" if colour_obj.is_light() else "rgb(var(--fg-white));"
+    return (
+        "var(--foreground-black);"
+        if colour_obj.is_light()
+        else "var(--foreground-white);"
+    )
 
 
 @blueprint.app_template_filter()
diff --git a/onlylegs/static/sass/components/banner.sass b/onlylegs/static/sass/components/banner.sass
index a88b2c3..724a5ee 100644
--- a/onlylegs/static/sass/components/banner.sass
+++ b/onlylegs/static/sass/components/banner.sass
@@ -44,7 +44,7 @@
         width: 100%
         height: 100%
 
-        background: linear-gradient(to right, var(--background-100), transparent)
+        background: linear-gradient(to right, var(--background-100), transparent 80%, var(--background-100) 100%)
 
         z-index: +1
 
@@ -157,7 +157,7 @@
             margin-left: auto
             width: auto
 
-@media (max-width: var(--breakpoint))
+@media (max-width: 800px)
     .banner,
     .banner-small
         &::after
@@ -169,7 +169,7 @@
         max-height: 30vh
 
         .banner-filter
-            background: linear-gradient(to bottom, var(--background-100), transparent)
+            background: linear-gradient(to top, var(--background-100), transparent)
 
         .banner-content
             padding: 0.5rem
diff --git a/onlylegs/static/sass/components/buttons/block.sass b/onlylegs/static/sass/components/buttons/block.sass
index 121aba2..7994647 100644
--- a/onlylegs/static/sass/components/buttons/block.sass
+++ b/onlylegs/static/sass/components/buttons/block.sass
@@ -39,35 +39,35 @@
 
         &:hover, &:focus-visible
             background-color: var(--primary)
-            color: var(--white)
+            color: var(--black)
     &.success
         background-color: var(--success-transparent)
         color: var(--success)
 
         &:hover, &:focus-visible
             background-color: var(--success)
-            color: var(--white)
+            color: var(--black)
     &.warning
         background-color: var(--warning-transparent)
         color: var(--warning)
 
         &:hover, &:focus-visible
             background-color: var(--warning)
-            color: var(--white)
+            color: var(--black)
     &.critical
         background-color: var(--danger-transparent)
         color: var(--danger)
 
         &:hover, &:focus-visible
             background-color: var(--danger)
-            color: var(--white)
+            color: var(--black)
     &.info
         background-color: var(--info-transparent)
         color: var(--info)
 
         &:hover, &:focus-visible
             background-color: var(--info)
-            color: var(--white)
+            color: var(--black)
     &.black
         background-color: var(--black-transparent)
         color: var(--white)
@@ -201,4 +201,4 @@
 
     &.error
         background-color: var(--danger)
-        color: var(--danger)
+        color: var(--black)
diff --git a/onlylegs/static/sass/components/buttons/top-of-page.sass b/onlylegs/static/sass/components/buttons/top-of-page.sass
index 1ce9cdb..dd356ba 100644
--- a/onlylegs/static/sass/components/buttons/top-of-page.sass
+++ b/onlylegs/static/sass/components/buttons/top-of-page.sass
@@ -13,7 +13,7 @@
     justify-content: center
     align-items: center
 
-    background-color: var(--background-300)
+    background-color: var(--background-100)
     color: var(--foreground-white)
     border-radius: calc(var(--rad) / 2)
     border: none
@@ -34,6 +34,6 @@
         right: 0.75rem
         opacity: 1
 
-@media (max-width: var(--breakpoint))
+@media (max-width: 800px)
     .top-of-page
         bottom: 4.25rem
diff --git a/onlylegs/static/sass/components/context-menu.sass b/onlylegs/static/sass/components/context-menu.sass
index 0a58aed..8d4b4aa 100644
--- a/onlylegs/static/sass/components/context-menu.sass
+++ b/onlylegs/static/sass/components/context-menu.sass
@@ -31,7 +31,7 @@
 
     overflow: hidden
 
-    transition: transform 0.2s ease-in-out, opacity 0.2s ease-in-out
+    transition: transform 0.35s var(--animation-bounce), opacity 0.35s var(--animation-bounce)
     transform-origin: center center
     opacity: 0.5
     transform: scale(0, 0)
diff --git a/onlylegs/static/sass/components/gallery.sass b/onlylegs/static/sass/components/gallery.sass
index 7194f63..081af88 100644
--- a/onlylegs/static/sass/components/gallery.sass
+++ b/onlylegs/static/sass/components/gallery.sass
@@ -26,7 +26,7 @@
 
     box-sizing: border-box
     overflow: hidden
-    transition: box-shadow 0.2s cubic-bezier(.79, .14, .15, .86)
+    transition: box-shadow 0.2s var(--animation-smooth)
 
     .image-filter
         margin: 0
@@ -47,7 +47,7 @@
         opacity: 0 // hide
 
         z-index: +4
-        transition: opacity 0.2s cubic-bezier(.79, .14, .15, .86)
+        transition: opacity 0.2s var(--animation-smooth)
 
         .image-title,
         .image-subtitle
@@ -163,7 +163,7 @@
             border-radius: calc(var(--rad) / 2)
             box-shadow: 0 0 0.4rem 0.25rem var(--black-transparent)
 
-            transition: transform 0.2s cubic-bezier(.79, .14, .15, .86)
+            transition: transform 0.2s var(--animation-smooth)
 
         &.size-1
             .data-1
@@ -218,5 +218,5 @@
         grid-template-columns: auto auto auto
 
     .gallery-item
-        margin: 0.35rem
+        margin: 0.1rem
         position: relative
\ No newline at end of file
diff --git a/onlylegs/static/sass/components/image-view.sass b/onlylegs/static/sass/components/image-view.sass
index 1edcd8a..473bdc6 100644
--- a/onlylegs/static/sass/components/image-view.sass
+++ b/onlylegs/static/sass/components/image-view.sass
@@ -58,13 +58,6 @@ details
             font-size: 1.1rem
             font-weight: 500
 
-    &[open]
-        summary
-            margin-bottom: 0.5rem
-
-            > i.collapse-indicator
-                transform: rotate(90deg)
-
     p
         margin: 0
         padding: 0
@@ -137,6 +130,16 @@ details
         tr:last-of-type td
             padding-bottom: 0
 
+    &[open]
+        summary
+            margin-bottom: 0.5rem
+
+            > i.collapse-indicator
+                transform: rotate(90deg)
+
+    &:last-of-type
+        margin-bottom: 0
+
 .img-colours
     width: 100%
 
diff --git a/onlylegs/static/sass/components/navigation.sass b/onlylegs/static/sass/components/navigation.sass
index a3d8cd9..9ea4332 100644
--- a/onlylegs/static/sass/components/navigation.sass
+++ b/onlylegs/static/sass/components/navigation.sass
@@ -78,7 +78,7 @@ nav
             background-color: currentColor
             border-radius: calc(var(--rad) / 2)
 
-@media (max-width: var(--breakpoint))
+@media (max-width: 800px)
     nav
         width: 100vw
         height: 3.5rem
diff --git a/onlylegs/static/sass/components/notification.sass b/onlylegs/static/sass/components/notification.sass
index d2c74b0..d7a3de5 100644
--- a/onlylegs/static/sass/components/notification.sass
+++ b/onlylegs/static/sass/components/notification.sass
@@ -32,7 +32,7 @@
 
     position: relative
 
-    background-color: var(--background-300)
+    background-color: var(--background-400)
     border-radius: calc(var(--rad) / 2)
     color: var(--foreground-white)
     opacity: 0
@@ -97,7 +97,7 @@
     justify-content: center
     align-items: center
 
-    background-color: var(--background-200)
+    background-color: var(--background-300)
 
     i
         font-size: 1.25rem
@@ -119,7 +119,7 @@
     line-height: 1
     text-align: left
 
-@media (max-width: var(--breakpoint))
+@media (max-width: 800px)
     .notifications
         bottom: 3.8rem
         width: calc(100vw - 0.6rem)
diff --git a/onlylegs/static/sass/components/pop-up.sass b/onlylegs/static/sass/components/pop-up.sass
index 565f63b..9542f13 100644
--- a/onlylegs/static/sass/components/pop-up.sass
+++ b/onlylegs/static/sass/components/pop-up.sass
@@ -38,12 +38,12 @@
         display: flex
         flex-direction: column
 
-        background-color: var(--background-200)
+        background-color: var(--background-400)
         border-radius: var(--rad)
         overflow: hidden
 
         z-index: +2
-        transition: transform 0.2s var(--animation-smooth)
+        transition: transform 0.2s var(--animation-bounce)
 
     .pop-up-header
         margin: 0 0 0.5rem 0
@@ -133,7 +133,7 @@
         .pop-up-wrapper
             transform: translate(-50%, 50%) scale(1)
 
-@media (max-width: var(--breakpoint))
+@media (max-width: 800px)
     .pop-up
         .pop-up-wrapper
             max-width: calc(100% - 0.75rem)
diff --git a/onlylegs/static/sass/components/tags.sass b/onlylegs/static/sass/components/tags.sass
index 346918a..b45b630 100644
--- a/onlylegs/static/sass/components/tags.sass
+++ b/onlylegs/static/sass/components/tags.sass
@@ -13,7 +13,7 @@
 
     border-radius: calc(var(--rad) / 2)
     border: none
-    background-color: var(--primary--transparent)
+    background-color: var(--primary-transparent)
     color: var(--primary)
 
     cursor: pointer
@@ -23,4 +23,4 @@
         font-size: 1.15rem
 
     &:hover
-        background-color: var(--primary--transparent)
+        background-color: var(--primary-transparent)
diff --git a/onlylegs/static/sass/components/upload-panel.sass b/onlylegs/static/sass/components/upload-panel.sass
index 04bb941..e4fb816 100644
--- a/onlylegs/static/sass/components/upload-panel.sass
+++ b/onlylegs/static/sass/components/upload-panel.sass
@@ -13,7 +13,7 @@
 
     overflow: hidden
     z-index: 68
-    transition: background-color 0.25s cubic-bezier(0.76, 0, 0.17, 1)
+    transition: background-color 0.25s var(--animation-smooth)
 
     h3
         margin: 0
@@ -70,7 +70,7 @@
 
         z-index: +2
 
-        transition: left 0.25s cubic-bezier(0.76, 0, 0.17, 1), bottom 0.25s cubic-bezier(0.76, 0, 0.17, 1)
+        transition: left 0.25s var(--animation-smooth), bottom 0.25s var(--animation-smooth)
 
         #dragIndicator
             display: none
@@ -97,7 +97,7 @@
                 background-color: var(--background-400)
                 border-radius: calc(var(--rad) / 2)
 
-                transition: width 0.25s var(--animation-bounce)
+                transition: width 0.25s var(--animation-smooth)
 
         &.dragging #dragIndicator::after
             width: 9rem
@@ -163,7 +163,7 @@
 
                 z-index: +3
 
-                transition: color 0.25s cubic-bezier(0.76, 0, 0.17, 1)
+                transition: color 0.25s var(--animation-smooth)
 
             .progress
                 width: 100%
@@ -175,10 +175,10 @@
 
                 background-color: var(--primary)
 
-                animation: uploadingLoop 1s cubic-bezier(0.76, 0, 0.17, 1) infinite
+                animation: uploadingLoop 1s var(--animation-smooth) infinite
 
                 z-index: +5
-                transition: left 1s cubic-bezier(0.76, 0, 0.17, 1)
+                transition: left 1s var(--animation-smooth)
 
             &.critical
                 .job__status, .progress
@@ -203,7 +203,7 @@
         .container
             left: 0
 
-@media (max-width: var(--breakpoint))
+@media (max-width: 800px)
     .upload-panel
         width: 100%
         height: calc(100vh - 3.5rem)
diff --git a/onlylegs/static/sass/style.sass b/onlylegs/static/sass/style.sass
index e607c97..19a6ec1 100644
--- a/onlylegs/static/sass/style.sass
+++ b/onlylegs/static/sass/style.sass
@@ -57,7 +57,7 @@ body
     color: var(--foreground-white)
 
     overflow-x: hidden
-@media (max-width: var(--breakpoint))
+@media (max-width: 800px)
     body
         padding: 0 0 3.5rem 0
 
@@ -71,10 +71,13 @@ main
     color: var(--foreground-black)
     border-radius: var(--rad)
     overflow: hidden
-@media (max-width: var(--breakpoint))
+@media (max-width: 800px)
     main
         margin: 0 0.5rem
-        // border-radius: 0
+
+header
+    position: sticky
+    top: 0
 
 .error-page
     min-height: 100%
@@ -100,7 +103,7 @@ main
         font-size: 1.25rem
         font-weight: 400
         text-align: center
-@media (max-width: var(--breakpoint))
+@media (max-width: 800px)
     .error-page
         h1
             font-size: 4.5rem
diff --git a/onlylegs/static/sass/variables.sass b/onlylegs/static/sass/variables.sass
index 754caa2..8f5818f 100644
--- a/onlylegs/static/sass/variables.sass
+++ b/onlylegs/static/sass/variables.sass
@@ -1,6 +1,6 @@
 \:root
-    --background-hsl-hue: 0
-    --background-hsl-saturation: 2%
+    --background-hsl-hue: 69
+    --background-hsl-saturation: 25%
 
     --background-100: hsl(var(--background-hsl-hue), var(--background-hsl-saturation), 6%)
     --background-200: hsl(var(--background-hsl-hue), var(--background-hsl-saturation), 8%)
@@ -13,14 +13,15 @@
 
     --background-shade: hsl(var(--background-hsl-hue), var(--background-hsl-saturation), 6%, 0.5)
 
-    --foreground-gray: rgb(102, 102, 102)
     --foreground-white: rgb(232, 227, 227)
+    --foreground-gray: rgb(102, 102, 102)
     --foreground-black: rgb(16, 16, 16)
 
     --black: rgb(20, 20, 20)
     --black-transparent: rgba(20, 20, 20, 0.2)
     --white: rgb(232, 227, 227)
     --white-transparent: rgba(232, 227, 227, 0.2)
+
     --red: rgb(182, 100, 103)
     --red-transparent: rgba(182, 100, 103, 0.1)
     --orange: rgb(217, 140, 95)
@@ -33,12 +34,17 @@
     --blue-transparent: rgba(141, 163, 185, 0.1)
     --purple: rgb(169, 136, 176)
     --purple-transparent: rgba(169, 136, 176, 0.1)
-
     --primary: rgb(183, 169, 151)
+    --primary-transparent: rgba(183, 169, 151, 0.1)
+
     --warning: var(--orange)
+    --warning-transparent: var(--orange-transparent)
     --danger: var(--red)
+    --danger-transparent: var(--red-transparent)
     --success: var(--green)
+    --success-transparent: var(--green-transparent)
     --info: var(--blue)
+    --info-transparent: var(--blue-transparent)
 
     --rad: 0.4rem
 
@@ -47,4 +53,4 @@
 
     --breakpoint: 800px
 
-    --font-family: 'Rubik', sans-serif
+    --font-family: 'Switzer', sans-serif
diff --git a/onlylegs/templates/base.html b/onlylegs/templates/base.html
index 58b7bb2..403288f 100644
--- a/onlylegs/templates/base.html
+++ b/onlylegs/templates/base.html
@@ -17,9 +17,7 @@
     <meta name="twitter:description" content="{{ config.WEBSITE_CONF.motto }}">
 
     <!-- Fonts -->
-    <link rel="preconnect" href="https://fonts.googleapis.com">
-    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
-    <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap">
+    <link href="https://api.fontshare.com/v2/css?f[]=switzer@101,600,701,800,501,601,900,100,700,901,400,201,401,200,300,301,801,500&display=swap" rel="stylesheet">
 
     <!-- phosphor icons -->
     <script src="https://unpkg.com/@phosphor-icons/web"></script>
@@ -130,6 +128,10 @@
     <script type="text/javascript">
         keepSquare();
 
+        {% for message in get_flashed_messages() %}
+            addNotification('{{ message[0] }}', {{ message[1] }});
+        {% endfor %}
+
         const times = document.querySelectorAll('.time');
         for (let i = 0; i < times.length; i++) {
             // Remove milliseconds
@@ -191,10 +193,6 @@
                 }
             }
         }
-
-        {% for message in get_flashed_messages() %}
-            addNotification('{{ message[0] }}', {{ message[1] }});
-        {% endfor %}
     </script>
 
     {% block script %}{% endblock %}
diff --git a/onlylegs/templates/profile.html b/onlylegs/templates/profile.html
index 7880339..c0e42af 100644
--- a/onlylegs/templates/profile.html
+++ b/onlylegs/templates/profile.html
@@ -29,7 +29,7 @@
 {% block header %}
     <div class="banner">
         {% if user.banner %}
-            <img src="{{ url_for('static', filename='icon.png') }}" alt="Profile Banner" onload="imgFade(this)" style="opacity:0;"/>
+            <img src="{{ url_for('api.media', path='banner/' + user.banner) }}" alt="Profile Banner" onload="imgFade(this)" style="opacity:0;"/>
         {% else %}
             <img src="{{ url_for('static', filename='banner.png') }}" alt="Profile Banner" onload="imgFade(this)" style="opacity:0;"/>
         {% endif %}
diff --git a/onlylegs/templates/settings.html b/onlylegs/templates/settings.html
index 88216b4..7cad7ab 100644
--- a/onlylegs/templates/settings.html
+++ b/onlylegs/templates/settings.html
@@ -22,15 +22,20 @@
             <i class="ph ph-caret-down collapse-indicator"></i>
         </summary>
 
-        <form method="POST" action="{{ url_for('api.account_picture', user_id=current_user.id) }}" enctype="multipart/form-data">
+        <form method="POST" action="{{ url_for('settings.account_picture') }}" enctype="multipart/form-data">
             <h3>Profile Picture</h3>
             <input type="file" name="file" tab-index="-1"/>
-            <input type="submit" value="Upload" class="btn-block">
+            <button type="submit" class="btn-block">Change Profile Picture</button>
         </form>
-        <form method="POST" action="{{ url_for('api.account_username', user_id=current_user.id) }}" enctype="multipart/form-data">
+        <form method="POST" action="{{ url_for('settings.account_banner') }}" enctype="multipart/form-data">
+            <h3>Profile Banner</h3>
+            <input type="file" name="file" tab-index="-1"/>
+            <button type="submit" class="btn-block">Change Profile Banner</button>
+        </form>
+        <form method="POST" action="{{ url_for('settings.account_username') }}" enctype="multipart/form-data">
             <h3>Username</h3>
             <input type="text" name="name" class="input-block" value="{{ current_user.username }}" />
-            <input type="submit" value="Upload" class="btn-block"/>
+            <button type="submit" class="btn-block">Change Username</button>
         </form>
     </details>
 
@@ -40,10 +45,27 @@
             <i class="ph ph-caret-down collapse-indicator"></i>
         </summary>
 
-        <form method="POST" action="" enctype="multipart/form-data">
-                <h3>Email</h3>
-                <input type="text" name="email" class="input-block" value="{{ current_user.email }}" />
-                <input type="submit" value="Upload" class="btn-block"/>
-            </form>
+        <form method="POST" action="{{ url_for('settings.account_email') }}" enctype="multipart/form-data">
+            <h3>Email</h3>
+            <input type="text" name="email" class="input-block" value="{{ current_user.email }}" />
+            <input type="password" name="current" class="input-block" placeholder="Current Password" />
+            <button type="submit" class="btn-block">Change Email</button>
+        </form>
+        <form method="POST" action="{{ url_for('settings.account_password') }}" enctype="multipart/form-data">
+            <h3>Password</h3>
+            <input type="password" name="current" class="input-block" placeholder="Current Password" />
+            <input type="password" name="password" class="input-block" placeholder="New Password" />
+            <input type="password" name="confirm" class="input-block" placeholder="Confirm Password" />
+            <button type="submit" class="btn-block">Change Password</button>
+        </form>
+    </details>
+
+    <details open>
+        <summary>
+            <i class="ph ph-info"></i><h2>Server</h2><span style="width: 100%"></span>
+            <i class="ph ph-caret-down collapse-indicator"></i>
+        </summary>
+
+        <p>Nothing here :3</p>
     </details>
 {% endblock %}
diff --git a/onlylegs/utils/startup.py b/onlylegs/utils/startup.py
index 722b37f..c08fc3d 100644
--- a/onlylegs/utils/startup.py
+++ b/onlylegs/utils/startup.py
@@ -16,6 +16,7 @@ REQUIRED_DIRS = {
     "uploads": os.path.join(APPLICATION_ROOT, "media", "uploads"),
     "cache": os.path.join(APPLICATION_ROOT, "media", "cache"),
     "pfp": os.path.join(APPLICATION_ROOT, "media", "pfp"),
+    "banner": os.path.join(APPLICATION_ROOT, "media", "banner"),
 }
 
 EMAIL_REGEX = re.compile(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b")
@@ -28,11 +29,10 @@ def check_dirs():
     """
 
     for directory in REQUIRED_DIRS.values():
-        if os.path.exists(directory):
-            print("User directory already exists at:", directory)
-            return
-        os.makedirs(directory)
-        print("Created directory at:", directory)
+        if not os.path.exists(directory):
+            os.makedirs(directory)
+            print("Created directory at:", directory)
+        print("User directory already exists at:", directory)
 
 
 def check_env():
diff --git a/onlylegs/views/settings.py b/onlylegs/views/settings.py
index 389433b..7bf360e 100644
--- a/onlylegs/views/settings.py
+++ b/onlylegs/views/settings.py
@@ -1,17 +1,159 @@
 """
 OnlyLegs - Settings page
 """
-from flask import Blueprint, render_template
-from flask_login import login_required
+import os
+import pathlib
+import re
+import logging
+from colorthief import ColorThief
+from flask import (
+    Blueprint,
+    request,
+    current_app,
+    render_template,
+    flash,
+    redirect,
+    url_for,
+)
+from flask_login import login_required, current_user
+from werkzeug.security import check_password_hash, generate_password_hash
+from onlylegs.extensions import db
+from onlylegs.models import Users
 
 
 blueprint = Blueprint("settings", __name__, url_prefix="/settings")
 
 
-@blueprint.route("/")
+@blueprint.route("/", methods=["GET"])
 @login_required
 def general():
     """
     General settings page
     """
     return render_template("settings.html")
+
+
+@blueprint.route("/account/pfp", methods=["POST"])
+@login_required
+def account_picture():
+    user_record = Users.query.filter_by(id=current_user.id).first()
+    uploaded_file = request.files.get("file", None)
+    if not uploaded_file:
+        return "No file uploaded!", 400
+
+    image_mime = pathlib.Path(uploaded_file.filename).suffix.replace(".", "").lower()
+    image_name = str(user_record.id) + "_pfp." + image_mime
+    image_path = os.path.join(current_app.config["PFP_FOLDER"], image_name)
+
+    if image_mime not in current_app.config["ALLOWED_EXTENSIONS"].keys():
+        logging.info("File extension not allowed: %s", image_mime)
+        return "File extension not allowed", 403
+
+    if user_record.picture:
+        os.remove(os.path.join(current_app.config["PFP_FOLDER"], user_record.picture))
+        cache_name = user_record.picture.rsplit(".")[0]
+        for file in pathlib.Path(current_app.config["CACHE_FOLDER"]).glob(
+            cache_name + "*"
+        ):
+            os.remove(file)
+
+    uploaded_file.save(image_path)
+    image_colours = ColorThief(image_path).get_color()
+
+    user_record.colour = image_colours
+    user_record.picture = image_name
+    db.session.commit()
+
+    return "File uploaded", 200
+
+
+@blueprint.route("/account/banner", methods=["POST"])
+@login_required
+def account_banner():
+    user_record = Users.query.filter_by(id=current_user.id).first()
+    uploaded_file = request.files.get("file", None)
+    if not uploaded_file:
+        return "No file uploaded!", 400
+
+    image_mime = pathlib.Path(uploaded_file.filename).suffix.replace(".", "").lower()
+    image_name = str(user_record.id) + "_banner." + image_mime
+    image_path = os.path.join(current_app.config["BANNER_FOLDER"], image_name)
+
+    if image_mime not in current_app.config["ALLOWED_EXTENSIONS"].keys():
+        logging.info("File extension not allowed: %s", image_mime)
+        return "File extension not allowed", 403
+
+    if user_record.banner:
+        os.remove(os.path.join(current_app.config["BANNER_FOLDER"], user_record.banner))
+        cache_name = user_record.banner.rsplit(".")[0]
+        for file in pathlib.Path(current_app.config["CACHE_FOLDER"]).glob(
+            cache_name + "*"
+        ):
+            os.remove(file)
+
+    uploaded_file.save(image_path)
+    user_record.banner = image_name
+    db.session.commit()
+
+    return "File uploaded", 200
+
+
+@blueprint.route("/account/username", methods=["POST"])
+@login_required
+def account_username():
+    user_record = Users.query.filter_by(id=current_user.id).first()
+    new_username = request.form.get("username", "").strip()
+
+    username_regex = re.compile(r"\b[A-Za-z0-9._-]+\b")
+
+    if not new_username or not username_regex.match(new_username):
+        return "Username is invalid", 400
+
+    user_record.username = new_username
+    db.session.commit()
+
+    return "Username changed", 200
+
+
+@blueprint.route("/account/email", methods=["POST"])
+@login_required
+def account_email():
+    user_record = Users.query.filter_by(id=current_user.id).first()
+    current_password = request.form.get("current", "").strip()
+    new_email = request.form.get("email", "").strip()
+
+    email_regex = re.compile(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b")
+
+    if not current_password or not new_email:
+        return "Fill in all the fields!", 400
+    if not email_regex.match(new_email):
+        return "Email is invalid!", 400
+    if not check_password_hash(user_record.password, current_password):
+        return "Incorrect password!", 400
+
+    user_record.email = new_email
+    db.session.commit()
+
+    return "Email changed", 200
+
+
+@blueprint.route("/account/password", methods=["POST"])
+@login_required
+def account_password():
+    user_record = Users.query.filter_by(id=current_user.id).first()
+    current_password = request.form.get("current", "").strip()
+    new_password = request.form.get("password", "").strip()
+    new_confirm = request.form.get("confirm", "").strip()
+
+    if not current_password or not new_password or not new_confirm:
+        return "Fill in all the fields!", 400
+    if new_password != new_confirm:
+        return "Passwords do not match!", 400
+    if not check_password_hash(user_record.password, current_password):
+        return "Incorrect password!", 400
+
+    user_record.password = generate_password_hash(new_password, method="scrypt")
+    db.session.commit()
+
+    flash(["Password changed! You must login now", 0])
+    return redirect(url_for("auth.logout"))