This is a submission for Frontend Challenge: Comfort Food Edition, Perfect Landing: Comfort Food.
Inspiration
Demlik — the word means "teapot" — is a Turkish tea house that exists only in my head: four tables, one wood stove, a cat named Poğaça, and Black Sea tea brewed the slow way — twenty minutes, two pots, no shortcuts. It's every neighborhood tea house in Türkiye at once, and the landing page tries to feel the way those places feel: warm, unhurried, and a little playful.
(It's the companion piece to my CSS Art submission — same tea, different discipline.)
Demo
https://codepen.io/melik-baryank/pen/XJpwLap
Things to try:
- Watch the hero on load — the copy rises line by line, the glass fills with tea, the steam starts only after the pour, and an underline draws itself beneath the last word.
- Scroll — sections reveal with staggered timing, and in Chromium the hero art drifts with a scroll-driven animation (
animation-timeline: scroll(), wrapped in@supportsso other browsers gracefully skip it). - Hover a menu card — it lifts, a light sweep crosses it, and the dish icon wobbles.
- Toggle dark mode — colors cross-fade smoothly, the choice persists, and with no choice made the page follows your system preference.
- Tab through the page — skip link, visible focus rings, and a keyboard-friendly mobile menu (Escape closes it).
- Check the opening hours — today's row is highlighted with a pulsing dot and marked with
aria-current="date". - Submit the form empty or with a typo — accessible inline errors via
aria-describedby/aria-invalid; success pops in and is announced through a polite live region. - Hover the scrolling ticker — it pauses. Hover the cat fact — 🐾.
Journey
One HTML file, no frameworks, no build step: semantic HTML, vanilla CSS and ~70 lines of JavaScript.
Accessibility first. Landmarks, a skip link, :focus-visible rings on everything interactive, an hours table with caption and scope, form errors in a role="alert" region wired with aria-describedby + aria-invalid (so they're announced even on an Enter-key submit), success in a polite role="status" region, and aria-pressed on the theme toggle. Every dish is named in English first with its Turkish original beneath, and those originals carry lang="tr" so screen readers switch pronunciation instead of mangling them — nothing on the page is left untranslated for an English reader. Closing the mobile menu with Escape returns focus to the button that opened it. The auto-scrolling ticker is pausable by keyboard focus, not just hover (WCAG 2.2.2), and lists that lose their markers keep role="list" for VoiceOver. Button and badge foregrounds are theme tokens (--on-brand, --on-accent) so text contrast clears WCAG AA in both palettes — checked, not assumed. Every animation on the page — steam, drifting bread ring, ticker, reveals — is disabled under prefers-reduced-motion.
Theming. Design tokens as custom properties, defined light-first. Dark mode comes in three layers: system preference by default (prefers-color-scheme), an explicit data-theme override from the toggle, and localStorage to remember the choice. The moon-cutout icon on the toggle only appears when dark is actually active.
The hero illustration. A tulip glass, saucer, steam, and a sesame bread ring — all CSS gradients and clip-paths, no images. The sesame seeds are stacked radial-gradients positioned in percentages, so they scale with the ring at every viewport size.
Motion design. The page opens with a choreographed hero: staggered fade-up entrances, a tea-fill (scaleY from the bottom, so the gradient surface rides up), delayed steam, and a self-drawing underline. Scroll-driven parallax on the hero art is a pure progressive enhancement — animation-timeline: scroll() behind @supports, so Firefox and Safari simply see the static composition. Menu-card light sweeps animate background-position instead of a transformed overlay, so nothing leaks outside the rounded corners. And every single one of these — entrances, sweeps, pulses, parallax — is disabled under prefers-reduced-motion, with end-states baked into the base styles so nothing gets stuck invisible.
Little UX touches. Today's opening hours highlighted (with a pulsing "open" dot) via one line of JS and one CSS attribute selector; a chalkboard-style dotted leader between dish and price; the ticker duplicated once so the loop is seamless; scroll reveals via IntersectionObserver that unobserve after firing; the header grows a shadow only once you've scrolled.
Code quality. BEM-ish class names, tokens for every color and radius, one commented section per component, and JSON-LD (CafeOrCoffeeShop) in the head — Demlik may be fictional, but its markup takes itself seriously.
Afiyet olsun! 🫖
Top comments (0)