mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-04-01 07:26:03 +00:00
* website: improve first page load * remove low res images * simplify * fix buttons jitter * fix color scheme toggling * fix other pages --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
384 lines
21 KiB
HTML
384 lines
21 KiB
HTML
---
|
|
title: "SimpleX Chat: private and secure messenger without any user IDs (not even random)"
|
|
description: "SimpleX Chat - a private and encrypted messenger without any user IDs (not even random ones)! Make a private connection via link / QR code to send messages and make calls."
|
|
templateEngineOverride: njk
|
|
active_home: true
|
|
---
|
|
|
|
<!DOCTYPE html>
|
|
<html class="bg-[#C1E3FE] dark:bg-[#050920]" lang="{{ page.url | getlang }}"
|
|
{% for language in languages.languages %}
|
|
{% if language.label == page.url | getlang %}
|
|
dir="{{ "rtl" if language.rtl else "ltr" }}"
|
|
{% endif %}
|
|
{% endfor %}>
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
{% include "dark-mode.html" %}
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
|
<title>{{ title }}</title>
|
|
<meta name="description" content="{{ description }}"/>
|
|
<meta name="Content-Type" content="text/html;charset=utf-8"/>
|
|
{% if path %}
|
|
<meta http-equiv="onion-location" content="{% cfg 'onionLocation' %}{{ path }}" />
|
|
<meta property="og:url" content="{% cfg 'siteLocation' %}{{ path }}" />
|
|
{% else %}
|
|
<meta http-equiv="onion-location" content="{% cfg 'onionLocation' %}/" />
|
|
<meta property="og:url" content="{% cfg 'siteLocation' %}/" />
|
|
{% endif %}
|
|
<meta property="og:type" content="website"/>
|
|
<meta property="og:title" content="{{ title }}"/>
|
|
<meta property="og:description" content="{{ description }}"/>
|
|
<meta property="og:image" content="{% cfg 'siteLocation' %}/img/share_simplex.jpg"/>
|
|
<meta name="twitter:card" content="summary"/>
|
|
<link rel="icon" type="image/png" sizes="96x96" href="/img/favicon.ico"/>
|
|
<meta name="theme-color" content="#C1E3FE">
|
|
|
|
<link rel="preload" href="/img/design_3/cover.webp" as="image" type="image/webp">
|
|
<link rel="preload" href="/img/design_3/cover-light.webp" as="image" type="image/webp" media="(prefers-color-scheme: light)">
|
|
<link rel="preload" href="/img/new/logo-dark.png" as="image">
|
|
<link rel="preload" href="/img/new/logo-light.png" as="image" media="(prefers-color-scheme: light)">
|
|
<link rel="preload" href="/img/design_3/publications/trail-of-bits.png" as="image">
|
|
<link rel="preload" href="/img/design_3/publications/privacy-guides.png" as="image">
|
|
<link rel="preload" href="/img/design_3/publications/whonix.png" as="image">
|
|
<link rel="preload" href="/img/design_3/publications/heise.png" as="image">
|
|
<link rel="preload" href="/img/design_3/publications/kuketz-blog.png" as="image">
|
|
<link rel="preload" href="/img/design_3/publications/optout.png" as="image">
|
|
|
|
<link rel="preload" href="/fonts/GT-Walsheim/GT-Walsheim-LC-Bold.woff2" as="font" type="font/woff2" crossorigin>
|
|
<link rel="preload" href="/fonts/GT-Walsheim/GT-Walsheim-LC-Regular.woff2" as="font" type="font/woff2" crossorigin>
|
|
<link rel="preload" href="/fonts/Manrope/Manrope-ExtraLight.ttf" as="font" type="font/ttf" crossorigin>
|
|
<link rel="preload" href="/fonts/Manrope/Manrope-Light.ttf" as="font" type="font/ttf" crossorigin>
|
|
<link rel="preload" href="/fonts/Manrope/Manrope-Medium.ttf" as="font" type="font/ttf" crossorigin>
|
|
<link rel="preload" href="/fonts/Manrope/Manrope-Bold.ttf" as="font" type="font/ttf" crossorigin>
|
|
|
|
<link rel="preload" href="/img/new/apple_store.svg" as="image">
|
|
<link rel="preload" href="/img/new/google_play.svg" as="image">
|
|
<link rel="preload" href="/img/new/f_droid.svg" as="image" media="(max-width: 959px)">
|
|
<link rel="preload" href="/img/design_3/testflight-dark.png" as="image" media="(max-width: 959px)">
|
|
<link rel="preload" href="/img/design_3/android-dark.png" as="image" media="(max-width: 959px)">
|
|
<link rel="preload" href="/img/design_3/socials/linux.png" as="image" media="(min-width: 960px)">
|
|
<link rel="preload" href="/img/design_3/socials/apple.png" as="image" media="(min-width: 960px)">
|
|
<link rel="preload" href="/img/design_3/socials/windows.png" as="image" media="(min-width: 960px)">
|
|
|
|
<link href="/css/tailwind.css" rel="stylesheet"/>
|
|
<link rel="stylesheet" href="/css/design3.css">
|
|
<link rel="stylesheet" href="/css/design3-nav.css">
|
|
<script src="/js/flag-anchor.js"></script>
|
|
<script>
|
|
function detectMobileBrowser() {
|
|
const ua = navigator.userAgent;
|
|
|
|
// Chrome on iOS
|
|
if (/CriOS/.test(ua)) {
|
|
document.documentElement.classList.add('chrome-ios');
|
|
}
|
|
// Safari on iOS
|
|
else if (/Safari/.test(ua) && /iPhone|iPad|iPod/.test(ua) && !/CriOS|FxiOS|EdgiOS/.test(ua)) {
|
|
document.documentElement.classList.add('safari-ios');
|
|
}
|
|
// Chrome on Android
|
|
else if (/Chrome/.test(ua) && /Android/.test(ua)) {
|
|
document.documentElement.classList.add('chrome-android');
|
|
}
|
|
}
|
|
|
|
detectMobileBrowser();
|
|
</script>
|
|
</head>
|
|
|
|
<body class="bg-[#C1E3FE] dark:bg-[#050920]">
|
|
{% include "navbar.html" %}
|
|
{% set lang = page.url | getlang %}
|
|
|
|
<div class="screen">
|
|
<section class="page-1 cover page">
|
|
<div class="area">
|
|
<div class="content">
|
|
<h1>{{ "index-hero-h1" | i18n({}, lang) | safe }}</h1>
|
|
<h2 class="gradient-text">{{ "index-hero-h2" | i18n({}, lang) | safe }}</h2>
|
|
<p>{{ "index-hero-p1" | i18n({}, lang) | safe }}</p>
|
|
|
|
<div class="socials">
|
|
<a href="/downloads" class="desktop-app-btn hidden" title="{{ 'index-hero-download-desktop-btn-title' | i18n({}, lang) }}">
|
|
<div class="btn-content">
|
|
<div>
|
|
<img class="no-border" src="/img/design_3/socials/linux.png">
|
|
<img class="no-border" src="/img/design_3/socials/apple.png" alt="">
|
|
<img class="no-border" src="/img/design_3/socials/windows.png" alt="">
|
|
</div>
|
|
<p>Download<br>Desktop App</p>
|
|
</div>
|
|
</a>
|
|
<a class="apple-store-btn hidden" href="https://apps.apple.com/us/app/simplex-chat/id1605771084" target="_blank"><img src="/img/new/apple_store.svg"></a>
|
|
<a class="google-play-btn hidden" href="https://play.google.com/store/apps/details?id=chat.simplex.app" target="_blank"><img src="/img/new/google_play.svg"></a>
|
|
<a class="f-droid-btn hidden" href="{{ '' if lang == 'en' else '/' ~ lang }}/fdroid" title="{{ 'index-f-droid-title' | i18n({}, lang) }}"><img src="/img/new/f_droid.svg"></a>
|
|
<a class="testflight-btn hidden" href="https://testflight.apple.com/join/DWuT2LQu" target="_blank"><img class="no-border" src="/img/design_3/testflight-dark.png" title="{{ 'index-testflight-title' | i18n({}, lang) }}"></a>
|
|
<a class="android-btn hidden" href="https://github.com/simplex-chat/simplex-chat/releases/latest/download/simplex-aarch64.apk" target="_blank"><img src="/img/design_3/android-dark.png"></a>
|
|
</div>
|
|
<div class="security-btns">
|
|
<a title="{{ 'index-security-assessment-title' | i18n({}, lang) }}" href="https://www.trailofbits.com/about" target="_blank">
|
|
<img src="/img/design_3/publications/trail-of-bits.png" alt="Trail of Bits">
|
|
</a>
|
|
<a class="box-btn btn-1" title="{{ 'index-security-review-2022-title' | i18n({}, lang) }}" href="/blog/20221108-simplex-chat-v4.2-security-audit-new-website.html">
|
|
2022
|
|
</a>
|
|
<a class="box-btn gold-gradient" title="{{ 'index-security-review-2024-title' | i18n({}, lang) }}" href="/blog/20241014-simplex-network-v6-1-security-review-better-calls-user-experience.html">
|
|
2024
|
|
</a>
|
|
<p class="security-audits">
|
|
{{ 'index-security-audits-label' | i18n({}, lang) | safe }}
|
|
</p>
|
|
</div>
|
|
<div class="publications-btns">
|
|
<a title="{{ 'index-publications-privacy-guides-title' | i18n({}, lang) }}" href="https://www.privacyguides.org/en/real-time-communication/#simplex-chat" target="_blank">
|
|
<img src="/img/design_3/publications/privacy-guides.png" alt="Privacy Guides">
|
|
</a>
|
|
<a title="{{ 'index-publications-whonix-title' | i18n({}, lang) }}" href="https://www.whonix.org/wiki/Chat#Recommendation" target="_blank">
|
|
<img src="/img/design_3/publications/whonix.png" alt="Whonix" class="whonix">
|
|
</a>
|
|
<a title="{{ 'index-publications-heise-title' | i18n({}, lang) }}" href="https://www.heise.de/suche/?q=simplex+chat&sort_by=date&rm=search" target="_blank">
|
|
<img src="/img/design_3/publications/heise.png" alt="Heise">
|
|
</a>
|
|
<a title="{{ 'index-publications-kuketz-title' | i18n({}, lang) }}" href="https://www.kuketz-blog.de/simplex-eindruecke-vom-messenger-ohne-identifier/" target="_blank">
|
|
<img src="/img/design_3/publications/kuketz-blog.png" alt="Kuketz Blog" class="kuketz">
|
|
</a>
|
|
<a title="{{ 'index-publications-optout-title' | i18n({}, lang) }}" href="https://optoutpod.com/episodes/s3e02-simplexchat/" target="_blank">
|
|
<img src="/img/design_3/publications/optout.png" alt="Opt Out">
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<main>
|
|
<div class="section-bg"></div>
|
|
|
|
<section id="messaging" class="page-2 page">
|
|
<div class="area">
|
|
<div class="text-container">
|
|
<h2>{{ "worlds-most-secure-messaging" | i18n({}, lang ) | safe }}</h2>
|
|
<p>{{ "index-messaging-p1" | i18n({}, lang) }}</p>
|
|
<p>{{ "index-messaging-p2" | i18n({}, lang) | safe }}</p>
|
|
<a class="gradient-text" href="{{ '' if lang == 'en' else '/' ~ lang }}/messaging">{{ "index-messaging-cta" | i18n({}, lang) }}</a>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="nextweb" class="page-3 page">
|
|
<div class="area">
|
|
<div class="text-container">
|
|
<h2>{{ "index-nextweb-h2" | i18n({}, lang) | safe }}</h2>
|
|
<p>{{ "index-nextweb-p1" | i18n({}, lang) | safe }}</p>
|
|
<p>{{ "index-nextweb-p2" | i18n({}, lang) }}</p>
|
|
<a class="gradient-text" href="{{ '' if lang == 'en' else '/' ~ lang }}/why">{{ "why-footer-link" | i18n({}, lang) | safe }}</a>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="token" class="page-4 page">
|
|
<div class="area">
|
|
<div class="text-container">
|
|
<h2>{{ "index-token-h2" | i18n({}, lang) }}</h2>
|
|
<p>{{ "index-token-p1" | i18n({}, lang) }}</p>
|
|
<p>{{ "index-token-p2" | i18n({}, lang) }}</p>
|
|
<a class="gradient-text" href="/vouchers">{{ "index-token-cta" | i18n({}, lang) | safe }}</a>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="roadmap" class="page-5 page">
|
|
<div class="area">
|
|
<div class="text-container">
|
|
<h2>{{ "index-roadmap-h2" | i18n({}, lang) }}</h2>
|
|
<div class="roadmap">
|
|
<p>{{ "index-roadmap-2025" | i18n({}, lang) }} </p>
|
|
<p class="title"><span class="ltr:order-first rtl:order-last">: </span>{{ "index-roadmap-2025-title" | i18n({}, lang) }}</p>
|
|
<p>{{ "index-roadmap-2025-desc" | i18n({}, lang) }}</p>
|
|
</div>
|
|
<div class="roadmap">
|
|
<p>{{ "index-roadmap-2026" | i18n({}, lang) }} </p>
|
|
<p class="title"><span class="ltr:order-first rtl:order-last">: </span>{{ "index-roadmap-2026-title" | i18n({}, lang) }}</p>
|
|
<p>{{ "index-roadmap-2026-desc" | i18n({}, lang) }}</p>
|
|
</div>
|
|
<div class="roadmap">
|
|
<p>{{ "index-roadmap-2027" | i18n({}, lang) }} </p>
|
|
<p class="title"><span class="ltr:order-first rtl:order-last">: </span>{{ "index-roadmap-2027-title" | i18n({}, lang) }}</p>
|
|
<p>{{ "index-roadmap-2027-desc" | i18n({}, lang) }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
<section id="directory" class="page-6 page">
|
|
<div class="area">
|
|
<div class="group-images">
|
|
<a href="/directory" class="group-image" target="_blank"><img src="/img/group.svg"></a>
|
|
<a href="/directory" class="group-image" target="_blank"><img src="/img/group.svg"></a>
|
|
<a href="/directory" class="group-image" target="_blank"><img src="/img/group.svg"></a>
|
|
<a href="/directory" class="group-image" target="_blank"><img src="/img/group.svg"></a>
|
|
<a href="/directory" class="group-image" target="_blank"><img src="/img/group.svg"></a>
|
|
<a href="/directory" class="group-image" target="_blank"><img src="/img/group.svg"></a>
|
|
<a href="/directory" class="group-image" target="_blank"><img src="/img/group.svg"></a>
|
|
<a href="/directory" class="group-image" target="_blank"><img src="/img/group.svg"></a>
|
|
<a href="/directory" class="group-image" target="_blank"><img src="/img/group.svg"></a>
|
|
<a href="/directory" class="group-image" target="_blank"><img src="/img/group.svg"></a>
|
|
<a href="/directory" class="group-image" target="_blank"><img src="/img/group.svg"></a>
|
|
<a href="/directory" class="group-image" target="_blank"><img src="/img/group.svg"></a>
|
|
<a href="https://smp4.simplex.im/g#hr4lvFeBmndWMKTwqiodPz3VBo_6UmdGWocXd1SupsM" class="simplex-image" title="{{ 'index-directory-users-group-title' | i18n({}, lang) }}" target="_blank">
|
|
<img class="block dark:hidden" src="/img/new/logo-symbol-light.svg">
|
|
<img class="hidden dark:block" src="/img/new/logo-symbol-dark.svg">
|
|
</a>
|
|
<a href="/directory" class="group-image" target="_blank"><img src="/img/group.svg"></a>
|
|
<a href="/directory" class="group-image" target="_blank"><img src="/img/group.svg"></a>
|
|
<a href="/directory" class="group-image" target="_blank"><img src="/img/group.svg"></a>
|
|
<a href="/directory" class="group-image" target="_blank"><img src="/img/group.svg"></a>
|
|
<a href="/directory" class="group-image" target="_blank"><img src="/img/group.svg"></a>
|
|
<a href="/directory" class="group-image" target="_blank"><img src="/img/group.svg"></a>
|
|
<a href="/directory" class="group-image" target="_blank"><img src="/img/group.svg"></a>
|
|
<a href="/directory" class="group-image" target="_blank"><img src="/img/group.svg"></a>
|
|
<a href="/directory" class="group-image" target="_blank"><img src="/img/group.svg"></a>
|
|
<a href="/directory" class="group-image" target="_blank"><img src="/img/group.svg"></a>
|
|
<a href="/directory" class="group-image" target="_blank"><img src="/img/group.svg"></a>
|
|
<a href="/directory" class="group-image" target="_blank"><img src="/img/group.svg"></a>
|
|
<a href="/directory" class="group-image" target="_blank"><img src="/img/group.svg"></a>
|
|
<a href="/directory" class="group-image" target="_blank"><img src="/img/group.svg"></a>
|
|
</div>
|
|
<div class="text-container">
|
|
<h2>{{ "index-directory-h2" | i18n({}, lang) }}</h2>
|
|
<p>{{ "index-directory-p1" | i18n({}, lang) }}</p>
|
|
<p>{{ "index-directory-p2" | i18n({}, lang) }}</p>
|
|
<a class="gradient-text" href="/directory">{{ "index-directory-cta" | i18n({}, lang) }}</a>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
{% include "footer.html" %}
|
|
</div>
|
|
|
|
|
|
<script src="/js/directory.js"></script>
|
|
<script src="/js/design3.js"></script>
|
|
<script>
|
|
(function navColorOnScroll(){
|
|
const header = document.querySelector('header#navbar');
|
|
const cover = document.querySelector('.page-1.cover');
|
|
const footer = document.querySelector('.footer.page');
|
|
if (!header || !cover || !footer || !('IntersectionObserver' in window)) {
|
|
window.addEventListener('load', () => header && document.body.classList.toggle('change-nav-color', window.scrollY > 10));
|
|
window.addEventListener('scroll', () => header && document.body.classList.toggle('change-nav-color', window.scrollY > 10));
|
|
return;
|
|
}
|
|
const io = new IntersectionObserver((entries) => {
|
|
// if cover or footer is mostly visible, keep white nav; else switch to #001796
|
|
const onObserved = entries.some((e) => e.isIntersecting && e.intersectionRatio >= 0.02);
|
|
document.body.classList.toggle('change-nav-color', !onObserved);
|
|
}, { threshold: [0, 0.02, 1] });
|
|
io.observe(cover);
|
|
io.observe(footer);
|
|
})();
|
|
</script>
|
|
<script>
|
|
(function sectionHeaderState(){
|
|
const mobileHeader = document.getElementById('mobile-header');
|
|
if (!mobileHeader) return;
|
|
|
|
const cover = document.querySelector('.page-1.cover');
|
|
const messaging = document.getElementById('messaging');
|
|
const nextweb = document.getElementById('nextweb');
|
|
const token = document.getElementById('token');
|
|
const roadmap = document.getElementById('roadmap');
|
|
const directory = document.getElementById('directory');
|
|
const footer = document.querySelector('footer, .footer, #footer');
|
|
|
|
const observed = [
|
|
{ key: 'cover', el: cover },
|
|
{ key: 'main', el: messaging },
|
|
{ key: 'main', el: nextweb },
|
|
{ key: 'main', el: token },
|
|
{ key: 'main', el: roadmap },
|
|
{ key: 'main', el: directory },
|
|
{ key: 'footer', el: footer }
|
|
].filter(t => t.el);
|
|
|
|
if (!observed.length) return;
|
|
|
|
const MIN_COVER = 0.02;
|
|
|
|
function clear(){
|
|
mobileHeader.classList.remove('cover','main','footer');
|
|
}
|
|
|
|
function apply(key){
|
|
clear();
|
|
if (key) mobileHeader.classList.add(key);
|
|
}
|
|
|
|
function viewportCoverageY(el){
|
|
const r = el.getBoundingClientRect();
|
|
const vh = window.innerHeight || document.documentElement.clientHeight;
|
|
const visibleY = Math.max(0, Math.min(vh, r.bottom) - Math.max(0, r.top));
|
|
return visibleY / Math.max(1, vh);
|
|
}
|
|
|
|
function computeKeyMax(){
|
|
const keyMax = { cover: 0, main: 0, footer: 0 };
|
|
for (const t of observed) {
|
|
const ratio = viewportCoverageY(t.el);
|
|
if (ratio > keyMax[t.key]) keyMax[t.key] = ratio;
|
|
}
|
|
return keyMax;
|
|
}
|
|
|
|
let lastKey = null;
|
|
let rafId = null;
|
|
|
|
function update(){
|
|
rafId = null; // Reset RAF flag
|
|
|
|
const keyMax = computeKeyMax();
|
|
|
|
let bestKey = null, best = MIN_COVER;
|
|
for (const [k, r] of Object.entries(keyMax)) {
|
|
if (r > best) { best = r; bestKey = k; }
|
|
}
|
|
|
|
// Only update DOM if state actually changed
|
|
if (bestKey !== lastKey) {
|
|
if (bestKey) {
|
|
apply(bestKey);
|
|
} else {
|
|
clear();
|
|
}
|
|
lastKey = bestKey;
|
|
}
|
|
}
|
|
|
|
function scheduleUpdate(){
|
|
if (rafId) return; // Already scheduled
|
|
rafId = requestAnimationFrame(update);
|
|
}
|
|
|
|
let observer = null;
|
|
|
|
if ('IntersectionObserver' in window) {
|
|
observer = new IntersectionObserver(scheduleUpdate, {
|
|
threshold: [0, 0.02, 0.1, 0.25, 0.5, 0.75, 1],
|
|
rootMargin: '0px 0px -35% 0px'
|
|
});
|
|
observed.forEach(t => observer.observe(t.el));
|
|
scheduleUpdate(); // Initial update
|
|
} else {
|
|
// Fallback for older browsers
|
|
['scroll','resize','load'].forEach(ev =>
|
|
window.addEventListener(ev, scheduleUpdate, { passive: true })
|
|
);
|
|
scheduleUpdate();
|
|
}
|
|
})();
|
|
</script>
|
|
</body>
|
|
|
|
</html>
|