Skip to content

Commit 4050f46

Browse files
Merge pull request #878 from AjayBandiwaddar/feat/ease-dropdown
feat: `ease-dropdown` — Animated CSS-only dropdown menu component
2 parents 15c68db + 2602eb9 commit 4050f46

3 files changed

Lines changed: 498 additions & 394 deletions

File tree

Lines changed: 70 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,89 @@
11
# Toast Notification Component
22

3-
### What does this do?
4-
This submission adds a stackable, auto-dismissing toast notification component (`.ease-toast`) that slides in from the screen edge and collapses vertically upon exit.
3+
## 1. What does this do?
54

6-
---
5+
Provides a composable toast notification system with four semantic color variants (success, danger, warning, info), a fixed stack container with four placement modifiers, an animated auto-dismiss progress bar, and a clean JS dismiss helper — delegating all entrance and exit animation entirely to existing EaseMotion animation classes.
76

8-
### How is it used?
7+
## 2. How is it used?
98

10-
Place `.ease-toast` elements inside a fixed `.ease-toast-container` wrapper (usually positioned top-right or bottom-right):
9+
**HTML structure**
1110

1211
```html
13-
<div class="ease-toast-container">
14-
<!-- Success Toast -->
15-
<div class="ease-toast ease-toast-success">
16-
<span>Database sync complete!</span>
12+
<!-- Stack container — place once near </body> -->
13+
<div class="toast-stack">
14+
15+
<!-- Individual toast — add any EaseMotion entrance class -->
16+
<div class="toast toast-success ease-slide-in-right">
17+
<span class="toast-icon">✓</span>
18+
<span class="toast-message">Changes saved successfully.</span>
19+
<button class="toast-close" onclick="dismissToast(this.closest('.toast'))">✕</button>
20+
</div>
21+
22+
<div class="toast toast-danger ease-slide-in-right">
23+
<span class="toast-icon">✕</span>
24+
<span class="toast-message">Something went wrong.</span>
25+
<button class="toast-close" onclick="dismissToast(this.closest('.toast'))">✕</button>
1726
</div>
27+
28+
</div>
29+
```
30+
31+
**Optional stack placement modifiers**
32+
33+
```html
34+
<div class="toast-stack toast-stack--top-right">…</div>
35+
<div class="toast-stack toast-stack--top-left">…</div>
36+
<div class="toast-stack toast-stack--bottom-left">…</div>
37+
<!-- default is bottom-right, no modifier needed -->
38+
```
39+
40+
**Optional two-line toast (title + body)**
41+
42+
```html
43+
<div class="toast toast-warning ease-slide-in-right">
44+
<span class="toast-icon">⚠</span>
45+
<span class="toast-message">
46+
<span class="toast-title">Session expiring</span>
47+
<span class="toast-body">You will be logged out in 5 minutes.</span>
48+
</span>
49+
<button class="toast-close">✕</button>
1850
</div>
1951
```
2052

21-
#### Theme Modifiers
22-
- **Success Accent**: `.ease-toast-success`
23-
- **Danger Accent**: `.ease-toast-danger`
24-
- **Warning Accent**: `.ease-toast-warning`
25-
- **Info Accent**: `.ease-toast-info`
53+
**Minimal JS helper (copy-paste ready)**
2654

27-
#### Custom CSS Variables Configuration
28-
You can customize the toast appearance dynamically:
55+
```js
56+
function dismissToast(toast) {
57+
if (!toast || toast.classList.contains('toast--dismissing')) return;
58+
toast.classList.add('toast--dismissing');
59+
setTimeout(() => toast.remove(), 350);
60+
}
2961

30-
```css
31-
.my-custom-toast {
32-
--toast-bg: #090c14;
33-
--toast-color: #f8fafc;
34-
--toast-accent: #a855f7; /* Custom purple accent border */
62+
function fireToast(variant, message, duration = 4000) {
63+
const icons = { success: '', danger: '', warning: '', info: '' };
64+
const stack = document.querySelector('.toast-stack');
65+
const el = document.createElement('div');
66+
el.className = `toast toast-${variant} ease-slide-in-right`;
67+
el.style.setProperty('--toast-duration', `${duration}ms`);
68+
el.setAttribute('role', 'alert');
69+
el.innerHTML = `
70+
<span class="toast-icon">${icons[variant]}</span>
71+
<span class="toast-message">${message}</span>
72+
<button class="toast-close" onclick="dismissToast(this.closest('.toast'))">✕</button>
73+
`;
74+
stack.appendChild(el);
75+
setTimeout(() => dismissToast(el), duration);
3576
}
3677
```
3778

38-
---
79+
## 3. Why is it useful?
80+
81+
Toasts are one of the most animation-heavy UI patterns in any product — every project needs them and every project currently builds them from scratch.
82+
83+
This submission fits EaseMotion's philosophy in two specific ways:
3984

40-
### Why does it fit EaseMotion CSS?
85+
**Zero new keyframes.** Entrance animation is a single composable class (`ease-slide-in-right`, `ease-fade-in`, `ease-slide-up` — developer's choice). Exit animation is handled by the `.toast--dismissing` state, which reverses the same keyframes already in `animations.css`. The component itself is pure structure and color.
4186

42-
A dynamic toast notification is a key UI pattern missing from the components library.
87+
**Token-aware by default.** Every color value references a semantic alias (`--ease-color-surface`, `--ease-color-success`, `--ease-color-muted`, etc.), so toasts inherit the dark-mode overrides from `variables.css` automatically — no extra dark-mode CSS required.
4388

44-
This implementation provides both the entry slide-in transition and the auto-dismiss exit collapse sequence in **pure CSS** (using a delayed exit animation with `animation-fill-mode: forwards`). It requires zero JavaScript runtime for animation logic, supporting EaseMotion's philosophy of highly performant, lightweight, and human-readable styling.
89+
The auto-dismiss progress bar uses a single `scaleX` keyframe driven by a CSS custom property (`--toast-duration`), making the dismiss duration controllable per-toast without JavaScript.

0 commit comments

Comments
 (0)