@manufosela/animation-snowcss
CSS-based snow effect web component built with Lit 3
Demo code (CodePen-ready HTML, CSS, JS)
HTML (html)
<animation-snowcss id="snow" num-snowflakes="200" speed="2" color="white"></animation-snowcss>
<h1>ANIMATION SNOW CSS</h1>
<div class="demo-container">
<div class="scene">
<h2>CSS Snow Effect</h2>
<p>Pure CSS animated snowflakes with gentle falling and drift</p>
</div>
<div class="controls">
<div class="control-group">
<label for="numSnowflakes">Snowflakes</label>
<input type="range" id="numSnowflakes" min="20" max="400" value="200" />
<span class="value-display" id="numSnowflakesVal">200</span>
</div>
<div class="control-group">
<label for="speed">Speed</label>
<input type="range" id="speed" min="0.2" max="6" step="0.1" value="2" />
<span class="value-display" id="speedVal">2</span>
</div>
<div class="control-group">
<label for="color">Color</label>
<input type="color" id="color" value="#ffffff" />
</div>
<div class="control-group" style="justify-content: flex-end;">
<div class="btn-group">
<button id="btnStart">Start</button>
<button id="btnStop">Stop</button>
</div>
</div>
</div>
<div class="info-section">
<h3>Usage</h3>
<pre><!-- Install -->
npm install @manufosela/animation-snowcss
<!-- Import -->
<script type="module">
import '@manufosela/animation-snowcss';
</script>
<!-- Use -->
<animation-snowcss
num-snowflakes="200"
speed="2"
color="white"
active
></animation-snowcss></pre>
</div>
<div class="info-section">
<h3>Attributes</h3>
<table>
<thead>
<tr>
<th>Attribute</th>
<th>Type</th>
<th>Default</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>num-snowflakes</code></td>
<td>Number</td>
<td><code>200</code></td>
<td>Number of snowflakes</td>
</tr>
<tr>
<td><code>speed</code></td>
<td>Number</td>
<td><code>2</code></td>
<td>Fall speed multiplier</td>
</tr>
<tr>
<td><code>color</code></td>
<td>String</td>
<td><code>white</code></td>
<td>Snowflake color (any CSS color)</td>
</tr>
<tr>
<td><code>active</code></td>
<td>Boolean</td>
<td><code>true</code></td>
<td>Whether animation is visible</td>
</tr>
</tbody>
</table>
</div>
<div class="info-section">
<h3>Methods</h3>
<table>
<thead>
<tr>
<th>Method</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>start()</code></td>
<td>Show and resume the snow animation</td>
</tr>
<tr>
<td><code>stop()</code></td>
<td>Hide the snow animation with a fade transition</td>
</tr>
</tbody>
</table>
</div>
<a class="back-link" href="https://manufosela.dev/animation-components/">
← animation-components
</a>
</div> CSS (css)
:root {
--bg: #0c0f14;
--bg-elevated: #141923;
--bg-panel: #171d28;
--border: #262f3f;
--text: #f4f6fb;
--text-muted: #a7b0c2;
--text-dim: #7d879b;
--accent: #ff8a3d;
--accent-strong: #ff6a00;
--accent-soft: rgba(255, 138, 61, 0.16);
--shadow: 0 20px 50px rgba(5, 8, 14, 0.45);
--radius-lg: 22px;
--radius-md: 14px;
--radius-sm: 10px;
--max-width: 1160px;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
h1 {
text-align: center;
padding: 2rem;
font-weight: 300;
letter-spacing: 4px;
color: #e8f4ff;
text-shadow: 0 0 20px rgba(220, 240, 255, 0.6);
}
.demo-container {
max-width: 900px;
margin: 0 auto;
padding: 0 1rem 4rem;
position: relative;
z-index: 1;
}
.scene {
border: 1px solid rgba(220, 240, 255, 0.2);
border-radius: 16px;
overflow: hidden;
margin-bottom: 2rem;
padding: 4rem 2rem;
text-align: center;
background: rgba(14, 22, 33, 0.6);
backdrop-filter: blur(2px);
}
.scene h2 {
font-size: 2.5rem;
font-weight: 200;
letter-spacing: 6px;
color: rgba(255, 255, 255, 0.9);
text-shadow: 0 2px 20px rgba(220, 240, 255, 0.4);
margin-bottom: 0.75rem;
}
.scene p {
font-size: 1.1rem;
color: rgba(220, 240, 255, 0.55);
}
.controls {
display: flex;
flex-wrap: wrap;
gap: 1.5rem;
justify-content: center;
padding: 1.5rem;
background: rgba(220, 240, 255, 0.04);
border: 1px solid rgba(220, 240, 255, 0.15);
border-radius: 12px;
margin-bottom: 2rem;
}
.control-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
min-width: 150px;
}
.control-group label {
font-size: 0.875rem;
color: #c8e4ff;
opacity: 0.8;
}
input[type='range'] {
width: 150px;
accent-color: #c8e4ff;
}
input[type='color'] {
width: 50px;
height: 32px;
border: 1px solid rgba(220, 240, 255, 0.3);
border-radius: 6px;
background: transparent;
cursor: pointer;
}
.value-display {
font-size: 0.8rem;
color: rgba(220, 240, 255, 0.45);
}
button {
padding: 0.75rem 1.5rem;
border: 1px solid rgba(220, 240, 255, 0.6);
border-radius: 8px;
background: transparent;
color: #c8e4ff;
font-size: 0.9rem;
letter-spacing: 1px;
cursor: pointer;
transition: background 0.2s, color 0.2s;
}
button:hover {
background: rgba(220, 240, 255, 0.12);
}
.btn-group {
display: flex;
gap: 0.75rem;
align-items: center;
}
.info-section {
background: rgba(220, 240, 255, 0.03);
border: 1px solid rgba(220, 240, 255, 0.12);
border-radius: 12px;
padding: 1.5rem;
margin-bottom: 1.5rem;
}
.info-section h3 {
color: #c8e4ff;
font-weight: 400;
margin-bottom: 1rem;
letter-spacing: 2px;
font-size: 0.95rem;
text-transform: uppercase;
}
pre {
background: rgba(0, 0, 0, 0.4);
border-radius: 8px;
padding: 1rem;
overflow-x: auto;
font-size: 0.85rem;
color: #b8d8f4;
line-height: 1.6;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 0.875rem;
}
th, td {
text-align: left;
padding: 0.6rem 0.8rem;
border-bottom: 1px solid rgba(220, 240, 255, 0.08);
}
th {
color: #c8e4ff;
font-weight: 400;
text-transform: uppercase;
letter-spacing: 1px;
font-size: 0.8rem;
}
td code {
background: rgba(220, 240, 255, 0.1);
padding: 0.1rem 0.4rem;
border-radius: 4px;
font-size: 0.8rem;
color: #b8d8f4;
}
.back-link {
display: block;
text-align: center;
padding: 1rem;
color: rgba(220, 240, 255, 0.4);
text-decoration: none;
font-size: 0.875rem;
letter-spacing: 1px;
}
.back-link:hover {
color: #c8e4ff;
} JS (js)
const snow = document.getElementById('snow');
const numSnowflakesInput = document.getElementById('numSnowflakes');
const speedInput = document.getElementById('speed');
const colorInput = document.getElementById('color');
const numSnowflakesVal = document.getElementById('numSnowflakesVal');
const speedVal = document.getElementById('speedVal');
numSnowflakesInput.addEventListener('input', () => {
numSnowflakesVal.textContent = numSnowflakesInput.value;
snow.setAttribute('num-snowflakes', numSnowflakesInput.value);
});
speedInput.addEventListener('input', () => {
speedVal.textContent = Number(speedInput.value).toFixed(1);
snow.setAttribute('speed', speedInput.value);
});
colorInput.addEventListener('input', () => {
snow.setAttribute('color', colorInput.value);
});
document.getElementById('btnStart').addEventListener('click', () => snow.start());
document.getElementById('btnStop').addEventListener('click', () => snow.stop());