diff --git a/src/components/common/messaging/MessageBase.tsx b/src/components/common/messaging/MessageBase.tsx
index afddb0d5d87150e4b43d9afa1609e7683143d39b..1ed75ffdae52bd8c55c65e2bc57c1a803e9aa0d1 100644
--- a/src/components/common/messaging/MessageBase.tsx
+++ b/src/components/common/messaging/MessageBase.tsx
@@ -151,7 +151,7 @@ export const MessageContent = styled.div`
     flex-grow: 1;
     display: flex;
     // overflow: hidden;
-    font-size: 0.875rem;
+    font-size: var(--text-size);
     flex-direction: column;
     justify-content: center;
 `;
diff --git a/src/components/common/messaging/MessageBox.tsx b/src/components/common/messaging/MessageBox.tsx
index 340d073b00dd486a188a93c25e343080ed0c4425..1243cbb397ece98953cf8c89f1cc427f0b443180 100644
--- a/src/components/common/messaging/MessageBox.tsx
+++ b/src/components/common/messaging/MessageBox.tsx
@@ -65,7 +65,7 @@ const Base = styled.div`
     background: var(--message-box);
 
     textarea {
-        font-size: 0.875rem;
+        font-size: var(--text-size);
         background: transparent;
     }
 `;
@@ -75,7 +75,7 @@ const Blocked = styled.div`
     align-items: center;
     padding: 14px 0;
     user-select: none;
-    font-size: 0.875rem;
+    font-size: var(--text-size);
     color: var(--tertiary-foreground);
 
     svg {
@@ -423,10 +423,10 @@ function MessageBox({ channel, draft }: Props) {
                     autoFocus
                     hideBorder
                     maxRows={20}
-                    padding={12}
                     id="message"
-                    value={draft ?? ""}
                     onKeyUp={onKeyUp}
+                    value={draft ?? ""}
+                    padding="var(--message-box-padding)"
                     onKeyDown={(e) => {
                         if (onKeyDown(e)) return;
 
diff --git a/src/components/ui/ComboBox.tsx b/src/components/ui/ComboBox.tsx
index 5d5c796308ac92f1b9855dfe7c7d4f23bce62205..5b794b49fe5f839991eaec0c662a23f20dfa7356 100644
--- a/src/components/ui/ComboBox.tsx
+++ b/src/components/ui/ComboBox.tsx
@@ -6,7 +6,7 @@ export default styled.select`
     font-family: inherit;
     color: var(--secondary-foreground);
     background: var(--secondary-background);
-    font-size: 0.875rem;
+    font-size: var(--text-size);
     border: none;
     outline: 2px solid transparent;
     transition: outline-color 0.2s ease-in-out;
diff --git a/src/components/ui/TextArea.tsx b/src/components/ui/TextArea.tsx
index ab087d5762c85703bc1c558f918149eccd3240a2..1a2016bcbf7f0837e5a598f7e93c224788a8cb95 100644
--- a/src/components/ui/TextArea.tsx
+++ b/src/components/ui/TextArea.tsx
@@ -2,8 +2,8 @@ import styled, { css } from "styled-components";
 
 export interface TextAreaProps {
     code?: boolean;
-    padding?: number;
-    lineHeight?: number;
+    padding?: string;
+    lineHeight?: string;
     hideBorder?: boolean;
 }
 
