DEV Community

Cover image for Home Cantina: A Modern Glassmorphism Comfort Food Landing Page
Delean Mafra
Delean Mafra

Posted on • Edited on

Home Cantina: A Modern Glassmorphism Comfort Food Landing Page

Frontend Challenge Perfect Landing Submission 🍲🥧

This is a submission for Frontend Challenge - Comfort Food Edition, Perfect Landing

What I Built

For this challenge, I created "Cantina de Casa", a landing page focused on classic home-cooked comfort food dishes. When I started building this page, the idea wasn't to create something overly complex, but rather to leverage concepts that make navigation organized and intuitive, with a clean interface where users can find information without having to hunt around for where to click. All classes and variables were structured using the dm_ prefix to keep the code scope isolated and standardized.

Demo

Live Demo:
https://es-d-64362832320260731-019fb003-ca65-7e4e-8c71-9937a6046590.codepen.dev/

Below I'm sharing the full codebase for anyone who wants to analyze the structure or run it locally.

HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Cantina de Casa - Comfort Food</title>
    <link rel="stylesheet" href="style.css">
</head>
<body class="dm_body">
    <header class="dm_header">
        <div class="dm_header_content">
            <div class="dm_logo">🍲 Cantina de Casa</div>
            <div class="dm_search_container">
                <input type="text" class="dm_search_input" placeholder="Search comfort food...">
            </div>
            <nav class="dm_nav">
                <ul class="dm_nav_list">
                    <li class="dm_nav_item"><a href="#" class="dm_nav_link">Home</a></li>
                    <li class="dm_nav_item"><a href="#" class="dm_nav_link">Menu</a></li>
                </ul>
            </nav>
        </div>
    </header>

    <main class="dm_main_content">
        <section class="dm_hero_section">
            <h1 class="dm_hero_title">Food That Feels Like Home</h1>
            <p class="dm_hero_subtitle">Family recipes prepared with love and fresh ingredients.</p>
        </section>

        <section class="dm_menu_section">
            <h2 class="dm_section_title">Pot Highlights</h2>
            <div class="dm_grid_container" id="dm_products_grid">

                <!-- Card 1 -->
                <article class="dm_card">
                    <div class="dm_card_img_wrapper">
                        <img src="<a href="https://images.unsplash.com/photo-1604908176997-125f25cc6f3d?auto=format&fit=crop&w=800&q=80">https://images.unsplash.com/photo-1604908176997-125f25cc6f3d?auto=format&fit=crop&w=800&q=80</a>" alt="Full Feijoada" class="dm_card_img">
                    </div>
                    <div class="dm_card_info">
                        <h3 class="dm_card_title">Full Feijoada</h3>
                        <p class="dm_card_desc">Rich black bean stew, selected meats, buttered cassava flour, and sautéed collard greens.</p>
                        <div class="dm_card_footer">
                            <span class="dm_card_price">$ 12.90</span>
                            <button class="dm_btn_add">Order</button>
                        </div>
                    </div>
                </article>

                <!-- Card 2 -->
                <article class="dm_card">
                    <div class="dm_card_img_wrapper">
                        <img src="<a href="https://images.unsplash.com/photo-1598514982205-f36b96d1e8d4?auto=format&fit=crop&w=800&q=80">https://images.unsplash.com/photo-1598514982205-f36b96d1e8d4?auto=format&fit=crop&w=800&q=80</a>" alt="Grandma's Pasta" class="dm_card_img">
                    </div>
                    <div class="dm_card_info">
                        <h3 class="dm_card_title">Grandma's Pasta</h3>
                        <p class="dm_card_desc">Fresh pasta with rustic roasted tomato sauce and handmade meatballs.</p>
                        <div class="dm_card_footer">
                            <span class="dm_card_price">$ 10.50</span>
                            <button class="dm_btn_add">Order</button>
                        </div>
                    </div>
                </article>

                <!-- Card 3 -->
                <article class="dm_card">
                    <div class="dm_card_img_wrapper">
                        <img src="<a href="https://images.unsplash.com/photo-1529042410759-befb1204b468?auto=format&fit=crop&w=800&q=80">https://images.unsplash.com/photo-1529042410759-befb1204b468?auto=format&fit=crop&w=800&q=80</a>" alt="Chicken with Okra" class="dm_card_img">
                    </div>
                    <div class="dm_card_info">
                        <h3 class="dm_card_title">Chicken with Okra</h3>
                        <p class="dm_card_desc">A wood-stove regional classic served with creamy corn polenta.</p>
                        <div class="dm_card_footer">
                            <span class="dm_card_price">$ 11.00</span>
                            <button class="dm_btn_add">Order</button>
                        </div>
                    </div>
                </article>

            </div>
        </section>
    </main>

    <script src="script.js"></script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

