feat: add flo

This commit is contained in:
rizaldy 2024-06-14 02:57:21 +07:00
parent 377891006b
commit bfa4c82afc
7 changed files with 584 additions and 0 deletions

380
assets/css/flo.css Normal file
View File

@ -0,0 +1,380 @@
@font-face {
font-display: swap;
font-family: 'Plus Jakarta Sans';
font-style: normal;
font-weight: 400;
src: url('../fonts/plus-jakarta-sans-v8-latin-regular.woff2') format('woff2');
}
@font-face {
font-display: swap;
font-family: 'Plus Jakarta Sans';
font-style: normal;
font-weight: 700;
src: url('../fonts/plus-jakarta-sans-v8-latin-700.woff2') format('woff2');
}
@font-face {
font-display: swap;
font-family: 'Plus Jakarta Sans';
font-style: normal;
font-weight: 800;
src: url('../fonts/plus-jakarta-sans-v8-latin-800.woff2') format('woff2');
}
:root {
--body-bg-color: #fff;
--base-font-size: 18px;
--font-family-sans-serif: "Plus Jakarta Sans", sans-serif;
--font-heading: var(--font-family-sans-serif);
--font-body: var(--font-family-sans-serif);
--primary-color: #2B205E;
--secondary-color: #5D21D0;
--text-color: var(--primary-color);
--anchor-color: var(--primary-color);
}
::selection {
background-color: var(--primary-color);
color: #ffffff;
}
.c-navbar {
z-index: 1;
position: relative;
width: 100%;
}
.c-navbar__wrapper {
display: flex;
align-items: center;
justify-content: space-between;
flex-direction: column;
}
.c-navbar h2 {
padding: 0;
font-family: inherit;
font-style: normal;
font-weight: bold;
font-size: 28px;
}
.c-navbar ul {
display: flex;
}
.c-navbar li {
color: var(--primary-color);
font-weight: bold;
padding: 0 0.5rem;
}
.c-hero {
background-color: var(--primary-color);
min-height: 300px;
color: #ffffff;
display: flex;
padding: 2rem;
align-items: center;
position: relative;
}
.c-hero h1 {
font-size: 3rem;
font-weight: bold;
margin-bottom: 1rem;
}
.c-hero h3 {
color: #ddd;
line-height: 1.8rem;
}
.c-hero__right {
max-height: 440px;
overflow: hidden;
position: absolute;
right: 50px;
bottom: 0;
display: none;
}
.c-button {
border: 1px solid transparent;
border-radius: 5px;
font-family: inherit;
background-color: #fff;
color: var(--primary-color);
font-weight: bold;
font-size: inherit;
padding: 1rem 2rem;
display: block;
}
.c-button--cta {
margin-top: 2rem;
}
.c-hero img {
border-radius: 30px;
width: 230px;
max-width: 100%;
border: 5px solid #111;
}
.c-button--cta-navbar {
color: #ffffff;
background-color: var(--primary-color);
padding: 10px 20px;
margin: 2rem 0;
}
.c-section {
margin-top: 4rem;
text-align: center;
}
.c-section--flex {
display: grid;
grid-template-columns: auto;
}
.c-section h3 {
font-size: 3rem;
font-family: inherit;
font-weight: bold;
}
.c-section h4 {
font-size: 1.8rem;
font-family: inherit;
font-weight: bold;
}
.c-card {
text-align: left;
padding: 2rem;
border: 1px solid #eee;
border-radius: 10px;
background-color: #eee;
}
.c-card h4 {
margin-bottom: 13.37px;
line-height: 2.3rem;
color: var(--primary-color);
}
.c-card p {
font-size: 1rem;
line-height: 1.5rem;
}
.c-hero--second {
display: block;
text-align: left;
margin-top: 6rem;
}
.c-hero--second ul {
margin-top: 3rem;
margin-bottom: 3rem;
max-width: 600px;
}
.c-hero--second li {
margin-bottom: 15px;
list-style: square;
line-height: 1.8rem;
}
.c-faq {
max-width: 666px;
padding: 2rem;
padding-top: 6rem;
}
.c-faq__section {
margin-bottom: 3rem;
}
.c-faq h4 {
color: var(--primary-color);
font-size: 3rem;
font-family: inherit;
font-weight: bold;
margin-bottom: 2rem;
}
.c-faq h5 {
color: var(--primary-color);
font-size: 1.8rem;
font-family: inherit;
font-weight: bold;
margin-bottom: 0.5rem;
}
.c-faq p {
color: #666;
}
.c-section__title {
color: var(--primary-color);
margin-bottom: 10px;
}
.c-navbar a {
text-decoration: none;
}
.c-faq a:hover,
.c-navbar a:hover {
opacity: 0.6;
}
#features {
padding-top: 2rem;
}
.js-modal {
display: none;
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.8);
z-index: 2;
}
.js-modal__content {
max-width: 500px;
height: 300px;
position: absolute;
left: 50%;
top: 50%;
margin-left: -200px;
margin-top: -150px;
background-color: #ffffff;
padding: 2rem;
border-radius: 10px;
z-index: 3;
text-align: center;
overflow: hidden;
}
.js-modal h2 {
font-family: inherit;
font-style: normal;
font-weight: bold;
margin-bottom: 1.8rem;
}
.js-modal input {
width: 100%;
padding: 0.8rem 1rem;
box-sizing: border-box;
font-family: inherit;
border: 2px solid var(--primary-color);
color: var(--primary-color);
border-radius: 3px;
margin-top: 2rem;
}
.js-modal button {
width: 100%;
padding: 0.8rem 1rem;
box-sizing: border-box;
font-family: inherit;
border: 2px solid var(--primary-color);
color: var(--primary-color);
border-radius: 3px;
margin-top: 2rem;
background-color: var(--primary-color);
color: #ffffff;
font-weight: bold;
font-size: 16px;
}
.js-modal__close {
float: right;
font-size: 28px;
font-weight: bold;
margin-top: -20px;
margin-right: -10px;
color: var(--primary-color);
}
.c-button--cta,
.c-button--cta-navbar,
.js-modal__close,
.js-modal button {
cursor: pointer;
}
@media screen and (min-width: 30em) {
.c-navbar {
top: 0;
left: 0;
width: 100%;
display: initial;
padding: 0px 10px;
border-radius: 0px;
}
.c-navbar__wrapper {
margin-top: -8rem;
}
.c-navbar li {
padding: 0 1rem;
}
}
@media screen and (min-width: 60em) {
.c-navbar {
top: 0;
left: 50%;
width: 100%;
max-width: 1440px;
position: fixed;
margin-left: -720px;
margin-top: 10px;
padding: 0px 20px;
border-radius: 100px;
background-color: #eee;
}
.c-navbar__wrapper {
margin-top: 0;
flex-direction: row;
}
.c-button--cta-navbar {
margin: 0;
}
.c-navbar li {
padding: 0 2rem;
}
.c-hero {
border-radius: 10px;
padding: 4rem;
}
.c-hero__right {
display: initial;
}
.c-section--flex {
grid-template-columns: auto auto auto;
grid-gap: 20px;
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/img/flo_hero.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@ -0,0 +1,24 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>flo</title>
<link rel="icon" type="image/png" sizes="32x32" href="/assets/img/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/assets/img/favicon-16x16.png">
<link href="/assets/css/reset.css" rel="stylesheet">
<link href="/assets/css/style.css" rel="stylesheet">
<link href="/assets/css/flo.css" rel="stylesheet">
</head>
<body>
<main class="l-container">
{{ content }}
</main>
</body>
</html>

180
src/flo/index.liquid Normal file
View File

@ -0,0 +1,180 @@
---
title: Home
layout: layouts/flo
---
<nav class="c-navbar">
<div class="c-navbar__wrapper">
<div class="c-navbar__left">
<h2><a href="#">flo</a></h2>
</div>
<div class="c-navbar__center">
<ul>
<li><a href="#features">Features</a></li>
<li><a href="#faq">FAQ</a></li>
<li><a target="_blank" rel="noopener noreferer" href="https://github.com/kepelet/flo">Source Code</a></li>
<li><a target="_blank" rel="noopener noreferer" href="https://github.com/orgs/kepelet/projects/1/views/1">Roadmap</a></li>
</ul>
</div>
<div class="c-navbar__right">
<button class="c-button c-button--cta-navbar" id="openModal">Join the waitlist</button>
</div>
</div>
</nav>
<div class="c-hero">
<div>
<h1>Meet flo</h1>
<h3>An open source Navidrome client written in Swift.</h3>
<button class="c-button c-button--cta" id="openModal2">Join the waitlist</button>
<div class="c-hero__right">
<img src="/assets/img/flo_hero.png" alt="flo">
</div>
</div>
</div>
<div class="c-section">
<h3 class="c-section__title">Is flo right for you?</h3>
<p class="c-section__subtitle">If you have Navidrome at home <em>and</em> an iPhone, maybe give flo a try.</p>
</div>
<div class="c-section c-section--flex">
<div class="c-card">
<h4>Modern, familiar interface</h4>
<p>flo is written in Swift and relies heavily on SwiftUI, a UI framework by Apple for Apple devices.</p>
</div>
<div class="c-card">
<h4>Navidrome native client</h4>
<p>While Navidrome supports Subsonic APIs, flo was purposely designed for Navidrome servers.</p>
</div>
<div class="c-card">
<h4>Customizations</h4>
<p>Make flo look the way you want — Custom app icons? Check. Color theme? Check. Hot girlfriend with sweatpants and a hoodie? Color theme? Check.</p>
</div>
</div>
<div id="features">
<div class="c-hero c-hero--second">
<h1>Features</h1>
<h3>flo is a music player app, meaning it plays music. But wait, there is more.</h3>
<ul>
<li>Scrobble — Share your listening activity. A ListenBrainz or Last.fm account is required. Discord now playing status may also be supported.</li>
<li>Offline listening — Save your mobile data plan, keep the flow even when the server is down, or maybe when you're nowhere. </li>
<li>Plus — Opt-in, nice-to-have, unnecessary features that are not related to your listening experience but may make it more fun. Coming soon.</li>
</ul>
<p>Eventually, flo is an Open Source Software (OSS) and is licensed under the permissive MIT License. Anyone can help and contribute to the project to make flo better for everyone.</p>
<p>Did I mention that flo is <strong>free?</strong></p>
</div>
</div>
<div id="faq" class="c-faq">
<h4>FAQs</h4>
<div class="c-faq__section">
<h5>What is flo?</h5>
<p>flo is an open source Navidrome client written in Swift. iPadOS and macOS support is planned, by the way.</p>
</div>
<div class="c-faq__section">
<h5>What is Navidrome?</h5>
<p>Navidrome is an open source web-based music collection server and streamer. It gives you freedom to listen to your music collection from any browser or mobile device. It's like your personal Spotify! Learn more <a target="_blank" rel="noopener noreferer" href="https://www.navidrome.org/">here.</a></p>
</div>
<div class="c-faq__section">
<h5>Is this the official Navidrome client?</h5>
<p>Nope.</p>
</div>
<div class="c-faq__section">
<h5>Why are there no Android clients?</h5>
<p>You can check the <a target="_blank" rel="noopener noreferer" href="https://www.subsonic.org/pages/apps.jsp">Subsonic Apps</a> list for alternatives.</p>
</div>
<div class="c-faq__section">
<h5>Any tutorial on running a Navidrome server?</h5>
<p>Great! I wrote one here.</p>
</div>
<div class="c-faq__section">
<h5>Where to get songs?</h5>
<p>My personal favorite is the iTunes Store. It's DRM-free, relatively affordable and also offers higher quality 256 kbps AAC encoding. I don't really understand the number but I think that sounds good.</p>
</div>
<div class="c-faq__section">
<h5>So we can actually purchase a song?</h5>
<p>Always has been.</p>
</div>
<div class="c-faq__section">
<h5>Is it any good?</h5>
<p><a href="https://github.com/glenjamin/node-fib/#is-it-any-good" target="_blank" rel="noopener noreferer">Yes.</a></p>
</div>
<div class="c-faq__section">
<p>If your question is not listed here, I'm reachable via email at oss@rizaldy.club or @rizaldy@edgy.social on Fediverse. You can also use GitHub Discussions for non-private questions.</p>
</div>
</div>
<div class="js-modal" id="modal">
<div class="js-modal__content">
<span class="js-modal__close" id="closeModal">&times;</span>
<h2>Join the waitlist</h2>
<p>Currently flo is in early preview and we'll let you know when we're on the App Store.</p>
<input id="email" type="email" placeholder="john@doe.com" required>
<button id="submit"">Submit</button>
</div>
</div>
<script>
const modal = document.getElementById('modal')
const openModal = document.getElementById('openModal')
const openModal2 = document.getElementById('openModal2')
const closeModal = document.getElementById('closeModal')
const emailForm = document.getElementById('email')
const submitButton = document.getElementById('submit')
openModal.onclick = function() {
modal.style.display = 'block'
}
openModal2.onclick = function() {
modal.style.display = 'block'
}
closeModal.onclick = function() {
modal.style.display = 'none'
}
window.onclick = function () {
if (event.target === modal) {
modal.style.display = 'none'
}
}
submitButton.onclick = function() {
if (!emailForm.value.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) {
window.alert("email doesnt seem valid")
return
}
const form = new FormData()
form.append('payload', emailForm.value)
fetch('https://flo.edgy0.workers.dev/', {
method: 'POST',
body: form
})
.then(res => res.text())
.then(text => {
window.alert(text)
modal.style.display = 'none'
emailForm.value = ''
})
.catch(err => {
window.alert("Something went wrong ", err.message)
})
}
</script>