@@ -17,8 +17,8 @@ export default styled.textarea<TextAreaProps>`
     display: block;
     color: var(--foreground);
     background: var(--secondary-background);
-    padding: ${(props) => props.padding ?? DEFAULT_TEXT_AREA_PADDING}px;
-    line-height: ${(props) => props.lineHeight ?? DEFAULT_LINE_HEIGHT}px;
+    padding: ${(props) => (props.padding) ?? 'var(--textarea-padding)'};
+    line-height: ${(props) => (props.lineHeight) ?? 'var(--textarea-line-height)'};
 
     ${(props) =>
         props.hideBorder &&
@@ -31,7 +31,7 @@ export default styled.textarea<TextAreaProps>`
         css`
             border-radius: 4px;
             transition: border-color 0.2s ease-in-out;
-            border: ${TEXT_AREA_BORDER_WIDTH}px solid transparent;
+            border: var(--input-border-width) solid transparent;
         `}
 
     &:focus {
@@ -40,7 +40,7 @@ export default styled.textarea<TextAreaProps>`
         ${(props) =>
             !props.hideBorder &&
             css`
-                border: ${TEXT_AREA_BORDER_WIDTH}px solid var(--accent);
+                border: var(--input-border-width) solid var(--accent);
             `}
     }
 
diff --git a/src/lib/TextAreaAutoSize.tsx b/src/lib/TextAreaAutoSize.tsx
index b067b95e65016c8b3242cda1de814a2330533304..5660dedfa9e030344c8bb2c0d83f45e606718733 100644
--- a/src/lib/TextAreaAutoSize.tsx
+++ b/src/lib/TextAreaAutoSize.tsx
@@ -1,4 +1,6 @@
-import { useEffect, useRef } from "preact/hooks";
+import styled from "styled-components";
+
+import { useEffect, useLayoutEffect, useRef } from "preact/hooks";
 
 import TextArea, {
     DEFAULT_LINE_HEIGHT,
@@ -12,7 +14,7 @@ import { isTouchscreenDevice } from "./isTouchscreenDevice";
 
 type TextAreaAutoSizeProps = Omit<
     JSX.HTMLAttributes<HTMLTextAreaElement>,
-    "style" | "value"
+    "style" | "value" | "onChange"
 > &
     TextAreaProps & {
         forceFocus?: boolean;
@@ -22,8 +24,37 @@ type TextAreaAutoSizeProps = Omit<
         value: string;
 
         id?: string;
+
+        onChange?: (ev: JSX.TargetedEvent<HTMLTextAreaElement, Event>) => void;
     };
 
+const Container = styled.div`
+    flex-grow: 1;
+    display: flex;
+    flex-direction: column;
+`;
+
+const Ghost = styled.div<{ lineHeight: string, maxRows: number }>`
+    flex: 0;
+    width: 100%;
+    overflow: hidden;
+    visibility: hidden;
+    position: relative;
+
+    > div {
+        width: 100%;
+        white-space: pre-wrap;
+        word-break: break-all;
+        
+        top: 0;
+        position: absolute;
+        font-size: var(--text-size);
+        line-height: ${(props) => props.lineHeight};
+
+        max-height: calc(calc( ${(props) => props.lineHeight} * ${ (props) => props.maxRows } ));
+    }
+`;
+
 export default function TextAreaAutoSize(props: TextAreaAutoSizeProps) {
     const {
         autoFocus,
@@ -39,19 +70,13 @@ export default function TextAreaAutoSize(props: TextAreaAutoSizeProps) {
         onChange,
         ...textAreaProps
     } = props;
-    const line = lineHeight ?? DEFAULT_LINE_HEIGHT;
-
-    const heightPadding =
-        ((padding ?? DEFAULT_TEXT_AREA_PADDING) +
-            (hideBorder ? 0 : TEXT_AREA_BORDER_WIDTH)) *
-        2;
-    const height = Math.max(
-        Math.min(value.split("\n").length, maxRows ?? Infinity) * line +
-            heightPadding,
-        minHeight ?? 0,
-    );
 
     const ref = useRef<HTMLTextAreaElement>();
+    const ghost = useRef<HTMLDivElement>();
+
+    useLayoutEffect(() => {
+        ref.current.style.height = ghost.current.clientHeight + 'px';
+    }, [ghost, props.value]);
 
     useEffect(() => {
         if (isTouchscreenDevice) return;
@@ -101,18 +126,29 @@ export default function TextAreaAutoSize(props: TextAreaAutoSizeProps) {
     }, [ref]);
 
     return (
-        <TextArea
-            ref={ref}
-            value={value}
-            padding={padding}
-            style={{ height }}
-            hideBorder={hideBorder}
-            lineHeight={lineHeight}
-
-            onChange={ev => {
-                onChange && onChange(ev);
-            }}
-            {...textAreaProps}
-        />
+        <Container>
+            <TextArea
+                ref={ref}
+                value={value}
+                padding={padding}
+                style={{ height: minHeight }}
+                hideBorder={hideBorder}
+                lineHeight={lineHeight}
+                onChange={(ev) => {
+                    onChange && onChange(ev);
+                }}
+                {...textAreaProps}
+            />
+            <Ghost lineHeight={lineHeight ?? 'var(--textarea-line-height)'} maxRows={maxRows ?? 5}>
+                <div ref={ghost} style={{ padding }}>
+                    {props.value
+                        ? props.value
+                              .split("\n")
+                              .map((x) => `\u200e${x}`)
+                              .join("\n")
+                        : undefined ?? "‎\n"}
+                </div>
+            </Ghost>
+        </Container>
     );
 }
diff --git a/src/pages/channels/messaging/MessageEditor.tsx b/src/pages/channels/messaging/MessageEditor.tsx
index 3aa1a43c3ca50252fdde210d1da1ee9de103af83..ba93b91b10ba5f3955985a7ca21b9832c157623a 100644
--- a/src/pages/channels/messaging/MessageEditor.tsx
+++ b/src/pages/channels/messaging/MessageEditor.tsx
@@ -23,9 +23,9 @@ const EditorBase = styled.div`
     textarea {
         resize: none;
         padding: 12px;
-        font-size: 0.875rem;
         border-radius: 3px;
         white-space: pre-wrap;
+        font-size: var(--text-size);
         background: var(--secondary-header);
     }
 
@@ -101,9 +101,9 @@ export default function MessageEditor({ message, finish }: Props) {
             <TextAreaAutoSize
                 forceFocus
                 maxRows={3}
-                padding={12}
                 value={content}
                 maxLength={2000}
+                padding="var(--message-box-padding)"
                 onChange={(ev) => {
                     onChange(ev);
                     setContent(ev.currentTarget.value);
diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss
index ec7151d241f964edb74c5455e36db886a34a128b..21a9b500c3845e34fd2213ec722415c0c04c91af 100644
--- a/src/styles/_variables.scss
+++ b/src/styles/_variables.scss
@@ -1,9 +1,16 @@
 :root {
     --ligatures: none;
+    --text-size: 14px;
     --font: "Open Sans";
     --app-height: 100vh;
     --codeblock-font: "Fira Code";
     --sidebar-active: var(--secondary-background);
+    
+    --input-border-width: 2px;
+
+    --textarea-padding: 16px;
+    --textarea-line-height: 20px;
+    --message-box-padding: 12px;
 
     --bottom-navigation-height: 50px;
 }