refactor: convert color system to RGB channels for native Tailwind opacity
Root cause fix for all broken CSS variable opacity modifiers. Converted every hex CSS variable to space-separated RGB channels (e.g. #fda4af → 253 164 175) and updated Tailwind config to use rgb(var(...) / <alpha-value>). This makes bg-accent-rose/10, border-border-hard/50, etc. work natively everywhere — no more currentColor fallback. Reverted all previous explicit rgba() workarounds back to clean Tailwind syntax. Also fixed the voice connection wifi icon SVG (was bottom-left aligned, now centered).
This commit is contained in:
@@ -42,7 +42,7 @@ export function LoginPage() {
|
||||
|
||||
<form onSubmit={handleSubmit}>
|
||||
{error && (
|
||||
<div className="mb-4 p-3 bg-[rgba(253,164,175,0.10)] border border-[rgba(253,164,175,0.30)] rounded text-txt-danger text-sm">
|
||||
<div className="mb-4 p-3 bg-accent-rose/10 border border-accent-rose/30 rounded text-txt-danger text-sm">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -54,7 +54,7 @@ export function RegisterPage() {
|
||||
|
||||
<form onSubmit={handleSubmit}>
|
||||
{error && (
|
||||
<div className="mb-4 p-3 bg-[rgba(253,164,175,0.10)] border border-[rgba(253,164,175,0.30)] rounded text-txt-danger text-sm">
|
||||
<div className="mb-4 p-3 bg-accent-rose/10 border border-accent-rose/30 rounded text-txt-danger text-sm">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -161,7 +161,7 @@ export function FriendsPage() {
|
||||
</button>
|
||||
</form>
|
||||
{addStatus && (
|
||||
<div className={`text-sm p-3 rounded-lg border ${addStatus.type === 'success' ? 'text-txt-positive border-[rgba(134,239,172,0.20)] bg-[rgba(134,239,172,0.05)]' : 'text-txt-danger border-[rgba(253,164,175,0.20)] bg-[rgba(253,164,175,0.05)]'}`}>
|
||||
<div className={`text-sm p-3 rounded-lg border ${addStatus.type === 'success' ? 'text-txt-positive border-status-online/20 bg-status-online/5' : 'text-txt-danger border-accent-rose/20 bg-accent-rose/5'}`}>
|
||||
{addStatus.message}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -67,8 +67,8 @@ const aetherTheme = {
|
||||
...themes.oneDark,
|
||||
plain: {
|
||||
...themes.oneDark.plain,
|
||||
backgroundColor: 'var(--bg-elevated)',
|
||||
color: 'var(--text-message)',
|
||||
backgroundColor: 'rgb(var(--bg-elevated))',
|
||||
color: 'rgb(var(--text-message))',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -286,7 +286,7 @@ export function Message({ message, isCompact, isFirstInGroup }: MessageProps) {
|
||||
key={att.id}
|
||||
href={`/api/uploads/${att.filename}`}
|
||||
download={att.originalName}
|
||||
className="flex items-center gap-3 p-4 bg-[rgba(26,26,35,0.50)] rounded-lg border border-border-hard hover:bg-interactive-hover transition-all max-w-[400px] mt-1 group/att"
|
||||
className="flex items-center gap-3 p-4 bg-surface-channel/50 rounded-lg border border-border-hard hover:bg-interactive-hover transition-all max-w-[400px] mt-1 group/att"
|
||||
>
|
||||
<div className="p-2 bg-surface-base rounded text-txt-tertiary group-hover/att:text-txt-primary transition-colors">
|
||||
<svg className="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
|
||||
@@ -261,7 +261,7 @@ export function MessageInput({ channelId, channelName }: MessageInputProps) {
|
||||
|
||||
{/* File previews */}
|
||||
{files.length > 0 && (
|
||||
<div className="p-4 flex flex-wrap gap-4 bg-[rgba(26,26,35,0.30)]">
|
||||
<div className="p-4 flex flex-wrap gap-4 bg-surface-channel/30">
|
||||
{files.map((file, i) => (
|
||||
<div key={i} className="relative group bg-surface-channel rounded-lg p-2 max-w-[200px] shadow-elevation-low border border-border-hard">
|
||||
{file.type.startsWith('image/') ? (
|
||||
|
||||
@@ -32,7 +32,7 @@ export function TypingIndicator({ channelId }: TypingIndicatorProps) {
|
||||
return (
|
||||
<div className="h-[24px] px-4 flex items-center text-[12px] text-txt-primary font-medium select-none pointer-events-none">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex gap-[2px] bg-[rgba(37,37,48,0.20)] rounded-full px-2 py-1">
|
||||
<div className="flex gap-[2px] bg-surface-elevated/20 rounded-full px-2 py-1">
|
||||
<div className="w-[5px] h-[5px] bg-txt-message rounded-full animate-bounce" style={{ animationDelay: '0ms', animationDuration: '0.8s' }} />
|
||||
<div className="w-[5px] h-[5px] bg-txt-message rounded-full animate-bounce" style={{ animationDelay: '150ms', animationDuration: '0.8s' }} />
|
||||
<div className="w-[5px] h-[5px] bg-txt-message rounded-full animate-bounce" style={{ animationDelay: '300ms', animationDuration: '0.8s' }} />
|
||||
|
||||
@@ -619,7 +619,7 @@ function UserAreaPanel({
|
||||
}}
|
||||
className="w-full h-1.5 rounded-full appearance-none cursor-pointer accent-accent-primary bg-surface-base [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-3 [&::-webkit-slider-thumb]:h-3 [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-white [&::-webkit-slider-thumb]:shadow-md"
|
||||
style={{
|
||||
background: `linear-gradient(to right, var(--accent-primary) 0%, var(--accent-primary) ${inputVolume / 2}%, var(--interactive-muted) ${inputVolume / 2}%, var(--interactive-muted) 100%)`,
|
||||
background: `linear-gradient(to right, rgb(var(--accent-primary)) 0%, rgb(var(--accent-primary)) ${inputVolume / 2}%, rgb(var(--interactive-muted)) ${inputVolume / 2}%, rgb(var(--interactive-muted)) 100%)`,
|
||||
}}
|
||||
/>
|
||||
{/* Mic level meter */}
|
||||
@@ -705,7 +705,7 @@ function UserAreaPanel({
|
||||
}}
|
||||
className="w-full h-1.5 rounded-full appearance-none cursor-pointer bg-surface-base [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-3 [&::-webkit-slider-thumb]:h-3 [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-white [&::-webkit-slider-thumb]:shadow-md"
|
||||
style={{
|
||||
background: `linear-gradient(to right, var(--accent-primary) 0%, var(--accent-primary) ${outputVolume / 2}%, var(--interactive-muted) ${outputVolume / 2}%, var(--interactive-muted) 100%)`,
|
||||
background: `linear-gradient(to right, rgb(var(--accent-primary)) 0%, rgb(var(--accent-primary)) ${outputVolume / 2}%, rgb(var(--interactive-muted)) ${outputVolume / 2}%, rgb(var(--interactive-muted)) 100%)`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -15,7 +15,7 @@ function getMemberGroup(member: MemberWithUser, ownerId: string | undefined) {
|
||||
return {
|
||||
key: '__owner__',
|
||||
label: 'OWNER',
|
||||
color: ownerRole?.color ?? 'var(--accent-rose)',
|
||||
color: ownerRole?.color ?? 'rgb(var(--accent-rose))',
|
||||
position: Infinity,
|
||||
};
|
||||
}
|
||||
@@ -79,7 +79,7 @@ export function MemberSidebar() {
|
||||
return { color: sorted[0]!.color };
|
||||
}
|
||||
if (ownerId && member.userId === ownerId) {
|
||||
return { color: 'var(--accent-rose)' };
|
||||
return { color: 'rgb(var(--accent-rose))' };
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
@@ -104,7 +104,7 @@ export function ChannelSettingsModal() {
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="p-2 bg-[rgba(253,164,175,0.10)] border border-[rgba(253,164,175,0.30)] rounded text-txt-danger text-sm">
|
||||
<div className="p-2 bg-accent-rose/10 border border-accent-rose/30 rounded text-txt-danger text-sm">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
@@ -139,7 +139,7 @@ export function ChannelSettingsModal() {
|
||||
</div>
|
||||
|
||||
{isPrivate && !isFetching && (
|
||||
<div className="flex items-start gap-2 p-2 bg-[rgba(17,17,24,0.50)] rounded text-xs text-txt-tertiary">
|
||||
<div className="flex items-start gap-2 p-2 bg-surface-input/50 rounded text-xs text-txt-tertiary">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" className="flex-shrink-0 mt-0.5 text-txt-secondary">
|
||||
<path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z" />
|
||||
</svg>
|
||||
|
||||
@@ -48,7 +48,7 @@ export function CreateChannelModal() {
|
||||
<Modal isOpen={isOpen} onClose={closeModal} title="Create Channel">
|
||||
<form onSubmit={handleSubmit}>
|
||||
{error && (
|
||||
<div className="mb-3 p-2 bg-[rgba(253,164,175,0.10)] border border-[rgba(253,164,175,0.30)] rounded text-txt-danger text-sm">
|
||||
<div className="mb-3 p-2 bg-accent-rose/10 border border-accent-rose/30 rounded text-txt-danger text-sm">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -41,7 +41,7 @@ export function CreateServerModal() {
|
||||
<Modal isOpen={isOpen} onClose={closeModal} title="Create a Server">
|
||||
<form onSubmit={handleSubmit}>
|
||||
{error && (
|
||||
<div className="mb-3 p-2 bg-[rgba(253,164,175,0.10)] border border-[rgba(253,164,175,0.30)] rounded text-txt-danger text-sm">
|
||||
<div className="mb-3 p-2 bg-accent-rose/10 border border-accent-rose/30 rounded text-txt-danger text-sm">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -55,7 +55,7 @@ export function InviteModal() {
|
||||
Share this invite link with friends to let them join your server.
|
||||
</p>
|
||||
{error && (
|
||||
<div className="mb-3 p-2 bg-[rgba(253,164,175,0.10)] border border-[rgba(253,164,175,0.30)] rounded text-txt-danger text-sm">
|
||||
<div className="mb-3 p-2 bg-accent-rose/10 border border-accent-rose/30 rounded text-txt-danger text-sm">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -52,7 +52,7 @@ export function JoinServerModal() {
|
||||
Enter an invite code to join an existing server.
|
||||
</p>
|
||||
{error && (
|
||||
<div className="mb-3 p-2 bg-[rgba(253,164,175,0.10)] border border-[rgba(253,164,175,0.30)] rounded text-txt-danger text-sm">
|
||||
<div className="mb-3 p-2 bg-accent-rose/10 border border-accent-rose/30 rounded text-txt-danger text-sm">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -189,7 +189,7 @@ function StreamingLimitsPanel() {
|
||||
|
||||
{/* Save / Reset */}
|
||||
{saveError && (
|
||||
<div className="p-2 bg-[rgba(253,164,175,0.10)] border border-[rgba(253,164,175,0.30)] rounded text-txt-danger text-sm">{saveError}</div>
|
||||
<div className="p-2 bg-accent-rose/10 border border-accent-rose/30 rounded text-txt-danger text-sm">{saveError}</div>
|
||||
)}
|
||||
{saveSuccess && (
|
||||
<div className="p-2 bg-status-online/10 border border-status-online/30 rounded text-status-online text-sm">Settings saved</div>
|
||||
@@ -369,7 +369,7 @@ export function ServerSettingsModal() {
|
||||
{/* Content */}
|
||||
<div className="flex-1 min-w-0">
|
||||
{error && (
|
||||
<div className="mb-3 p-2 bg-[rgba(253,164,175,0.10)] border border-[rgba(253,164,175,0.30)] rounded text-txt-danger text-sm">{error}</div>
|
||||
<div className="mb-3 p-2 bg-accent-rose/10 border border-accent-rose/30 rounded text-txt-danger text-sm">{error}</div>
|
||||
)}
|
||||
|
||||
{tab === 'overview' && (
|
||||
|
||||
@@ -76,7 +76,7 @@ export function UserSettingsModal() {
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="p-2 bg-[rgba(253,164,175,0.10)] border border-[rgba(253,164,175,0.30)] rounded text-txt-danger text-sm">{error}</div>
|
||||
<div className="p-2 bg-accent-rose/10 border border-accent-rose/30 rounded text-txt-danger text-sm">{error}</div>
|
||||
)}
|
||||
{success && (
|
||||
<div className="p-2 bg-status-online/10 border border-status-online/30 rounded text-status-online text-sm">{success}</div>
|
||||
|
||||
@@ -115,7 +115,7 @@ export function VoiceControls() {
|
||||
title="Connection Info"
|
||||
>
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" className={qualityColor}>
|
||||
<path d="M1.5 21.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3zM3.14 15.75a.75.75 0 01-.09-1.06A8.46 8.46 0 0112 11a8.46 8.46 0 018.95 3.69.75.75 0 01-1.15.97A6.96 6.96 0 0012 12.5a6.96 6.96 0 00-7.8 3.16.75.75 0 01-1.06.09zM6.37 18.3a.75.75 0 01-.08-1.06A5.46 5.46 0 0112 15a5.46 5.46 0 015.71 2.24.75.75 0 01-1.14.97A3.96 3.96 0 0012 16.5a3.96 3.96 0 00-4.57 1.71.75.75 0 01-1.06.09z" />
|
||||
<path d="M1 9l2 2c4.97-4.97 13.03-4.97 18 0l2-2C16.93 2.93 7.08 2.93 1 9zm8 8l3 3 3-3c-1.65-1.66-4.34-1.66-6 0zm-4-4l2 2c2.76-2.76 7.24-2.76 10 0l2-2C15.14 9.14 8.87 9.14 5 13z" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ export function VoiceGrid({ participants }: VoiceGridProps) {
|
||||
|
||||
{/* Bottom strip of other tiles */}
|
||||
{!stripHidden && otherTiles.length > 0 && (
|
||||
<div className="h-[120px] flex-shrink-0 flex items-center justify-center gap-2 p-2 bg-[rgba(11,11,16,0.50)] overflow-x-auto no-scrollbar">
|
||||
<div className="h-[120px] flex-shrink-0 flex items-center justify-center gap-2 p-2 bg-surface-base/50 overflow-x-auto no-scrollbar">
|
||||
{otherTiles.map((t) => (
|
||||
<div
|
||||
key={t.key}
|
||||
|
||||
@@ -4,55 +4,55 @@
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
/* ── Matte Surfaces ── */
|
||||
--bg-base: #0b0b10;
|
||||
--bg-channel: #1a1a23;
|
||||
--bg-chat: #13131a;
|
||||
--bg-members: #1a1a23;
|
||||
--bg-elevated: #252530;
|
||||
--bg-input: #111118;
|
||||
/* ── Matte Surfaces (RGB channels for Tailwind opacity support) ── */
|
||||
--bg-base: 11 11 16;
|
||||
--bg-channel: 26 26 35;
|
||||
--bg-chat: 19 19 26;
|
||||
--bg-members: 26 26 35;
|
||||
--bg-elevated: 37 37 48;
|
||||
--bg-input: 17 17 24;
|
||||
--bg-overlay: rgba(0, 0, 0, 0.85);
|
||||
|
||||
/* ── Borders ── */
|
||||
--border-hard: #22222c;
|
||||
--border-soft: #2a2a36;
|
||||
--border-hard: 34 34 44;
|
||||
--border-soft: 42 42 54;
|
||||
|
||||
/* ── Pastel Accents ── */
|
||||
--accent-mint: #86efac;
|
||||
--accent-peach: #fca5a5;
|
||||
--accent-lavender: #c4b5fd;
|
||||
--accent-sky: #7dd3fc;
|
||||
--accent-amber: #fcd34d;
|
||||
--accent-rose: #fda4af;
|
||||
--accent-coral: #fb923c;
|
||||
--accent-mint: 134 239 172;
|
||||
--accent-peach: 252 165 165;
|
||||
--accent-lavender: 196 181 253;
|
||||
--accent-sky: 125 211 252;
|
||||
--accent-amber: 252 211 77;
|
||||
--accent-rose: 253 164 175;
|
||||
--accent-coral: 251 146 60;
|
||||
|
||||
/* ── Primary Action (replaces blurple) ── */
|
||||
--accent-primary: #7c6cf6;
|
||||
--accent-primary-hover: #6b5ce0;
|
||||
--accent-primary-active: #5f4fd0;
|
||||
--accent-primary: 124 108 246;
|
||||
--accent-primary-hover: 107 92 224;
|
||||
--accent-primary-active: 95 79 208;
|
||||
|
||||
/* ── Text Hierarchy ── */
|
||||
--text-primary: #efefef;
|
||||
--text-secondary: #a0a0aa;
|
||||
--text-tertiary: #5c5c68;
|
||||
--text-category: #484854;
|
||||
--text-message: #d8d8de;
|
||||
--text-link: #7dd3fc;
|
||||
--text-positive: #86efac;
|
||||
--text-warning: #fcd34d;
|
||||
--text-danger: #fca5a5;
|
||||
--text-primary: 239 239 239;
|
||||
--text-secondary: 160 160 170;
|
||||
--text-tertiary: 92 92 104;
|
||||
--text-category: 72 72 84;
|
||||
--text-message: 216 216 222;
|
||||
--text-link: 125 211 252;
|
||||
--text-positive: 134 239 172;
|
||||
--text-warning: 252 211 77;
|
||||
--text-danger: 252 165 165;
|
||||
|
||||
/* ── Interactive ── */
|
||||
--interactive-hover: rgba(255, 255, 255, 0.06);
|
||||
--interactive-active: rgba(255, 255, 255, 0.12);
|
||||
--interactive-selected: rgba(255, 255, 255, 0.16);
|
||||
--interactive-muted: #3a3a44;
|
||||
--interactive-muted: 58 58 68;
|
||||
|
||||
/* ── Status ── */
|
||||
--status-online: #86efac;
|
||||
--status-idle: #fcd34d;
|
||||
--status-dnd: #fca5a5;
|
||||
--status-offline: #3a3a44;
|
||||
--status-online: 134 239 172;
|
||||
--status-idle: 252 211 77;
|
||||
--status-dnd: 252 165 165;
|
||||
--status-offline: 58 58 68;
|
||||
|
||||
/* ── Glass Material ── */
|
||||
--glass-bg: rgba(20, 20, 26, 0.52);
|
||||
@@ -60,7 +60,7 @@
|
||||
--glass-highlight: rgba(255, 255, 255, 0.05);
|
||||
|
||||
/* ── Notification ── */
|
||||
--notification: #fda4af;
|
||||
--notification: 253 164 175;
|
||||
}
|
||||
|
||||
* {
|
||||
@@ -70,7 +70,7 @@
|
||||
|
||||
::selection {
|
||||
background: rgba(134, 239, 172, 0.25);
|
||||
color: var(--text-primary);
|
||||
color: rgb(var(--text-primary));
|
||||
}
|
||||
|
||||
html, body, #root {
|
||||
@@ -92,14 +92,14 @@
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: var(--bg-input);
|
||||
background-color: rgb(var(--bg-input));
|
||||
border-radius: 9999px;
|
||||
border: 2px solid transparent;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background-color: var(--bg-channel);
|
||||
background-color: rgb(var(--bg-channel));
|
||||
}
|
||||
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
@@ -180,23 +180,23 @@
|
||||
.glass {
|
||||
backdrop-filter: none;
|
||||
-webkit-backdrop-filter: none;
|
||||
background: var(--bg-channel);
|
||||
background: rgb(var(--bg-channel));
|
||||
}
|
||||
.glass-strip {
|
||||
backdrop-filter: none;
|
||||
-webkit-backdrop-filter: none;
|
||||
background: var(--bg-channel);
|
||||
background: rgb(var(--bg-channel));
|
||||
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.20);
|
||||
}
|
||||
.glass-bubble {
|
||||
backdrop-filter: none;
|
||||
-webkit-backdrop-filter: none;
|
||||
background: var(--bg-elevated);
|
||||
background: rgb(var(--bg-elevated));
|
||||
}
|
||||
.glass-pill {
|
||||
backdrop-filter: none;
|
||||
-webkit-backdrop-filter: none;
|
||||
background: var(--bg-elevated);
|
||||
background: rgb(var(--bg-elevated));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user