.toast-host. На одном элементе центрирование через translateX(-50%) и анимация появления спорят за transform, и уведомление уезжает из центра ровно в момент показа. CSS и вёрстка
Всплывающее уведомление четырёх типов: иконка, цветная полоса, полоска обратного отсчёта, шесть позиций на экране и четыре анимации появления.
.toast-host. На одном элементе центрирование через translateX(-50%) и анимация появления спорят за transform, и уведомление уезжает из центра ровно в момент показа. CSS
.toast-host {
position: fixed;
top: 24px;
right: 24px;
z-index: 60;
display: flex;
flex-direction: column;
gap: 10px;
}
.toast {
position: relative;
display: flex;
align-items: flex-start;
gap: 12px;
width: 340px;
max-width: calc(100vw - 32px);
padding: 14px 16px;
border-radius: 12px;
background: #ffffff;
color: #0f172a;
border-left: 4px solid #16a34a;
box-shadow: 0 12px 32px rgba(15, 23, 42, 0.18);
overflow: hidden;
animation: toast-in 320ms cubic-bezier(0.2, 0.9, 0.3, 1) both;
}
.toast__icon {
display: grid;
place-items: center;
flex: none;
width: 24px;
height: 24px;
border-radius: 50%;
background: rgba(22, 163, 74, 0.14);
color: #16a34a;
font-size: 14px;
font-weight: 700;
}
.toast__title {
margin: 0;
font-size: 15px;
font-weight: 600;
}
.toast__text {
margin: 4px 0 0;
font-size: 13px;
line-height: 1.5;
color: #475569;
}
.toast__close {
flex: none;
margin-left: auto;
padding: 2px 6px;
border: none;
background: transparent;
color: #475569;
font-size: 16px;
line-height: 1;
cursor: pointer;
}
.toast__progress {
position: absolute;
left: 0;
bottom: 0;
height: 3px;
width: 100%;
transform-origin: left;
background: #16a34a;
animation: toast-progress 4s linear forwards;
}
@keyframes toast-in {
from { opacity: 0; transform: translateX(24px); }
to { opacity: 1; transform: none; }
}
@keyframes toast-progress {
from { transform: scaleX(1); }
to { transform: scaleX(0); }
}
@media (prefers-reduced-motion: reduce) {
.toast,
.toast__progress { animation: none; }
}HTML
<div class="toast-host">
<div class="toast" role="status" aria-live="polite">
<span class="toast__icon" aria-hidden="true">✓</span>
<div>
<p class="toast__title">Сохранено</p>
<p class="toast__text">Изменения применены и уже видны всем.</p>
</div>
<button class="toast__close" type="button" aria-label="Закрыть уведомление">×</button>
<span class="toast__progress"></span>
</div>
</div>role="status" и aria-live="polite" обязательны: без них уведомления для скринридера просто не существует.