TidelockLaunch Waitlist
Trang “sắp ra mắt” với các hành tinh quay quanh quỹ đạo, đồng hồ đếm ngược kiểu bảng lật và danh sách chờ trao cho mỗi người đăng ký một link giới thiệu
Chưa mở bán
Sản phẩm này sắp mua được. Để lại email, tiệm báo bạn ngay khi mở bán.
- Trả một lần, dùng trọn đời
- Cập nhật miễn phí trọn đời
- Đủ mã nguồn: HTML / CSS / JS + React
Đang chạy trực tiếp
Giới thiệu
Trang ra mắt cho một sản phẩm sắp tới nơi. Tidelock là một ứng dụng lịch hư cấu, chuyên tìm ra khung giờ mà ai cũng rảnh, nên cả trang xoay quanh một ý: mọi thứ thẳng hàng. Ở hero, bốn hành tinh quay quanh một mặt trời nhỏ trong ô cửa sổ nhìn ra trời đêm; cứ mười tám giây chúng lại xếp thành một đường thẳng và một vệt sáng chạy xuyên qua. Ngay bên dưới, bảng lật đếm ngược tới ngày ra mắt, từng thẻ một.
- Đăng ký mà như được thưởng: “You're #1,284 in line” đếm lên, pháo giấy bung ra, và người xem nhận link mời riêng để sao chép hay chia sẻ
- “Lên 3 bậc cho mỗi người bạn đăng ký” — mã giới thiệu đi theo link và quay về cùng lượt đăng ký
- Bốn thẻ hé lộ tính năng với hình minh hoạ tạo sẵn, bị làm mờ và đóng dấu Classified cho tới khi bạn rê chuột, focus hay chạm vào
- Bằng chứng xã hội sống động: avatar tạo từ tên, con số lớn tăng lên khi có người đăng ký, và ba nhận xét ngắn
- FAQ chạy không cần JavaScript, và một lời kêu gọi ở cuối trang
- Nội dung mẫu chân thực cho một sản phẩm hư cấu, sẵn sàng thay bằng của bạn
Lượt đăng ký đi về đâu tuỳ bạn: Formspree, Supabase hay API riêng, chỉ bằng một thiết lập. Trước khi nối, adapter demo lưu chúng ngay trong trình duyệt nên toàn bộ luồng vẫn chạy khi không có mạng.
Gói có hai bản. Bản HTML là một trang duy nhất, mở bằng trình soạn thảo nào cũng sửa được và đặt
lên host nào cũng chạy. Bản React là một dự án Vite + TypeScript hoàn chỉnh: mọi chữ, ngày giờ và
thiết lập nằm trong một file content.ts, chạy npm install && npm run dev là lên.
Tính năng · 11
- Trọn một trang: header, hero với cảnh quỹ đạo vẽ bằng canvas, đồng hồ đếm ngược kiểu bảng lật, thẻ đăng ký danh sách chờ, thẻ hé lộ tính năng, khối bằng chứng xã hội kèm nhận xét, FAQ, lời kêu gọi cuối trang, footer
- Hai bản: HTML/CSS/JS tĩnh sửa trực tiếp, và một dự án Vite + React + TypeScript chạy được ngay, mọi chữ và mọi thiết lập nằm trong một file content.ts
- Đếm ngược kiểu bảng lật tới bất kỳ ngày nào — hoặc ghi tương đối "+23d 7h" cho bản demo — và tự chuyển sang khối “We're live” khi tới giờ
- Form danh sách chờ kiểm tra email, có đủ bốn trạng thái rõ ràng: chờ nhập, đang gửi, lỗi và thành công
- Khi thành công: hiện vị trí của người xem (“You're #1,284 in line”, số đếm lên), link giới thiệu riêng có nút sao chép, nút chia sẻ lên X, LinkedIn, WhatsApp, email, bảng chia sẻ của trình duyệt nếu có, và một màn pháo giấy
- Backend cắm-là-chạy: một thuộc tính endpoint là gửi JSON tới Formspree, một hàm Supabase hay API của bạn; không đặt thì adapter demo lưu đăng ký ngay trong trình duyệt, vị trí cố định cho mỗi email
- Link giới thiệu chạy trọn vòng: mã ?ref= được đọc khi khách vào trang và gửi kèm lượt đăng ký; bản demo đẩy người giới thiệu lên ba bậc cho mỗi người bạn
- Số “đang xếp hàng” tăng ngay khi có người đăng ký; avatar được tạo từ tên nên không cần ảnh phải mua bản quyền
- Giao diện sáng và tối: theo hệ thống, nhớ lựa chọn của người xem; bầu trời đêm ở hero và mọi màu đều là CSS custom property
- Script và stylesheet cộng lại chỉ khoảng 16 KB sau gzip, không web font, không gọi ra ngoài
- Không phụ thuộc thư viện nào ngoài React ở bản React
Cách dùng
Mở index.html bằng trình soạn thảo: mọi chữ trên trang đều nằm ngay trong đó, nên đó cũng là nội
dung công cụ tìm kiếm đọc được. File này gọi hai file nằm cạnh nó:
<link rel="stylesheet" href="launch-waitlist.css" />
<script src="launch-waitlist.js" defer></script>- Ngày ra mắt:
<div data-countdown data-launch="2027-03-21T09:00:00Z">. Nhớ ghi múi giờ (Zhoặc+07:00). Bản demo dùng"+23d 7h 41m"— tính từ lúc trang mở — nên không bao giờ hết giờ; hãy thay bằng ngày thật. Người xem thấy ngày giờ theo múi giờ của chính họ. Khi qua mốc đó, bảng lật được thay bằng khốidata-countdown-live(“We're live.”). - Đăng ký: thẻ
data-waitlist. Xem phần Lượt đăng ký đi về đâu bên dưới. - Link giới thiệu:
data-share-urllà địa chỉ mà link mời trỏ tới (…?ref=k3x9a1), còndata-spotslà số bậc mỗi người bạn mang lại, dùng trong câu chữ. - Thẻ hé lộ: mỗi tính năng là một
<li>, cứ sao chép thêm.data-artchọn hình minh hoạ tạo sẵn (grid,rings,alerts,tides) — hoặc đặt<img class="lw-art" src="…" alt="">vào chỗ đó. - Số người đang chờ: mọi
<span data-waitlist-count="1283">1,283</span>đều tăng khi có người đăng ký. Avatar là<li class="lw-avatar" style="--hue: 105; --hue-2: 160">MO</li>; hue nào cũng được.
Script tự gắn mọi hành vi theo data attribute khi trang tải xong. Với phần HTML thêm vào sau, gọi
LaunchWaitlist.init(container); hàm này trả về một hàm để gỡ bỏ. Mã nguồn dễ đọc nằm trong src/
(src/core/ có mỗi hành vi một module nhỏ) nếu bạn muốn build lại.
Bản tải về là một dự án Vite + React 19 + TypeScript hoàn chỉnh (React 18 cũng chạy):
npm install
npm run dev # http://localhost:5173
npm run build # static files in dist/src/app/content.ts— mọi chữ, ngày ra mắt (LAUNCH.at), endpoint và thiết lập demo (WAITLIST). Sửa ở đây là cả trang đổi theo.src/app/sections/— mỗi section một component;src/app/ui/— các atom, molecule và organism dựng nên chúng.src/core/— phần hành vi (đếm ngược, cảnh quỹ đạo, danh sách chờ, pháo giấy…), đúng đoạn mã bản HTML đang chạy, gắn vào qua hookuseBehaviour.src/launch-waitlist.css— stylesheet duy nhất, dùng chung cho cả hai bản.
App.tsx mở đầu bằng "use client" nên đặt thẳng vào App Router được. Chép src/app vào, chẳng
hạn, components/launch, rồi đặt src/core và src/launch-waitlist.css cạnh nó trong
components/ (các import là đường dẫn tương đối: ../core/…, ../launch-waitlist.css). Sau đó:
// app/page.tsx
import { App } from "@/components/launch/App";
export default function Page() {
return <App />;
}Thêm id="top" cho <body> trong app/layout.tsx (logo và link “Back to top” trỏ về đó). Muốn áp
theme đã lưu trước lần vẽ đầu tiên, chép đoạn script nhỏ trong index.html vào <head> của layout
và thêm suppressHydrationWarning cho <html>.
Thẻ đăng ký gọi một adapter với { email, ref } rồi hiển thị kết quả trả về. Chọn một trong các
cách sau:
Demo (mặc định). Không đặt endpoint thì lượt đăng ký nằm lại trong trình duyệt của người xem
(localStorage, key launch-waitlist-demo; nằm trong bộ nhớ nếu storage bị chặn). Người đầu tiên
là #1,284 (data-demo-base="1283" + 1); cùng một email luôn giữ cùng vị trí; đăng ký từ link của ai
thì người đó được lên data-spots bậc. Thử một địa chỉ bắt đầu bằng fail@ để xem trạng thái lỗi.
Endpoint bất kỳ. Đặt data-endpoint (React: WAITLIST.endpoint). Thẻ sẽ gửi
POST <your endpoint>
Content-Type: application/json
Accept: application/json
{ "email": "[email protected]", "ref": "k3x9a1", "source": "launch-waitlist" }và mọi phản hồi 2xx đều được tính là đã đăng ký. Nếu phản hồi là JSON có position (hoặc rank),
referralCode (hoặc referral_code), total và returning, thẻ sẽ dùng chúng; thiếu trường nào
thì có giá trị thay thế (không có vị trí: “You're on the list.”; không có mã: tạo từ địa chỉ email).
Mã trạng thái khác, hoặc không có phản hồi, sẽ hiện trạng thái lỗi và giữ nguyên những gì người xem
đã nhập. Header bổ sung đặt trong data-endpoint-headers='{"apikey":"…"}' (React:
WAITLIST.endpointHeaders).
Formspree. Tạo một form rồi dùng địa chỉ của nó — không cần viết mã:
<div data-waitlist data-endpoint="https://formspree.io/f/your-form-id" …>Formspree lưu từng email kèm ref, nên bạn biết ai đã mời ai; nhưng Formspree không xếp hạng, nên
người xem sẽ thấy “You're on the list.” thay vì một con số.
Supabase. Một bảng và một hàm là có vị trí thật và link giới thiệu chạy thật. Trong SQL editor:
create table public.waitlist (
id bigint generated always as identity primary key,
email text not null unique,
referral_code text not null unique default substr(md5(random()::text), 1, 6),
referred_by text,
bonus int not null default 0,
source text,
created_at timestamptz not null default now()
);
-- Row level security with no policies: visitors cannot read or write the table directly.
alter table public.waitlist enable row level security;
create or replace function public.join_waitlist(email text, ref text default null, source text default null)
returns json
language plpgsql
security definer
set search_path = public
as $$
declare
entry public.waitlist;
is_new boolean := false;
begin
select * into entry from public.waitlist w where w.email = lower(trim(join_waitlist.email));
if not found then
insert into public.waitlist (email, referred_by, source)
values (lower(trim(join_waitlist.email)), join_waitlist.ref, join_waitlist.source)
returning * into entry;
is_new := true;
-- Each friend moves the referrer up three places.
update public.waitlist w set bonus = w.bonus + 3 where w.referral_code = join_waitlist.ref and w.id <> entry.id;
end if;
return (
select json_build_object('position', ranked.position, 'referralCode', ranked.referral_code, 'total', ranked.total, 'returning', not is_new)
from (
select w.id, w.referral_code, row_number() over (order by w.id - w.bonus, w.id) as position, count(*) over () as total
from public.waitlist w
) ranked
where ranked.id = entry.id
);
end;
$$;
grant execute on function public.join_waitlist(text, text, text) to anon;Sau đó trỏ thẻ tới hàm, kèm anon key công khai của project (key này vốn để công khai; bảng vẫn kín vì chỉ hàm trên mới đụng được vào):
<div data-waitlist
data-endpoint="https://YOUR-PROJECT.supabase.co/rest/v1/rpc/join_waitlist"
data-endpoint-headers='{"apikey":"YOUR-ANON-KEY","Authorization":"Bearer YOUR-ANON-KEY"}' …>API của riêng bạn. Nhận JSON như trên, trả về JSON dạng
{ "position": 1284, "referralCode": "k3x9a1", "total": 1284, "returning": false }, và bật CORS cho
domain của trang nếu API nằm ở domain khác.
Adapter viết bằng JavaScript. Với mọi trường hợp khác (một SDK, một hàng đợi…), viết
async ({ email, ref }) => ({ position, referralCode, total, returning }). Ở bản HTML, đổi tên thuộc
tính data-waitlist của thẻ (để script tự động bỏ qua) rồi tự gắn:
LaunchWaitlist.createWaitlist(element, { adapter }). Ở bản React, dùng
(element) => createWaitlist(element, { adapter }) — khai báo bên ngoài component — trong
WaitlistCard.tsx.
Thông báo lỗi nằm trên ô nhập (data-error-required, data-error-invalid) và trên thẻ
(data-failure). Ô ẩn _gotcha bắt các bot spam đơn giản: khi ô này có chữ, không gì được gửi đi và
bot chỉ thấy trạng thái thành công.
Mọi thứ là custom property trên :root, ở đầu file launch-waitlist.css. Màu được viết dạng
light-dark(sáng, tối), nên một dòng là đủ cho cả hai giao diện.
| Property | Thay đổi gì |
|---|---|
--lw-bg, --lw-bg-raised, --lw-glass, --lw-text, --lw-muted, --lw-line, --lw-line-strong |
Nền trang, ô nhập, thẻ kính mờ, chữ và đường kẻ mảnh |
--lw-violet, --lw-coral, --lw-teal, --lw-accent-text |
Màu thương hiệu: chữ gradient, điểm nhấn, eyebrow |
--lw-aurora-1, --lw-aurora-2, --lw-aurora-3 |
Các vầng sáng mềm ở nền trang |
--lw-cta-from, --lw-cta-to, --lw-on-cta |
Nút gradient và chữ trên nút |
--lw-focus, --lw-danger, --lw-success |
Viền focus, lỗi form, dòng “You're in” |
--lw-flip-top, --lw-flip-bottom, --lw-flip-text, --lw-flip-height, --lw-flip-radius, --lw-flip-duration |
Thẻ bảng lật: màu, kích thước, tốc độ lật |
--lw-sky-1, --lw-sky-2 |
Bầu trời đêm trong ô cửa sổ |
--lw-scene-star, --lw-scene-glow, --lw-scene-orbit, --lw-scene-beam, --lw-scene-sky-star, --lw-planet-1 … --lw-planet-4 |
Cảnh canvas: mặt trời, quỹ đạo, vệt sáng khi thẳng hàng, sao, hành tinh |
--lw-confetti-1 … --lw-confetti-5 |
Màu pháo giấy |
--lw-art-from, --lw-art-to, --lw-art-surface, --lw-peek-blur |
Hình minh hoạ thẻ hé lộ và độ mờ ban đầu |
--lw-font-body, --lw-font-display, --lw-font-mono, --lw-display-weight |
Các vai trò chữ |
--lw-radius-lg, --lw-radius, --lw-radius-sm, --lw-max, --lw-gutter, --lw-section-space, --lw-header-height |
Hình khối và bố cục |
--lw-ease, --lw-reveal-duration |
Chuyển động |
Các canvas đọc màu khi khởi động và mỗi lần đổi theme.
Font: trang chỉ dùng font hệ thống nên không tải gì thêm. Muốn dùng font riêng, thêm @font-face
(hoặc stylesheet của dịch vụ font) rồi đặt, ví dụ, --lw-font-display: "Your Display", sans-serif;.
Theme: data-theme="light" hoặc "dark" trên <html> cố định theme lúc đầu; không có thì theo
cài đặt hệ thống của người xem. Nút chuyển nhớ lựa chọn trong localStorage (launch-waitlist-theme).
Cả hai bản đều là file tĩnh: tải thư mục HTML, hoặc thư mục dist/ của dự án React sau khi chạy
npm run build, lên Netlify, Vercel, Cloudflare Pages, GitHub Pages hay host web bất kỳ.
Hỏi về sản phẩm này
Chưa chắc có hợp dự án của bạn? Cứ hỏi — câu trả lời lấy từ chính trang này.
Lịch sử phiên bản
1.0.0First releaseMới nhất
A coming-soon launch page in two builds — static HTML/CSS/JS and a Vite + React + TypeScript project — with an orbiting canvas hero, a flip countdown with a live state, a waitlist form with referral links, sharing and confetti, sneak-peek cards, social proof, FAQ, and light and dark themes.
- Bản sửa lỗi và tính năng mới đều có ngay ở nút tải này — file tải về luôn là bản mới nhất.
Có thể bạn sẽ thích
- TemplateTrang đích
Lumora — SaaS Landing
Trang đích một trang cho sản phẩm SaaS, với bầu trời cực quang chuyển động và một cửa sổ ứng dụng tự chạy thử nghiệm
9.000đ
- TemplateTrang đích
Lumora — SaaS Landing
Trang đích một trang cho sản phẩm SaaS, với bầu trời cực quang chuyển động và một cửa sổ ứng dụng tự chạy thử nghiệm
$4.90