CSS

:root {
    --dm_color_primary: #d9534f;
    --dm_color_primary_hover: #c9302c;
    --dm_color_text_main: #222222;
    --dm_color_text_muted: #444444;
    --dm_border_radius: 16px;
    --dm_shadow_sm: 0 4px 30px rgba(0, 0, 0, 0.1);
    --dm_shadow_md: 0 8px 32px rgba(0, 0, 0, 0.15);
    --dm_transition_default: all 0.3s ease;

    /* Glassmorphism Variables */
    --dm_glass_bg: rgba(255, 255, 255, 0.65);
    --dm_glass_border: rgba(255, 255, 255, 0.4);
    --dm_glass_blur: blur(12px);
}

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

.dm_body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    /* Adding a fixed background image to make the glass effect visible */
    background: url('[https://images.unsplash.com/photo-1495195134817-a1691359f9e8?auto=format&fit=crop&w=1200&q=80](https://images.unsplash.com/photo-1495195134817-a1691359f9e8?auto=format&fit=crop&w=1200&q=80)') center/cover fixed;
    color: var(--dm_color_text_main);
    line-height: 1.6;
}

/* Header */
.dm_header {
    background: var(--dm_glass_bg);
    backdrop-filter: var(--dm_glass_blur);
    -webkit-backdrop-filter: var(--dm_glass_blur);
    border-bottom: 1px solid var(--dm_glass_border);
    box-shadow: var(--dm_shadow_sm);
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    z-index: 100;
}

.dm_header_content {
    max-width: 1200px;
    margin: 0 auto;
    padding: 1rem 2rem;
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 1rem;
}

.dm_logo {
    font-size: 1.5rem;
    font-weight: bold;
    color: var(--dm_color_primary);
    text-shadow: 1px 1px 2px rgba(255,255,255,0.8);
}

.dm_search_input {
    padding: 0.75rem 1rem;
    border: 1px solid var(--dm_glass_border);
    background: rgba(255, 255, 255, 0.8);
    border-radius: 8px;
    width: 300px;
    outline: none;
    transition: var(--dm_transition_default);
}

.dm_search_input:focus {
    border-color: var(--dm_color_primary);
    box-shadow: 0 0 0 3px rgba(217, 83, 79, 0.2);
}

.dm_nav_list {
    list-style: none;
    display: flex;
    gap: 1.5rem;
}

.dm_nav_link {
    text-decoration: none;
    color: var(--dm_color_text_main);
    font-weight: 600;
    transition: var(--dm_transition_default);
}

.dm_nav_link:hover {
    color: var(--dm_color_primary);
}

.dm_main_content {
    padding-top: 80px; /* offset for fixed header */
}

/* Hero Section */
.dm_hero_section {
    text-align: center;
    padding: 6rem 2rem 4rem;
}

.dm_hero_title {
    font-size: 3rem;
    margin-bottom: 0.5rem;
    color: var(--dm_color_primary);
    text-shadow: 2px 2px 5px rgba(255, 255, 255, 0.9);
}

.dm_hero_subtitle {
    font-size: 1.3rem;
    color: var(--dm_color_text_main);
    font-weight: 600;
    text-shadow: 1px 1px 4px rgba(255, 255, 255, 0.9);
}

/* Menu Grid */
.dm_menu_section {
    max-width: 1200px;
    margin: 0 auto;
    padding: 2rem 2rem 4rem;
}

.dm_section_title {
    font-size: 2rem;
    margin-bottom: 2rem;
    border-left: 4px solid var(--dm_color_primary);
    padding-left: 1rem;
    color: #111;
    text-shadow: 1px 1px 3px rgba(255, 255, 255, 0.8);
}

.dm_grid_container {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: 2.5rem;
}

/* Cards (Glassmorphism applied) */
.dm_card {
    background: var(--dm_glass_bg);
    backdrop-filter: var(--dm_glass_blur);
    -webkit-backdrop-filter: var(--dm_glass_blur);
    border: 1px solid var(--dm_glass_border);
    border-radius: var(--dm_border_radius);
    overflow: hidden;
    box-shadow: var(--dm_shadow_sm);
    transition: var(--dm_transition_default);
    display: flex;
    flex-direction: column;
}

.dm_card:hover {
    transform: translateY(-5px);
    box-shadow: var(--dm_shadow_md);
    background: rgba(255, 255, 255, 0.75);
}

.dm_card_img_wrapper {
    height: 200px;
    overflow: hidden;
}

.dm_card_img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.5s ease;
}

.dm_card:hover .dm_card_img {
    transform: scale(1.05);
}

.dm_card_info {
    padding: 1.5rem;
    display: flex;
    flex-direction: column;
    flex-grow: 1;
}

