A personal website I built for someone special β handwritten letters organized into chapters, a real-time backend, a gallery of cat photos, an app showcase, and a companion Android admin app. Fully private for her, with a friendly demo mode for everyone else.
I started writing letters to someone I cared about. As they piled up, I wanted somewhere thoughtful to put them β not a Notes app, not a Google Doc. Something with intention. So I built her a website.
What began as a single static page turned into a full system: chapters of letters managed from an Android app I made, a gallery of cat photos, and real-time syncing to a public site I can share. It's the kind of project that's been more fun to work on than anything I've shipped at school, because every detail mattered.
- π Live countdown to her birthday (down to the second)
- π Days-together counter that ticks up from the day we began
- π» Floating sunflower background that drifts gently across the screen
- π Light and dark mode with a hand-tuned sunflower palette
- π Chapters that organize letters into eras of our story
- π Cards that expand on tap to reveal the full message
- π©΅ Subtle hand-lettered headings using Mrs Saint Delafield and Playfair Display
- π» A showcase section for the small Android apps I built for her (Faithful, Faithfully)
- π₯ Each card has its own icon, tagline, version, download button, and source link
- βοΈ Fully modular β new apps are just an entry in a config array
- πΌ Grid of cat photos with shimmer loading skeletons
- π Lightbox viewer with prev/next navigation (click, swipe, or arrow keys)
- πΈ Optimized images β small thumbnails for the grid, full-res in the lightbox
βοΈ Captions that fade in on hover, always visible on touch devices
- π Email/password sign-in for Ali; her account is the only one that unlocks the real content
- π Demo mode for everyone else β the site auto-shows placeholder chapters, sample letters, and free stock cat photos so portfolio visitors can see the layout without seeing anything personal
- π·οΈ Every sample card and photo gets a subtle "SAMPLE" watermark
- π Session persists forever β Ali only signs in once per device
- β‘ Real-time updates via Firestore β letters and photos appear instantly after the admin app adds them
- π± Collapsible side drawer on desktop, bottom nav on mobile β same components, fully responsive
- βοΈ Firebase Storage for photo hosting with public CDN delivery
- π Two-layer security β client-side auth UI + Firestore security rules that require an allow-listed UID
- π¨ Modular CSS architecture β split into 8 focused files instead of one giant
styles.css
| Home | Letters | Gallery | Dark mode |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
| Layer | What I used |
|---|---|
| Frontend | Vanilla HTML, CSS, JavaScript β no frameworks, no build step |
| Database | Firebase Firestore (real-time NoSQL) |
| File storage | Firebase Storage (photo CDN) |
| Auth | Firebase Auth β email/password, UID-restricted reads AND writes |
| Hosting | GitHub Pages with free HTTPS |
| Fonts | Google Fonts β Playfair Display, Mrs Saint Delafield, Inter |
| Companion | Faithfully App β Android app in Java |
I intentionally avoided a framework. The whole site is a few hundred lines of plain JavaScript and CSS split into modular files, which keeps it fast, light, and easy to maintain.
.
βββ index.html # entry point
βββ script.js # all interactivity β sections, countdowns, Firestore, gallery, auth, apps config
βββ css/
β βββ base.css # reset, variables, shared animations
β βββ layout.css # app shell, drawer (with collapse), bottom nav, sticky header
β βββ home.css # header, countdowns, floating background
β βββ letters.css # cards, chapters
β βββ apps.css # app showcase cards
β βββ gallery.css # photo grid + lightbox
β βββ auth.css # login overlay, demo banner, "SAMPLE" watermarks
β βββ theme.css # all dark mode overrides
βββ apps/ # APK downloads served from the Apps section
β βββ Faithful.apk
β βββ Faithfully.apk
βββ icons/ # app icons + favicon
β βββ faithful.png
β βββ faithfully.png
β βββ icon.png
βββ screenshots/ # images used in this README
β βββ home.png
β βββ letters.png
β βββ gallery.png
β βββ dark-mode.png
βββ CNAME # custom domain config for GitHub Pages
βββ LICENSE
βββ README.md
ββββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β Admin Android β β User β β Everyone else β
β App β β (signed in) β β (demo mode) β
β (Java + XML) β β β β β
ββββββββββ¬ββββββββββ ββββββββββ¬βββββββββ ββββββββββ¬βββββββββ
β writes β reads β reads
β (UID-restricted) β (UID-restricted) β mock data
β β β from script.js
βββββββββββββ¬βββββββββββββββββ β
βΌ βΌ
ββββββββββββββββββββββββββββββββββ (no Firebase call)
β Firebase β
β β’ Firestore β
β - chapters/ β
β - cards/ β
β - gallery/ β
β β’ Storage β
β - gallery/*.jpg β
β β’ Auth β
β - allow-listed UIDs only β
ββββββββββββββββββββββββββββββββββ
The Android app is the only thing that can write β security rules enforce my admin UID. Reads are restricted to two UIDs: mine (admin) and Ali's. Everyone else sees the demo version of the site, rendered from placeholder data hardcoded in script.js, without ever hitting Firestore.
It's a static site, so:
git clone https://github.com/mowtiie/Faithfully-Web.git
cd Faithfully-Web
# Open index.html in any browser, or serve locally:
python3 -m http.server 8000For Firestore data and gallery photos to load, you'd need to point script.js at your own Firebase project, create your own admin + viewer accounts, and paste the resulting UIDs into ALLOWED_UIDS in script.js and the firestore.rules / storage.rules files. See AUTH_SETUP.md for the full walkthrough.
Without configuring auth, you'll still see the site β just the demo mode with placeholder content.
- How to design and ship a small full-stack system end to end
- Firestore security rules β the API key in client code is fine when paired with strong rules and HTTP referrer restrictions
- Splitting CSS into focused files at the right time is way better than a 1500-line
styles.css - Real-time UIs feel magical when they work β uploading a photo from my phone makes it appear on the site in under a second
- Image optimization is critical β serving 5MB phone photos directly would have made the gallery painfully slow
- Two-layer auth (client-side UI + server-side rules) is the right pattern β the UI is the pleasant experience, the rules are the actual security
- Demo mode with hardcoded mock data is a lovely way to make a private site publicly showcase-able without ever exposing anything real
Her Mowtiie.
Made with π» for someone who already knows it's hers.



