Implement footer, redesign other sections

This commit is contained in:
2026-03-15 14:42:00 +03:00
Unverified
parent 0b81f7b947
commit 40968e9368
5 changed files with 302 additions and 171 deletions
+3
View File
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 42 42">
<path fill="#FFFFFF" fill-rule="evenodd" d="M21.47 41.88c-4.11 0-6.02-.6-9.34-3-2.1 2.7-8.75 4.81-9.04 1.2 0-2.71-.6-5-1.28-7.5C1 29.5.08 26.07.08 21.1.08 9.23 9.82.3 21.36.3c11.55 0 20.6 9.37 20.6 20.91a20.6 20.6 0 0 1-20.49 20.67Zm.17-31.32c-5.62-.29-10 3.6-10.97 9.7-.8 5.05.62 11.2 1.83 11.52.58.14 2.04-1.04 2.95-1.95a10.4 10.4 0 0 0 5.08 1.81 10.7 10.7 0 0 0 11.19-9.97 10.7 10.7 0 0 0-10.08-11.1Z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 497 B

+3
View File
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 200 200" width="200px" height="200px">
<path fill-rule="evenodd" clip-rule="evenodd" d="M 13.693 88.614 C 67.369 65.304 103.102 49.815 121.044 42.299 C 172.114 20.984 182.849 17.303 189.751 17.149 C 191.284 17.149 194.658 17.456 196.958 19.295 C 198.799 20.829 199.259 22.823 199.565 24.356 C 199.873 25.889 200.18 29.111 199.873 31.565 C 197.112 60.703 185.15 131.402 179.016 163.916 C 176.408 177.717 171.347 182.317 166.441 182.778 C 155.705 183.699 147.577 175.724 137.302 168.976 C 121.044 158.395 111.997 151.8 96.202 141.371 C 77.951 129.408 89.76 122.815 100.188 112.08 C 102.948 109.319 150.031 66.378 150.95 62.545 C 151.104 62.084 151.104 60.243 150.031 59.323 C 148.956 58.403 147.423 58.71 146.197 59.016 C 144.509 59.323 118.745 76.5 68.596 110.391 C 61.235 115.453 54.64 117.906 48.659 117.754 C 42.065 117.6 29.49 114.072 19.981 111.005 C 8.479 107.325 -0.723 105.331 0.045 98.89 C 0.503 95.516 5.105 92.142 13.693 88.614 Z" style="fill: rgb(255, 255, 255);"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -11,7 +11,7 @@ export default function DownloadAppPage() {
вам нужно скачать приложение мессенджера.
</p>
<a href="https://github.com/denis0001-dev/FromChat-android/releases/latest">
<a href="https://github.com/fromchat-messenger/app/releases/latest" target="_blank" rel="noopener noreferrer">
<MaterialButton>Скачать на GitHub</MaterialButton>
</a>
+105 -35
View File
@@ -1,21 +1,27 @@
import { useNavigate } from "react-router-dom";
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 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";
function GitHubLink({ children }: { children: React.ReactNode }) {
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 }) {
return (
<a href="https://github.com/denis0001-dev/FromChat" target="_blank">{children}</a>
<a href={`${GITHUB_WEB}/tree/main`} target="_blank" rel="noopener noreferrer" className={className}>{children}</a>
);
}
function SupportLink({ children }: { children: React.ReactNode }) {
function SupportLink({ children, className }: { children: React.ReactNode; className?: string }) {
return (
<a href="https://t.me/denis0001-dev" target="_blank">{children}</a>
<a href="https://t.me/denis0001-dev" target="_blank" rel="noopener noreferrer" className={className}>{children}</a>
);
}
@@ -99,7 +105,9 @@ export default function HomePage() {
<section className={styles.title}>
<div className={styles.titleLogo} />
<div className={styles.titleContent}>FromChat</div>
<div className={styles.titleDesc}>100% бесплатный и открытый мессенджер. Поддерживает self-hosted установку на своем сервере.</div>
<div className={styles.titleDesc}>
100% бесплатный и открытый мессенджер. Поддерживает self-hosted установку на своём сервере.
</div>
<div className={styles.titleButtons}>
<MaterialButton variant="filled" onClick={() => navigate("/auth?mode=login")} icon="devices">Открыть веб-версию</MaterialButton>
<MaterialButton variant="outlined" onClick={() => navigate("/download-app")} icon="download">Скачать приложение</MaterialButton>
@@ -112,12 +120,12 @@ export default function HomePage() {
screenshot={generalChatScreenshot}
right
>
Общайтесь со всеми пользователями этого сервера FromChat.
Открытый форум для всех пользователей сервера. Пишите сообщения, делитесь файлами и общайтесь в реальном времени.
</FeatureSection>
<FeatureSection
title={<>Личные сообщения</>}
screenshot={dmScreenshot}>
Общайтесь с одним человеком.
Общайтесь с одним человеком в личной переписке.
</FeatureSection>
</section>
@@ -126,31 +134,65 @@ export default function HomePage() {
<div className={styles.downloadContent}>
<h3>Скачайте приложение</h3>
<p>
Для лучшего опыта используйте настольное приложение с поддержкой
уведомлений и автономной работы.
Для лучшего опыта используйте настольное приложение с уведомлениями
и автономной работой или мобильное приложение для Android.
</p>
<div className={styles.downloadButtons}>
<div className={styles.downloadPlatforms}>
{!isMobile ? (
<>
<a
href="https://github.com/Toolbox-io/FromChat/actions/workflows/build.yml"
href={`${GITHUB_WEB}/actions/workflows/build.yml`}
target="_blank"
rel="noopener noreferrer"
className={styles.downloadCard}
>
<MaterialButton variant="filled">
<MaterialIcon name="computer" className={styles.downloadCardIcon} />
<span className={styles.downloadCardTitle}>Для ПК</span>
<span className={styles.downloadCardDesc}>Windows, macOS, Linux</span>
<MaterialButton variant="filled" className={styles.downloadCardBtn}>
<MaterialIcon name="download" slot="icon" />
Скачать для ПК
Скачать
</MaterialButton>
</a>
<MaterialButton variant="outlined" onClick={() => navigate("/login")}>
<MaterialIcon name="language" slot="icon" />
Веб-версия
</MaterialButton>
<a
href={`${GITHUB_APP}/releases/latest`}
target="_blank"
rel="noopener noreferrer"
className={styles.downloadCard}
>
<MaterialIcon name="android" className={styles.downloadCardIcon} />
<span className={styles.downloadCardTitle}>Android</span>
<span className={styles.downloadCardDesc}>APK на GitHub</span>
<MaterialButton variant="outlined" className={styles.downloadCardBtn}>
<MaterialIcon name="download" slot="icon" />
Скачать
</MaterialButton>
</a>
<div className={styles.downloadCard}>
<MaterialIcon name="language" className={styles.downloadCardIcon} />
<span className={styles.downloadCardTitle}>Веб-версия</span>
<span className={styles.downloadCardDesc}>Без установки</span>
<MaterialButton variant="outlined" className={styles.downloadCardBtn} onClick={() => navigate("/login")}>
<MaterialIcon name="open_in_new" slot="icon" />
Открыть
</MaterialButton>
</div>
</>
) : (
<MaterialButton variant="filled" onClick={() => navigate("/download-app")}>
Скачать приложение
</MaterialButton>
<a
href={`${GITHUB_APP}/releases/latest`}
target="_blank"
rel="noopener noreferrer"
className={styles.downloadCard}
>
<MaterialIcon name="android" className={styles.downloadCardIcon} />
<span className={styles.downloadCardTitle}>Android</span>
<span className={styles.downloadCardDesc}>Скачайте APK на GitHub</span>
<MaterialButton variant="filled" className={styles.downloadCardBtn}>
<MaterialIcon name="download" slot="icon" />
Скачать приложение
</MaterialButton>
</a>
)}
</div>
</div>
@@ -162,7 +204,8 @@ export default function HomePage() {
<div className={styles.ctaContent}>
<h3>Готовы начать общение?</h3>
<p>
Присоединяйтесь к FromChat и общайтесь безопасно с друзьями и коллегами.
Создайте аккаунт за минуту. Общайтесь в общем чате, ведите личную переписку
или звоните всё бесплатно и с открытым кодом.
</p>
<div className={styles.ctaActions}>
{isMobile ? (
@@ -190,20 +233,47 @@ export default function HomePage() {
</main>
<footer className={styles.homepageFooter}>
<div className={styles.container}>
<div className={styles.footerContent}>
<div className={styles.footerSection}>
<h4>Ссылки</h4>
<GitHubLink>GitHub</GitHubLink>
<SupportLink>Поддержка</SupportLink>
</div>
<div className={styles.footerSection}>
<h4>Лицензия</h4>
<p>GPL-3.0</p>
</div>
<div className={styles.footerBrand}>
<div className={styles.footerLogoRow}>
<div className={styles.footerLogo} />
<span className={styles.footerBrandName}>FromChat</span>
</div>
<div className={styles.footerBottom}>
<p>&copy; 2025 FromChat. Сделано программистом denis0001-dev с для свободы общения.</p>
<p className={styles.footerCopyright}>FromChat © 2025</p>
</div>
<div className={styles.footerLinks}>
<div className={styles.footerSection}>
<Link to="/download-app" className={styles.footerLink}>
<MaterialIcon name="download" className={styles.footerLinkIcon} />
Скачать приложение
</Link>
<Link to="/login" className={styles.footerLink}>
<MaterialIcon name="language" className={styles.footerLinkIcon} />
Веб-версия
</Link>
</div>
<div className={styles.footerSection}>
<a href={`${GITHUB_APP}/tree/main`} target="_blank" rel="noopener noreferrer" className={styles.footerLink}>
<MaterialIcon name="android" className={styles.footerLinkIcon} />
Исходный код приложения
</a>
<GitHubLink className={styles.footerLink}>
<MaterialIcon name="code" className={styles.footerLinkIcon} />
Исходный код веб-версии
</GitHubLink>
<a href={GITHUB_LICENSE} target="_blank" rel="noopener noreferrer" className={styles.footerLink}>
<MaterialIcon name="description" className={styles.footerLinkIcon} />
Лицензия
</a>
</div>
<div className={styles.footerSection}>
<a href="https://t.me/fromchat_ch" target="_blank" rel="noopener noreferrer" className={styles.footerLink}>
<span className={`${styles.footerLinkIcon} ${styles.footerLinkIconSvg}`} style={{ maskImage: `url(${telegramIcon})`, WebkitMaskImage: `url(${telegramIcon})` }} />
Telegram
</a>
<a href="https://max.ru/join/c5t6LfnCCPetQSAOshmouEvq9vsjHZT_Lt63kw8YCg0" target="_blank" rel="noopener noreferrer" className={styles.footerLink}>
<span className={`${styles.footerLinkIcon} ${styles.footerLinkIconSvg}`} style={{ maskImage: `url(${maxIcon})`, WebkitMaskImage: `url(${maxIcon})` }} />
MAX
</a>
</div>
</div>
</footer>
+189 -134
View File
@@ -1,5 +1,51 @@
@use "../../css/material" as *;
// Shared variables
$gradient-rainbow: linear-gradient(45deg, #9333EA, #6366F1, #3B82F6, #A855F7, #D946EF, #EC4899, #7E22CE);
$gradient-conic: conic-gradient(
from 0deg,
rgba(147, 51, 234, 0.5) 0%,
rgba(99, 102, 241, 0.6) 12.5%,
rgba(59, 130, 246, 0.55) 25%,
rgba(168, 85, 247, 0.5) 37.5%,
rgba(217, 70, 239, 0.6) 50%,
rgba(236, 72, 153, 0.55) 62.5%,
rgba(192, 132, 252, 0.5) 75%,
rgba(126, 34, 206, 0.6) 87.5%,
rgba(147, 51, 234, 0.5) 100%
);
$glow-purple: rgba(147, 51, 234, 0.5);
$radius-card: 20px;
$radius-pill: 9999px;
// Mixins
@mixin gradient-text {
background: $gradient-rainbow;
background-clip: text;
-webkit-text-fill-color: transparent;
text-shadow: 0 0 20px $glow-purple;
}
@mixin section-heading {
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 1.5rem;
@include gradient-text;
}
@mixin section-content {
text-align: center;
max-width: 600px;
margin: 0 auto;
p {
font-size: 1.25rem;
line-height: 1.6;
margin-bottom: 2.5rem;
opacity: 0.9;
}
}
.homepage {
min-height: 100vh;
color: $color-dark-on-background;
@@ -7,26 +53,14 @@
position: relative;
background-color: $color-dark-surface;
// Container styles
.container {
max-width: 1200px;
max-width: 960px;
margin: 0 auto;
padding: 0 2rem;
}
.colorSphere {
background: conic-gradient(
from 0deg,
rgba(147, 51, 234, 0.5) 0%,
rgba(99, 102, 241, 0.6) 12.5%,
rgba(59, 130, 246, 0.55) 25%,
rgba(168, 85, 247, 0.5) 37.5%,
rgba(217, 70, 239, 0.6) 50%,
rgba(236, 72, 153, 0.55) 62.5%,
rgba(192, 132, 252, 0.5) 75%,
rgba(126, 34, 206, 0.6) 87.5%,
rgba(147, 51, 234, 0.5) 100%
);
background: $gradient-conic;
position: absolute;
width: 100%;
aspect-ratio: 1 / 1;
@@ -37,7 +71,6 @@
filter: blur(80px);
}
// Header styles
.homepageHeader {
margin: 0 16px;
padding: 16px 5px;
@@ -73,11 +106,7 @@
font-size: 2rem;
font-weight: 700;
margin: 0;
background: linear-gradient(45deg, #9333EA, #6366F1, #3B82F6, #A855F7, #D946EF, #EC4899, #7E22CE);
background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-shadow: 0 0 20px rgba(147, 51, 234, 0.5);
@include gradient-text;
}
}
@@ -101,7 +130,6 @@
}
}
// Hero section
.title {
padding: 60px 80px;
display: flex;
@@ -122,12 +150,8 @@
.titleContent {
font-size: 30pt;
font-weight: 700;
background: linear-gradient(45deg, #9333EA, #6366F1, #3B82F6, #A855F7, #D946EF, #EC4899, #7E22CE);
background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-shadow: 0 0 20px rgba(147, 51, 234, 0.5);
margin-top: 10px;
@include gradient-text;
}
.titleDesc {
@@ -144,11 +168,11 @@
}
}
// Features section
.features {
display: flex;
flex-direction: column;
gap: 50px;
overflow-x: clip;
.featureContainer {
display: flex;
@@ -170,12 +194,8 @@
.featureTitle {
font-size: 30pt;
font-weight: 700;
background: linear-gradient(45deg, #9333EA, #6366F1, #3B82F6, #A855F7, #D946EF, #EC4899, #7E22CE);
background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-shadow: 0 0 20px rgba(147, 51, 234, 0.5);
line-height: normal;
@include gradient-text;
}
.featureDesc {
@@ -191,26 +211,11 @@
$size: 400px;
@keyframes rotateGradient {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
background: conic-gradient(
from 0deg,
rgba(147, 51, 234, 0.5) 0%,
rgba(99, 102, 241, 0.6) 12.5%,
rgba(59, 130, 246, 0.55) 25%,
rgba(168, 85, 247, 0.5) 37.5%,
rgba(217, 70, 239, 0.6) 50%,
rgba(236, 72, 153, 0.55) 62.5%,
rgba(192, 132, 252, 0.5) 75%,
rgba(126, 34, 206, 0.6) 87.5%,
rgba(147, 51, 234, 0.5) 100%
);
background: $gradient-conic;
position: absolute;
width: $size;
height: $size;
@@ -233,65 +238,74 @@
}
}
// Download section
.download {
padding: 6rem 0;
.downloadContent {
text-align: center;
max-width: 600px;
margin: 0 auto;
@include section-content;
h3 {
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 1.5rem;
background: linear-gradient(45deg, $color-dark-primary, $color-dark-tertiary);
background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-shadow: 0 0 20px rgba($color-dark-primary, 0.5);
@include section-heading;
}
p {
font-size: 1.25rem;
line-height: 1.6;
margin-bottom: 2.5rem;
opacity: 0.9;
}
.downloadButtons {
.downloadPlatforms {
display: flex;
gap: 1rem;
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;
}
.downloadCardTitle {
font-size: 1.25rem;
font-weight: 600;
@include gradient-text;
}
.downloadCardDesc {
font-size: 0.9rem;
opacity: 0.8;
}
.downloadCardBtn {
margin-top: 0.5rem;
}
}
}
// CTA section
.cta {
padding-bottom: 128px;
.ctaContent {
text-align: center;
max-width: 600px;
margin: 0 auto;
@include section-content;
h3 {
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 1.5rem;
background: linear-gradient(45deg, $color-dark-primary, $color-dark-tertiary);
background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-shadow: 0 0 20px rgba($color-dark-primary, 0.5);
}
p {
font-size: 1.25rem;
line-height: 1.6;
margin-bottom: 2.5rem;
opacity: 0.9;
@include section-heading;
}
.ctaActions {
@@ -303,57 +317,98 @@
}
}
// Footer
.homepageFooter {
.footerContent {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 2rem;
margin-bottom: 2rem;
padding: 0 16px;
background: $color-dark-surface;
display: flex;
flex-direction: row;
gap: 32px;
max-width: 1000px;
margin-left: auto;
margin-right: auto;
align-items: center;
justify-content: center;
padding-bottom: 48px;
.footerSection {
h4 {
font-size: 1.1rem;
font-weight: 600;
margin-bottom: 1rem;
color: $color-dark-on-surface;
text-shadow: 0 0 10px rgba($color-dark-primary, 0.3);
}
.footerBrand {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 0.75rem;
p {
opacity: 0.8;
line-height: 1.6;
color: $color-dark-on-surface-variant;
}
.footerLogoRow {
display: flex;
align-items: center;
gap: 10px;
}
a {
color: $color-dark-on-surface;
text-decoration: none;
opacity: 0.8;
display: block;
margin-bottom: 0.5rem;
transition: all 0.3s ease;
padding: 0.25rem 0;
border-radius: 4px;
.footerLogo {
width: 40px;
height: 40px;
background-image: url('../../images/logo_square.svg');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
border-radius: 12px;
}
&:hover {
opacity: 1;
color: $color-dark-primary;
text-shadow: 0 0 10px rgba($color-dark-primary, 0.5);
background: rgba($color-dark-primary, 0.1);
}
}
.footerBrandName {
font-size: 2rem;
font-weight: 700;
@include gradient-text;
}
.footerCopyright {
font-size: 0.875rem;
color: $color-dark-on-surface-variant;
opacity: 0.8;
margin: 0;
}
}
.footerBottom {
text-align: center;
padding-top: 2rem;
.footerLinks {
display: flex;
gap: 32px;
flex-wrap: wrap;
p {
opacity: 0.7;
margin: 0;
color: $color-dark-on-surface-variant;
.footerSection {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 0.75rem;
.footerLink {
display: flex;
flex-direction: row;
align-items: center;
gap: 0.5rem;
color: $color-dark-on-surface-variant;
text-decoration: none;
font-size: 0.95rem;
transition: color 0.2s ease;
&:hover {
color: $color-dark-on-surface;
}
.footerLinkIcon {
width: 24px;
height: 24px;
min-width: 24px;
opacity: 0.9;
}
.footerLinkIconSvg {
display: inline-block;
background-color: currentColor;
mask-size: contain;
mask-repeat: no-repeat;
mask-position: center;
-webkit-mask-size: contain;
-webkit-mask-repeat: no-repeat;
-webkit-mask-position: center;
}
}
}
}
}