.dm_card_title {
    font-size: 1.35rem;
    margin-bottom: 0.5rem;
    font-weight: 700;
}

.dm_card_desc {
    color: var(--dm_color_text_muted);
    font-size: 0.95rem;
    margin-bottom: 1.5rem;
    flex-grow: 1;
    font-weight: 500;
}

.dm_card_footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.dm_card_price {
    font-size: 1.25rem;
    font-weight: bold;
    color: var(--dm_color_primary);
}

.dm_btn_add {
    background-color: var(--dm_color_primary);
    color: white;
    border: none;
    padding: 0.6rem 1.4rem;
    border-radius: 8px;
    cursor: pointer;
    font-weight: bold;
    transition: var(--dm_transition_default);
}

.dm_btn_add:hover {
    background-color: var(--dm_color_primary_hover);
}

/* Responsiveness */
@media (max-width: 768px) {
    .dm_header_content {
        flex-direction: column;
        align-items: stretch;
    }

    .dm_search_input {
        width: 100%;
    }

    .dm_nav_list {
        justify-content: center;
    }

    .dm_main_content {
        padding-top: 130px; 
    }
}
Enter fullscreen mode Exit fullscreen mode

JavaScript

// Simple client-side search functionality
document.addEventListener('DOMContentLoaded', () => {
    const dm_searchInput = document.querySelector('.dm_search_input');
    const dm_cards = document.querySelectorAll('.dm_card');

    dm_searchInput.addEventListener('input', (event) => {
        const dm_searchTerm = event.target.value.toLowerCase();

        dm_cards.forEach(dm_card => {
            const dm_title = dm_card.querySelector('.dm_card_title').textContent.toLowerCase();
            const dm_desc = dm_card.querySelector('.dm_card_desc').textContent.toLowerCase();

            if (dm_title.includes(dm_searchTerm) || dm_desc.includes(dm_searchTerm)) {
                dm_card.style.display = 'flex';
            } else {
                dm_card.style.display = 'none';
            }
        });
    });

    // Simulated order button click
    const dm_addButtons = document.querySelectorAll('.dm_btn_add');
    dm_addButtons.forEach(dm_btn => {
        dm_btn.addEventListener('click', () => {
            const dm_productName = dm_btn.closest('.dm_card').querySelector('.dm_card_title').textContent;
            alert(`The dish "${dm_productName}" was added to your order!`);
        });
    });
});
Enter fullscreen mode Exit fullscreen mode

Journey

The first thing I did was separate the structure in HTML and keep all visual styles in CSS. This makes future maintenance much easier. If I ever need to change a color, spacing, or reorganize the food cards later on, I hardly need to touch the base HTML.

For the top bar, I opted for a fixed header. It doesn't need to be a complex layout, but keeping the search bar accessible at all times improves usability directly by preventing users from having to scroll back to the top whenever they want to filter dishes.

Next, I focused on layout organization using CSS Grid. I personally prefer Grid when elements (like product cards) share similar dimensions because it keeps everything aligned. CSS comes into play to define spacing, borders, subtle shadows, hover transitions, and of course, the glassmorphism effect. By setting a fixed background image on the body and using backdrop-filter: blur() combined with translucent backgrounds (rgba), I achieved the frosted glass look that fits the modern aesthetic of the challenge. A simple zoom effect on image hover enhances user experience without cluttering the interface with excessive animations. In my opinion, less is more when building this type of product-driven UI.

Another priority was ensuring full responsiveness. Modern applications must look good on both desktops and mobile screens. Using Media Queries, I restructured layout elements based on screen width, adjusting header positioning and reducing Grid columns automatically for smaller viewports.

I also made sure to use CSS variables (such as --dm_glass_bg and --dm_glass_blur) for key design tokens. If I need to overhaul the visual theme later, I can do so by changing just a few lines at the top of the CSS file. Additionally, I focused on avoiding unnecessary code duplication by building reusable classes.

Ultimately, CSS does far more than decorate. It transforms raw HTML structure into an enjoyable user experience. I prefer investing time upfront to organize variables and Grid structures properly rather than refactoring a messy codebase down the road. JavaScript was kept lightweight and purposeful, focusing on an active client-side search filter and simple event handling.

Top comments (1)

Collapse
 
merbayerp profile image
Mustafa ERBAY

Nice clean layout, and the use of CSS variables and Grid keeps the structure easy to follow.

One thing I noticed is that the implementation feels more like a polished card-based landing page than glassmorphism. Adding translucent surfaces, subtle borders, and backdrop blur would align the visual result more closely with the title.

There are also a couple of small code issues worth checking: the header is sticky rather than fixed, and some image URLs appear to contain rendered anchor or Markdown syntax inside the HTML/CSS. Fixing those would make the shared code run more reliably for readers copying it.