Project Files
Below are the three files for a FrontEnd Challenge — Comfort Food Edition submission: index.html, style.css, and script.js. The page renders a CSS-only string bean casserole illustration with subtle animation and includes three recipe variations. Save each block to the corresponding filename.
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>String Bean Casserole — CSS Art & Recipes</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main class="wrap">
<header class="header">
<h1>String Bean Casserole</h1>
<p class="lead">Comfort food CSS art with three recipe variations</p>
</header>
<section class="stage">
<figure class="art" aria-hidden="true">
<div class="table">
<div class="dish">
<div class="rim"></div>
<div class="filling">
<div class="beans">
<span class="bean b1"></span>
<span class="bean b2"></span>
<span class="bean b3"></span>
<span class="bean b4"></span>
<span class="bean b5"></span>
<span class="bean b6"></span>
<span class="bean b7"></span>
<span class="bean b8"></span>
<span class="bean b9"></span>
<span class="bean b10"></span>
<span class="bean b11"></span>
<span class="bean b12"></span>
</div>
<div class="crumbs"></div>
</div>
<div class="handles left"></div>
<div class="handles right"></div>
</div>
<div class="steam s1"></div>
<div class="steam s2"></div>
<div class="steam s3"></div>
</div>
<figcaption class="caption">Warm, bubbling string bean casserole rendered with CSS</figcaption>
</figure>
<aside class="panel">
<label for="variant" class="label">Choose a recipe</label>
<select id="variant" class="select">
<option value="classic">Classic Green Bean Casserole</option>
<option value="cheesy">Cheesy Bacon Green Bean Casserole</option>
<option value="vegan">Vegan Crispy Onion Casserole</option>
</select>
<div class="summary">
<h2 id="title">Classic Green Bean Casserole</h2>
<p id="desc">A creamy mushroom sauce, tender green beans, and crunchy topping.</p>
<div class="meta">
<span><strong>Prep</strong> 15m</span>
<span><strong>Cook</strong> 30m</span>
<span><strong>Serves</strong> 6</span>
</div>
<div class="actions">
<button id="print" class="btn">Print Recipe</button>
</div>
</div>
</aside>
</section>
<section class="recipes" id="recipes">
<article class="card" id="classic">
<h3>Classic Green Bean Casserole</h3>
<h4>Ingredients</h4>
<ul>
<li>1 lb fresh green beans trimmed</li>
<li>2 tbsp butter</li>
<li>8 oz mushrooms sliced</li>
<li>2 cloves garlic minced</li>
<li>2 tbsp all-purpose flour</li>
<li>1 cup chicken or vegetable broth</li>
<li>1 cup heavy cream</li>
<li>1 tsp soy sauce</li>
<li>1 cup crispy fried onions</li>
<li>Salt and pepper to taste</li>
</ul>
<h4>Directions</h4>
<ol>
<li>Blanch beans 3 minutes, shock in ice water, drain.</li>
<li>Sauté mushrooms in butter until golden, add garlic.</li>
<li>Sprinkle flour, cook 1 minute, whisk in broth and cream until thickened.</li>
<li>Season with soy sauce, salt, and pepper; fold in beans.</li>
<li>Transfer to baking dish, top with fried onions, bake 20 minutes at 375°F until bubbly.</li>
</ol>
</article>
<article class="card" id="cheesy" hidden>
<h3>Cheesy Bacon Green Bean Casserole</h3>
<h4>Ingredients</h4>
<ul>
<li>1 lb green beans trimmed</li>
<li>4 slices bacon cooked and crumbled</li>
<li>1 cup shredded sharp cheddar</li>
<li>8 oz mushrooms</li>
<li>1 cup milk</li>
<li>2 tbsp flour</li>
<li>1/2 cup panko breadcrumbs</li>
<li>2 tbsp butter</li>
<li>Salt and pepper</li>
</ul>
<h4>Directions</h4>
<ol>
<li>Blanch beans and set aside.</li>
<li>Cook mushrooms in butter; add flour and milk to make a sauce.</li>
<li>Stir in cheese until melted; fold in beans and bacon.</li>
<li>Top with panko and extra cheese; bake 18–22 minutes at 375°F.</li>
</ol>
</article>
<article class="card" id="vegan" hidden>
<h3>Vegan Crispy Onion Green Bean Casserole</h3>
<h4>Ingredients</h4>
<ul>
<li>1 lb green beans trimmed</li>
<li>2 tbsp olive oil</li>
<li>8 oz mushrooms</li>
<li>1 small onion finely chopped</li>
<li>2 tbsp flour or gluten-free flour</li>
<li>1 cup vegetable broth</li>
<li>1 cup unsweetened oat milk</li>
<li>1 tbsp nutritional yeast</li>
<li>1 cup vegan crispy fried onions</li>
<li>Salt and pepper</li>
</ul>
<h4>Directions</h4>
<ol>
<li>Blanch beans and cool.</li>
<li>Sauté onion and mushrooms in oil; add flour and cook briefly.</li>
<li>Whisk in broth and oat milk; add nutritional yeast and simmer until thick.</li>
<li>Combine with beans, top with vegan fried onions, bake 20 minutes at 375°F.</li>
</ol>
</article>
</section>
<footer class="footer">
<p>Submission for Frontend Challenge — Comfort Food Edition. CSS is the star.</p>
</footer>
</main>
<script src="script.js"></script>
</body>
</html>
style.css
:root{
--bg:#f7f4ef;
--card:#ffffff;
--accent:#6aa84f;
--dish-top:#f6b48a;
--dish-body:#e86a3a;
--bean:#2f8b3b;
--crumb:#d9b56a;
--muted:#6b6b6b;
--shadow: rgba(0,0,0,0.12);
--text:#2b2b2b;
}
*{box-sizing:border-box}
html,body{height:100%}
body{
margin:0;
font-family:Inter, system-ui, -apple-system, "Segoe UI", Roboto, Arial;
background: linear-gradient(180deg,var(--bg),#efeae2);
color:var(--text);
padding:28px;
display:flex;
align-items:center;
justify-content:center;
}
/* Layout */
.wrap{
width:100%;
max-width:1100px;
background:linear-gradient(180deg,var(--card),#fffaf6);
border-radius:14px;
box-shadow: 0 12px 30px var(--shadow);
padding:24px;
display:grid;
grid-template-rows:auto auto 1fr auto;
gap:18px;
}
.header h1{margin:0;font-size:28px}
.lead{margin:6px 0 0;color:var(--muted)}
/* Stage */
.stage{
display:grid;
grid-template-columns: 1fr 320px;
gap:20px;
align-items:start;
}
/* Art */
.art{display:flex;flex-direction:column;align-items:center;gap:12px;padding:12px}
.table{
width:460px;
height:300px;
position:relative;
display:flex;
align-items:center;
justify-content:center;
transform:translateY(6px);
}
/* table surface */
.table::before{
content:"";
position:absolute;
width:100%;
height:100%;
border-radius:18px;
background: radial-gradient(circle at 30% 20%, #fff 0%, #f6f2ec 20%, #efe6db 100%);
box-shadow: inset 0 -12px 30px rgba(0,0,0,0.03);
}
/* dish */
.dish{
width:360px;
height:180px;
border-radius:22px;
position:relative;
display:flex;
align-items:center;
justify-content:center;
box-shadow: 0 10px 24px rgba(0,0,0,0.12);
transform:translateY(-6px);
background: linear-gradient(180deg,var(--dish-top),var(--dish-body));
overflow:visible;
}
/* rim */
.rim{
position:absolute;
top:-18px;
left:18px;
right:18px;
height:36px;
border-radius:18px;
background: linear-gradient(180deg,#ffd9c0,#ffb88a);
box-shadow: inset 0 -6px 12px rgba(255,255,255,0.12);
}
/* handles */
.handles{
position:absolute;
top:56%;
width:48px;
height:18px;
background:linear-gradient(180deg,#f3c6a0,#e89a6a);
border-radius:10px;
transform:translateY(-50%);
box-shadow: 0 6px 12px rgba(0,0,0,0.08);
}
.handles.left{left:-28px}
.handles.right{right:-28px}
/* filling */
.filling{
width:86%;
height:68%;
position:relative;
top:6px;
border-radius:10px;
background: linear-gradient(180deg,#f7f0e6,#f3e6d8);
box-shadow: inset 0 -6px 12px rgba(0,0,0,0.03);
overflow:visible;
}
/* beans */
.beans{position:absolute;left:6%;right:6%;top:8%;height:72%}
.bean{
position:absolute;
width:44px;
height:14px;
border-radius:14px;
background: linear-gradient(90deg,#2f7f2f,#3fbf3f);
box-shadow: 0 3px 8px rgba(0,0,0,0.12), inset 0 -4px 8px rgba(255,255,255,0.06);
transform-origin:center;
filter:drop-shadow(0 2px 2px rgba(0,0,0,0.06));
}
/* natural pile positions */
.b1{left:4%; top:12%; transform:rotate(-12deg) scaleX(1.05)}
.b2{left:18%; top:8%; transform:rotate(6deg)}
.b3{left:34%; top:14%; transform:rotate(-6deg)}
.b4{left:50%; top:10%; transform:rotate(10deg)}
.b5{left:66%; top:14%; transform:rotate(-8deg)}
.b6{left:10%; top:34%; transform:rotate(8deg)}
.b7{left:26%; top:30%; transform:rotate(-10deg)}
.b8{left:42%; top:34%; transform:rotate(4deg)}
.b9{left:58%; top:32%; transform:rotate(-4deg)}
.b10{left:74%; top:34%; transform:rotate(12deg)}
.b11{left:30%; top:52%; transform:rotate(-6deg) scaleX(0.95)}
.b12{left:56%; top:50%; transform:rotate(6deg) scaleX(1.02)}
/* bean highlight */
.bean::after{
content:"";
position:absolute;
left:6px;
top:2px;
width:14px;
height:6px;
border-radius:6px;
background: linear-gradient(90deg, rgba(255,255,255,0.32), rgba(255,255,255,0.06));
transform:skewX(-12deg);
}
/* crumbs */
.crumbs{
position:absolute;
bottom:8%;
left:6%;
right:6%;
height:22%;
border-radius:8px;
background:
radial-gradient(circle at 12% 30%, #e6c98a 6%, transparent 7%),
radial-gradient(circle at 28% 60%, #d9b56a 6%, transparent 7%),
radial-gradient(circle at 62% 40%, #e6c98a 6%, transparent 7%),
radial-gradient(circle at 82% 70%, #d9b56a 6%, transparent 7%);
mix-blend-mode:multiply;
transform:translateY(6px);
box-shadow: inset 0 -6px 10px rgba(0,0,0,0.02);
}
/* steam */
.steam{
position:absolute;
width:22px;
height:80px;
bottom:calc(50% + 80px);
background: linear-gradient(180deg, rgba(255,255,255,0.95), rgba(255,255,255,0.05));
border-radius:50%;
filter: blur(6px);
opacity:0;
transform-origin:bottom center;
animation:steam 4s infinite ease-in-out;
}
.s1{left:34%; animation-delay:0s}
.s2{left:48%; animation-delay:0.6s}
.s3{left:62%; animation-delay:1.2s}
@keyframes steam{
0%{transform:translateY(0) scaleX(0.6) rotate(-6deg); opacity:0}
10%{opacity:0.6}
50%{transform:translateY(-70px) scaleX(1.2) rotate(6deg); opacity:0.9}
90%{opacity:0.2}
100%{transform:translateY(-140px) scaleX(1.6) rotate(12deg); opacity:0}
}
/* caption */
.caption{font-size:13px;color:var(--muted);margin-top:8px}
/* Panel */
.panel{
background:linear-gradient(180deg,#fff,#fbfbfb);
border-radius:10px;
padding:14px;
box-shadow: 0 8px 18px rgba(0,0,0,0.04);
display:flex;
flex-direction:column;
gap:12px;
}
.label{color:var(--muted);font-size:13px}
.select{
width:100%;
padding:10px 12px;
border-radius:8px;
border:1px solid #e6e6e6;
background:white;
font-size:15px;
}
.summary h2{margin:6px 0 0}
.summary p{margin:6px 0;color:var(--muted)}
.meta{display:flex;gap:10px;margin-top:10px;color:var(--muted);font-size:13px}
.btn{
padding:10px 12px;border-radius:8px;border:0;background:var(--accent);color:white;cursor:pointer;font-weight:600;
box-shadow: 0 8px 16px rgba(106,168,79,0.18);
}
/* Recipes grid */
.recipes{
display:grid;
grid-template-columns: repeat(3,1fr);
gap:14px;
margin-top:6px;
}
.card{
background:linear-gradient(180deg,#fff,#fffaf2);
border-radius:10px;
padding:14px;
box-shadow: 0 8px 18px rgba(0,0,0,0.04);
min-height:220px;
}
.card h3{margin-top:0}
.card ul{margin:8px 0 12px 18px}
.card ol{margin:8px 0 12px 18px}
/* Footer */
.footer{color:var(--muted);font-size:13px;text-align:center;padding:8px}
/* Responsive */
@media (max-width:980px){
.stage{grid-template-columns:1fr}
.recipes{grid-template-columns:1fr}
.table{width:360px;height:240px}
}
script.js
// Lightweight JS: toggle recipe cards, animate casserole emphasis, and print visible recipe
document.addEventListener('DOMContentLoaded', () => {
const select = document.getElementById('variant');
const title = document.getElementById('title');
const desc = document.getElementById('desc');
const printBtn = document.getElementById('print');
const casserole = document.querySelector('.dish');
const variants = {
classic: {
title: 'Classic Green Bean Casserole',
desc: 'A creamy mushroom sauce, tender green beans, and crunchy topping.',
id: 'classic'
},
cheesy: {
title: 'Cheesy Bacon Green Bean Casserole',
desc: 'Savory bacon and melted cheddar add extra comfort and richness.',
id: 'cheesy'
},
vegan: {
title: 'Vegan Crispy Onion Casserole',
desc: 'Plant-based version with oat milk and vegan fried onions for crunch.',
id: 'vegan'
}
};
function showVariant(key){
const v = variants[key];
title.textContent = v.title;
desc.textContent = v.desc;
// show only the selected recipe card
Object.values(variants).forEach(item => {
const el = document.getElementById(item.id);
if(!el) return;
el.hidden = item.id !== v.id;
});
// subtle emphasis animation on the dish
casserole.animate([
{ transform: 'translateY(-6px) scale(1)', boxShadow: '0 10px 24px rgba(0,0,0,0.12)' },
{ transform: 'translateY(-10px) scale(1.02)', boxShadow: '0 18px 36px rgba(0,0,0,0.18)' },
{ transform: 'translateY(-6px) scale(1)', boxShadow: '0 10px 24px rgba(0,0,0,0.12)' }
], { duration: 700, easing: 'ease-in-out' });
// small visual tweak: change crumb color for cheesy variant
const crumbs = document.querySelector('.crumbs');
if(key === 'cheesy') crumbs.style.background = 'radial-gradient(circle at 12% 30%, #f2d08a 6%, transparent 7%), radial-gradient(circle at 28% 60%, #e6b56a 6%, transparent 7%), radial-gradient(circle at 62% 40%, #f2d08a 6%, transparent 7%), radial-gradient(circle at 82% 70%, #e6b56a 6%, transparent 7%)';
else if(key === 'vegan') crumbs.style.background = 'radial-gradient(circle at 12% 30%, #d9c98a 6%, transparent 7%), radial-gradient(circle at 28% 60%, #cfae6a 6%, transparent 7%), radial-gradient(circle at 62% 40%, #d9c98a 6%, transparent 7%), radial-gradient(circle at 82% 70%, #cfae6a 6%, transparent 7%)';
else crumbs.style.background = '';
}
select.addEventListener('change', (e) => showVariant(e.target.value));
printBtn.addEventListener('click', () => {
const visible = document.querySelector('.card:not([hidden])');
if(!visible) return window.print();
const printWindow = window.open('', '_blank', 'width=700,height=900');
const styles = Array.from(document.querySelectorAll('link[rel="stylesheet"], style'))
.map(n => n.outerHTML).join('\n');
printWindow.document.write(`
<html><head><title>${visible.querySelector('h3').textContent}</title>
${styles}
<style>body{padding:24px;font-family:Inter,Arial;} .card{box-shadow:none;border-radius:0}</style>
</head><body>${visible.outerHTML}</body></html>
`);
printWindow.document.close();
printWindow.focus();
setTimeout(()=>printWindow.print(), 400);
});
// initialize
showVariant(select.value);
});
Submission Template for DevChallenge and Frontend Challenge
Title
String Bean Casserole — CSS Art and Recipes
Inspiration
Comfort food from family dinners inspired this piece. The goal was to capture the warm, nostalgic feeling of a bubbling green bean casserole using only HTML and CSS while offering three recipe variations that cover classic, cheesy, and vegan approaches.
Demo
Live demo instructions
- Save the three files as
index.html,style.css, andscript.jsin the same folder. - Open
index.htmlin a browser to view the CSS art and switch between recipes. Preview - The page shows a CSS-rendered casserole dish with steam animation and a recipe panel to toggle between variations.
Journey
Process and learnings
- Focused on building textures and depth with gradients, radial backgrounds, and layered pseudo-elements.
- Used CSS transforms and keyframe animation to create gentle steam and a subtle dish emphasis when switching recipes.
- Kept JavaScript minimal to respect the challenge rules: it only toggles recipe visibility, updates summary text, and handles printing. What I’m proud of
- The casserole is built entirely from simple HTML elements and CSS styling without images.
- The design balances aesthetics and accessibility by keeping recipe content in the DOM and marking the art
aria-hidden.
Code Notes
How CSS is the star
- All visual elements are CSS shapes, gradients, and pseudo-elements.
- Colors and textures are controlled via
:rootvariables for easy palette changes. Extensibility - Add more beans by duplicating
.beanelements and positioning them. - Swap color variables to create seasonal variations.
Credits and License
Author
- Your name or handle here License
- Suggested: MIT License
Files included
- index.html — markup and structure
- style.css — full CSS art and layout
- script.js — lightweight interactivity and print helper
Use this submission template to post on DEV, the Frontend Challenge page, or any CSS Art prompt. The CSS remains the primary focus while JavaScript is intentionally light and purposeful.
Top comments (1)
A warm, nostalgic casserole with creamy mushroom sauce and crunchy topping.🥗