129 lines
4.9 KiB
TypeScript
129 lines
4.9 KiB
TypeScript
/*
|
||
Skyline Bulletin — Simple, responsive "Coming Soon" page
|
||
File: src/App.tsx (React + Vite + TypeScript)
|
||
|
||
This single-file starter provides a focused, responsive "Coming Soon" landing page with:
|
||
- Hero with headline, subtitle, and email capture
|
||
- Responsive layout that works on mobile, tablet, desktop
|
||
- Accessible form (no backend; front-end mock) and success state
|
||
- Small features list and social links area
|
||
- Plain CSS inlined as separate file content below (create src/styles.css)
|
||
- No Tailwind; uses modern CSS with CSS variables and flex/grid
|
||
|
||
How to use:
|
||
1. Create a Vite + React + TS project if you haven't: `npm create vite@latest skyline-bulletin -- --template react-ts`
|
||
2. Install optional icons (lucide-react) if you want icons: `npm install lucide-react`
|
||
3. Replace src/App.tsx with this file and create src/styles.css with the CSS provided below.
|
||
4. Run `npm run dev`.
|
||
|
||
*/
|
||
|
||
import React, { useState } from "react";
|
||
import { Mail, Twitter, Linkedin, Sun, Moon } from "lucide-react";
|
||
import "./styles.css";
|
||
import logo from "./assets/Skybulletin_logo.jpeg";
|
||
|
||
export default function App(){
|
||
const [email, setEmail] = useState("");
|
||
const [subscribed, setSubscribed] = useState(false);
|
||
const [theme, setTheme] = useState<"light"|"dark">("light");
|
||
|
||
const onSubmit = (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
if (!email) return;
|
||
// mock subscribe
|
||
setSubscribed(true);
|
||
};
|
||
|
||
return (
|
||
<div className="cs-root" data-theme={theme}>
|
||
<header className="cs-header">
|
||
<img src={logo} alt="Skyline Bulletin logo" style={{ height: '50px'}}/>
|
||
<div className="brand">Skyline Bulletin</div>
|
||
<div className="header-actions">
|
||
<button onClick={() => setTheme(t => t === "light" ? "dark" : "light")} className="icon-btn" aria-label="Toggle theme">
|
||
{theme === 'light' ? <Moon /> : <Sun />}
|
||
</button>
|
||
</div>
|
||
</header>
|
||
|
||
<main className="cs-main">
|
||
<section className="hero">
|
||
<div className="hero-inner">
|
||
<h1 className="title">Skyline Bulletin — Coming Soon</h1>
|
||
<p className="subtitle">Short, verified, people-first news. Clean design. No noise. Launching soon.</p>
|
||
|
||
<div className="signup-card">
|
||
{subscribed ? (
|
||
<div className="subscribe-ok">
|
||
<strong>Thanks — you’re on the list.</strong>
|
||
<p className="muted">We’ll email you when Skyline Bulletin launches.</p>
|
||
</div>
|
||
) : (
|
||
<form onSubmit={onSubmit} className="signup-form" aria-label="Subscribe to launch updates">
|
||
<label className="sr-only">Email address</label>
|
||
<input
|
||
className="email-input"
|
||
type="email"
|
||
placeholder="you@email.com"
|
||
value={email}
|
||
onChange={(e) => setEmail(e.target.value)}
|
||
required
|
||
/>
|
||
<button className="btn-primary" type="submit">
|
||
<Mail className="icon-left" /> Notify me
|
||
</button>
|
||
</form>
|
||
)}
|
||
|
||
<div className="meta-row">
|
||
<span className="pill">No advertisment • Human-first</span>
|
||
<span className="muted">Expected launch: Q4</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="features">
|
||
<div className="feature">
|
||
<strong>Concise briefs</strong>
|
||
<p className="muted">Quick reads for busy days.</p>
|
||
</div>
|
||
<div className="feature">
|
||
<strong>Verified sources</strong>
|
||
<p className="muted">Transparent sourcing and context.</p>
|
||
</div>
|
||
<div className="feature">
|
||
<strong>People-first</strong>
|
||
<p className="muted">Human editorial judgement over algorithms.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<aside className="hero-side" aria-hidden>
|
||
<div className="visual-card">
|
||
<div className="visual-label">Preview</div>
|
||
<div className="visual-skyline">
|
||
<img src={logo} alt="preview" style={{height: '200px'}}/>
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
</section>
|
||
|
||
<section className="socials">
|
||
<div className="socials-inner">
|
||
<div className="small">Follow our progress</div>
|
||
<div className="icons">
|
||
<a href="#" aria-label="Twitter" className="social-link"><Twitter /></a>
|
||
<a href="#" aria-label="LinkedIn" className="social-link"><Linkedin /></a>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</main>
|
||
|
||
<footer className="cs-footer">
|
||
<div className="footer-inner">© {new Date().getFullYear()} Skyline Bulletin — Built with care.</div>
|
||
</footer>
|
||
</div>
|
||
);
|
||
}
|
||
|