From 78d11b8f27f9506cb0c470b6687773a0d81b9dce Mon Sep 17 00:00:00 2001 From: denis0001-dev Date: Sun, 15 Mar 2026 17:01:47 +0300 Subject: [PATCH] Improve styles, mobile adaptation, restructure code --- frontend/src/pages/home/HomePage.tsx | 155 +++++- frontend/src/pages/home/home.module.scss | 603 ++++++++++++++--------- 2 files changed, 492 insertions(+), 266 deletions(-) diff --git a/frontend/src/pages/home/HomePage.tsx b/frontend/src/pages/home/HomePage.tsx index 58658bc..085896a 100644 --- a/frontend/src/pages/home/HomePage.tsx +++ b/frontend/src/pages/home/HomePage.tsx @@ -2,26 +2,50 @@ import { Link, useNavigate } from "react-router-dom"; import { useUserStore } from "@/state/user"; import styles from "./home.module.scss"; import useDownloadAppScreen from "@/core/hooks/useDownloadAppScreen"; -import { MaterialButton, MaterialIcon } from "@/utils/material"; +import { MaterialButton, MaterialIcon, MaterialIconButton } from "@/utils/material"; import generalChatScreenshot from "../../images/screenshots/general-chat.png"; import dmScreenshot from "../../images/screenshots/dm.png"; -import telegramIcon from "../../images/telegram.svg"; -import maxIcon from "../../images/max.svg"; import type { ReactNode } from "react"; const GITHUB_WEB = "https://github.com/fromchat-messenger/web"; const GITHUB_APP = "https://github.com/fromchat-messenger/app"; const GITHUB_LICENSE = `${GITHUB_WEB}/blob/main/LICENSE`; -function GitHubLink({ children, className }: { children: React.ReactNode; className?: string }) { +function GitHubLink({ + children, + className, +}: { + children: React.ReactNode; + className?: string; +}) { return ( - {children} + + {children} + ); } -function SupportLink({ children, className }: { children: React.ReactNode; className?: string }) { +function SupportLink({ + children, + className, +}: { + children: React.ReactNode; + className?: string; +}) { return ( - {children} + + {children} + ); } @@ -32,7 +56,12 @@ interface FeatureSectionProps { right?: boolean; } -function FeatureSection({title, children, screenshot, right = false}: FeatureSectionProps) { +function FeatureSection({ + title, + children, + screenshot, + right = false, +}: FeatureSectionProps) { const featureText = (
{title}
@@ -43,7 +72,7 @@ function FeatureSection({title, children, screenshot, right = false}: FeatureSec const featureScreenshot = (
- +
) @@ -71,16 +100,22 @@ export default function HomePage() { } const openBtn = ( - + {isMobile ? "Скачать" : isLoggedIn ? "Открыть" : "Войти"} ); return (
-
-
+
@@ -96,6 +131,14 @@ export default function HomePage() {
{openBtn} + {isMobile ? ( + navigate("/download-app")} + icon="download" + className={styles.headerSmallButton} + /> + ) : null}
@@ -103,14 +146,30 @@ export default function HomePage() {
-
+
+
+
FromChat
100% бесплатный и открытый мессенджер. Поддерживает self-hosted установку на своём сервере.
- navigate("/auth?mode=login")} icon="devices">Открыть веб-версию - navigate("/download-app")} icon="download">Скачать приложение + {isMobile ? null : ( + navigate("/auth?mode=login")} + icon="devices" + > + Открыть веб-версию + + )} + navigate("/download-app")} + icon="download" + > + Скачать приложение +
@@ -169,10 +228,21 @@ export default function HomePage() {
- - Веб-версия - Без установки - navigate("/login")}> + + + Веб-версия + + + Без установки + + navigate("/login")} + > Открыть @@ -238,7 +308,7 @@ export default function HomePage() {
FromChat
-

FromChat © 2025

+

FromChat © 2026

@@ -250,9 +320,23 @@ export default function HomePage() { Веб-версия + + + ПК-клиент +
- + Исходный код приложения @@ -260,18 +344,37 @@ export default function HomePage() { Исходный код веб-версии - + Лицензия
diff --git a/frontend/src/pages/home/home.module.scss b/frontend/src/pages/home/home.module.scss index 67c641c..42be682 100644 --- a/frontend/src/pages/home/home.module.scss +++ b/frontend/src/pages/home/home.module.scss @@ -27,9 +27,9 @@ $radius-pill: 9999px; } @mixin section-heading { - font-size: 2.5rem; + font-size: 40px; font-weight: 700; - margin-bottom: 1.5rem; + margin-bottom: 24px; @include gradient-text; } @@ -39,285 +39,346 @@ $radius-pill: 9999px; margin: 0 auto; p { - font-size: 1.25rem; + font-size: 20px; line-height: 1.6; - margin-bottom: 2.5rem; + margin-bottom: 40px; opacity: 0.9; } } +@mixin container { + max-width: 960px; + margin: 0 auto; + padding: 0 32px; +} + .homepage { min-height: 100vh; color: $color-dark-on-background; font-family: 'Montserrat', sans-serif; position: relative; background-color: $color-dark-surface; + -webkit-tap-highlight-color: transparent; + -webkit-user-drag: none; - .container { - max-width: 960px; - margin: 0 auto; - padding: 0 2rem; + img { + -webkit-user-drag: none; + user-select: none; } - .colorSphere { - background: $gradient-conic; - position: absolute; - width: 100%; - aspect-ratio: 1 / 1; - top: -90vh; - opacity: 0.3; - left: 0; - border-radius: 50%; - filter: blur(80px); - } - - .homepageHeader { - margin: 0 16px; - padding: 16px 5px; - background: rgba($color-dark-surface, 0.8); - backdrop-filter: blur(10px); + header.homepageHeader { + display: flex; + justify-content: center; + padding: 16px; position: sticky; - top: 16px; + top: 0; z-index: 1000; - transition: all 0.3s ease; - border-radius: 30px; - .headerContent { - display: flex; - justify-content: space-between; - align-items: center; + .headerInner { + max-width: 960px; + width: 100%; + padding: 16px 24px; + background: rgba($color-dark-surface, 0.8); + backdrop-filter: blur(10px); + border-radius: 30px; - .logo { + .headerContent { display: flex; + justify-content: space-between; align-items: center; - gap: 10px; - .logoIcon { - width: 40px; - height: 40px; - background-image: url('../../images/logo_square.svg'); - background-size: cover; - background-position: center; - background-repeat: no-repeat; - border-radius: 12px; - } - - h1 { - font-size: 2rem; - font-weight: 700; - margin: 0; - @include gradient-text; - } - } - - .headerCenterLinks { - display: flex; - align-items: center; - gap: 10px; - - a { + .logo { display: flex; align-items: center; gap: 10px; - } - } - .headerButton { - display: flex; - align-items: center; - justify-content: center; - } - } - } - - .title { - padding: 60px 80px; - display: flex; - align-items: center; - margin-top: 0; - flex-direction: column; - - .titleLogo { - width: 120px; - height: 120px; - background-image: url('../../images/logo_square.svg'); - background-size: cover; - background-position: center; - background-repeat: no-repeat; - border-radius: 30px; - } - - .titleContent { - font-size: 30pt; - font-weight: 700; - margin-top: 10px; - @include gradient-text; - } - - .titleDesc { - font-size: 16pt; - opacity: 0.8; - margin-top: 10px; - text-align: center; - } - - .titleButtons { - display: flex; - gap: 10px; - margin-top: 20px; - } - } - - .features { - display: flex; - flex-direction: column; - gap: 50px; - overflow-x: clip; - - .featureContainer { - display: flex; - flex-direction: row; - gap: 20px; - padding: 0 16px; - align-items: center; - margin-left: auto; - margin-right: auto; - max-width: 700px; - - .featureText { - display: flex; - flex-direction: column; - gap: 10px; - flex-grow: 1; - flex-shrink: 1; - - .featureTitle { - font-size: 30pt; - font-weight: 700; - line-height: normal; - @include gradient-text; - } - - .featureDesc { - text-align: left; - font-size: 15pt; - } - } - - .featureScreenshotOuter { - position: relative; - - .featureScreenshotGlow { - $size: 400px; - - @keyframes rotateGradient { - from { transform: rotate(0deg); } - to { transform: rotate(360deg); } + .logoIcon { + width: 40px; + height: 40px; + background-image: url('../../images/logo_square.svg'); + background-size: cover; + background-position: center; + background-repeat: no-repeat; + border-radius: 12px; } + h1 { + font-size: 32px; + font-weight: 700; + margin: 0; + @include gradient-text; + } + } + + .headerCenterLinks { + display: flex; + align-items: center; + gap: 10px; + + @media (max-width: 730px) { + display: none; + } + + a { + display: flex; + align-items: center; + gap: 10px; + } + } + + .headerButton { + display: flex; + align-items: center; + justify-content: center; + } + + .headerDownloadButton { + display: block; + } + + .headerSmallButton { + display: none; + } + + @media (max-width: 450px) { + .headerSmallButton { + display: block; + } + + .headerDownloadButton { + display: none; + } + } + } + } + } + + main { + .title { + padding: 60px 0; + display: flex; + align-items: center; + margin-top: 0; + flex-direction: column; + + .titleLogoWrapper { + position: relative; + width: 100%; + overflow-x: clip; + display: flex; + justify-content: center; + + &::before { + $size: 1200px; + + content: ''; background: $gradient-conic; position: absolute; width: $size; height: $size; - top: calc(50% - ($size / 2)); - opacity: 0.6; + bottom: 0; + opacity: 0.3; left: calc(50% - ($size / 2)); border-radius: 50%; filter: blur(80px); - z-index: 0; - will-change: transform; - animation: rotateGradient 8s linear infinite; - } - - .featureScreenshot { - max-width: 300px; - z-index: 50; - position: relative; - } - } - } - } - - .download { - .downloadContent { - @include section-content; - - h3 { - @include section-heading; - } - - .downloadPlatforms { - display: flex; - gap: 1.5rem; - justify-content: center; - flex-wrap: wrap; - } - - .downloadCard { - display: flex; - flex-direction: column; - align-items: center; - gap: 0.75rem; - padding: 2rem 1.5rem; - min-width: 200px; - max-width: 260px; - background: rgba($color-dark-surface-container, 0.6); - border: 1px solid rgba($color-dark-outline, 0.3); - border-radius: $radius-card; - text-decoration: none; - color: inherit; - transition: all 0.3s ease; - - &:hover { - background: rgba($color-dark-surface-container-high, 0.8); - border-color: rgba($color-dark-primary, 0.5); - box-shadow: 0 0 24px rgba($color-dark-primary, 0.2); } } - .downloadCardIcon { - width: 48px; - height: 48px; - color: $color-dark-primary; + .titleLogo { + width: 120px; + height: 120px; + background-image: url('../../images/logo_square.svg'); + background-size: cover; + background-position: center; + background-repeat: no-repeat; + border-radius: 30px; + position: relative; + z-index: 1; } - .downloadCardTitle { - font-size: 1.25rem; - font-weight: 600; + .titleContent { + font-size: 30pt; + font-weight: 700; + margin-top: 10px; + padding: 0 80px; @include gradient-text; } - .downloadCardDesc { - font-size: 0.9rem; + .titleDesc { + font-size: 16pt; opacity: 0.8; + margin-top: 10px; + text-align: center; + padding: 0 80px; } - .downloadCardBtn { - margin-top: 0.5rem; - } - } - } - - .cta { - padding-bottom: 128px; - - .ctaContent { - @include section-content; - - h3 { - @include section-heading; - } - - .ctaActions { + .titleButtons { display: flex; - gap: 1rem; - justify-content: center; - flex-wrap: wrap; + gap: 10px; + margin-top: 20px; + padding: 0 80px; + } + } + + .features { + display: flex; + flex-direction: column; + gap: 50px; + overflow-x: clip; + + .featureContainer { + display: flex; + flex-direction: row; + gap: 20px; + padding: 0 16px; + align-items: center; + margin-left: auto; + margin-right: auto; + max-width: 700px; + + .featureText { + display: flex; + flex-direction: column; + gap: 10px; + flex-grow: 1; + flex-shrink: 1; + + .featureTitle { + font-size: 30pt; + font-weight: 700; + line-height: normal; + @include gradient-text; + } + + .featureDesc { + text-align: left; + font-size: 15pt; + } + } + + .featureScreenshotOuter { + position: relative; + + .featureScreenshotGlow { + $size: 400px; + + @keyframes rotateGradient { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } + } + + background: $gradient-conic; + position: absolute; + width: $size; + height: $size; + top: calc(50% - ($size / 2)); + opacity: 0.6; + left: calc(50% - ($size / 2)); + border-radius: 50%; + filter: blur(80px); + z-index: 0; + will-change: transform; + animation: rotateGradient 8s linear infinite; + } + + .featureScreenshot { + max-width: 300px; + z-index: 50; + position: relative; + } + } + } + } + + .download { + .container { + @include container; + } + + .downloadContent { + @include section-content; + + h3 { + @include section-heading; + } + + .downloadPlatforms { + display: flex; + gap: 24px; + justify-content: center; + flex-wrap: wrap; + } + + .downloadCard { + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; + padding: 32px 24px; + min-width: 200px; + max-width: 260px; + background: rgba($color-dark-surface-container, 0.6); + border: 1px solid rgba($color-dark-outline, 0.3); + border-radius: $radius-card; + text-decoration: none; + color: inherit; + transition: all 0.3s ease; + + &:hover { + background: rgba($color-dark-surface-container-high, 0.8); + border-color: rgba($color-dark-primary, 0.5); + box-shadow: 0 0 24px rgba($color-dark-primary, 0.2); + } + + .downloadCardIcon { + width: 48px; + height: 48px; + color: $color-dark-primary; + } + + .downloadCardTitle { + font-size: 20px; + font-weight: 600; + @include gradient-text; + } + + .downloadCardDesc { + font-size: 14px; + opacity: 0.8; + } + + .downloadCardBtn { + margin-top: 8px; + } + } + } + } + + .cta { + padding-bottom: 128px; + + .container { + @include container; + } + + .ctaContent { + @include section-content; + + h3 { + @include section-heading; + } + + .ctaActions { + display: flex; + gap: 16px; + justify-content: center; + flex-wrap: wrap; + } } } } - .homepageFooter { + footer.homepageFooter { padding: 0 16px; background: $color-dark-surface; display: flex; @@ -334,7 +395,7 @@ $radius-pill: 9999px; display: flex; flex-direction: column; align-items: flex-start; - gap: 0.75rem; + gap: 12px; .footerLogoRow { display: flex; @@ -353,13 +414,13 @@ $radius-pill: 9999px; } .footerBrandName { - font-size: 2rem; + font-size: 32px; font-weight: 700; @include gradient-text; } .footerCopyright { - font-size: 0.875rem; + font-size: 14px; color: $color-dark-on-surface-variant; opacity: 0.8; margin: 0; @@ -375,22 +436,28 @@ $radius-pill: 9999px; display: flex; flex-direction: column; align-items: flex-start; - gap: 0.75rem; + gap: 12px; .footerLink { display: flex; flex-direction: row; align-items: center; - gap: 0.5rem; + gap: 8px; color: $color-dark-on-surface-variant; text-decoration: none; - font-size: 0.95rem; - transition: color 0.2s ease; - + font-size: 15px; + transition: color 0.2s ease, transform 0.15s ease; + -webkit-user-drag: none; + user-select: none; + &:hover { color: $color-dark-on-surface; } + &:active { + transform: scale(0.92); + } + .footerLinkIcon { width: 24px; height: 24px; @@ -408,9 +475,65 @@ $radius-pill: 9999px; -webkit-mask-repeat: no-repeat; -webkit-mask-position: center; } + + .footerLinkIconSvgTelegram { + mask-image: url('../../images/telegram.svg'); + -webkit-mask-image: url('../../images/telegram.svg'); + } + + .footerLinkIconSvgMax { + mask-image: url('../../images/max.svg'); + -webkit-mask-image: url('../../images/max.svg'); + } } } } + + @media (max-width: 635px) { + flex-direction: column-reverse; + align-items: flex-start; + padding: 32px; + } + } + + @media (max-width: 635px) { + main { + .title { + .titleContent, + .titleDesc, + .titleButtons { + padding: 0 16px; + } + } + + .features { + padding: 0 16px; + + .featureContainer { + flex-direction: column; + align-items: center; + padding: 0 16px; + + .featureText { + order: 1; + text-align: center; + + .featureDesc { + text-align: center; + } + } + + .featureScreenshotOuter { + order: 2; + } + } + } + + .download .container, + .cta .container { + padding: 0 16px; + } + } } } @@ -431,4 +554,4 @@ $radius-pill: 9999px; 50% { opacity: 0.5; } -} \ No newline at end of file +}