JSFiddle - React, Tailwind, and code Playground

by Scott Kaye

SCSS

$primary: #ffff00;
$radius: 2px;

.notification-container {
    position: fixed;
    bottom: 10px;
    right: 10px;
    width: 300px;
    height: 200px;
    display: flex;
    flex-direction: column;
    flex-wrap: wrap;
    justify-content: flex-end;
    pointer-events: none;
    overflow: hidden;
}

.notification {
    pointer-events: all;
    box-sizing: border-box;
    width: 100%;
    border-radius: $radius;
    background: linear-gradient(#222, #333);
    box-shadow: 0 5px 5px rgba(0,0,0,0.2), inset 0 0 0 1px transparentize($primary, 0.8);
    color: #eee;
    padding: 8px 12px;
    margin-top: 5px;
    font: 10pt sans-serif;
    display: flex;
    justify-content: space-between;
    align-items: center;
    position: relative;
    overflow: hidden;
    .progress {
        position: absolute;
        top:0;
        left:0;
        width: 100%;
        height: 1px;
        background: linear-gradient(90deg, $primary 75%, transparent);
        animation: timeout 3s ease-out 1 forwards;
    }
    .options {
        margin-left: 10px;
        button {
            border: none;
            background: transparent;
            color: $primary;
            font-weight: 700;
            cursor: pointer;
            outline: none;
            transition: all 100ms ease;
            &:hover {
                text-shadow: 0 0 15px rgba(255, 255, 0, 0.5);
            }
        }
    }
    &.error {
        background: linear-gradient(#a00, #800);
    }
    &.in {
        animation: in 250ms ease 1 forwards;
    }
    &.out {
        animation: in 500ms ease 1 forwards reverse;
    }
}
@keyframes in {
    from {
        transform: translateY(50px);
        opacity: 0;
    }
    to {
        transform: none;
        opacity: 1;
    }
}

@keyframes timeout {
    to { width: 0 }
}

body {
    background: #555;
}

JavaScript

console.clear();
(function (notify, undefined) {
	var defaults = {
		message: null,
		error: false,
		refresh: false,
		options: [],
		timeout: 3000
	},
    container;

	function extendOptions(o1, o2) {
		var n = {
			message: o2.message,
			error: o2.error,
			refresh: o2.refresh,
			options: o2.options,
			timeout: o2.timeout
		};
		for (var prop in o2)
			if (o1.hasOwnProperty(prop)) n[prop] = o1[prop];
		return n;
	}

	function Notif(options) {
		var self = this;

		var el = document.createElement("div");
		el.classList.add("notification");
		if (options.error) el.classList.add("error");
		el.innerText = options.message;

		var progress = document.createElement("div");
		progress.classList.add("progress");
		el.appendChild(progress);
		progress.style.animationDuration = options.timeout + "ms";

		if (options.options) {
			var buttons = document.createElement("div");
			buttons.classList.add("options");
			options.options.forEach(function (c) {
				var btn = document.createElement("button");
				btn.innerText = c.title;
				btn.onclick = c.callback.bind(self);
				buttons.appendChild(btn);
			});
			el.appendChild(buttons);
		}

		this.options = options;
		this.hidden = true;
		this.element = el;
	}

	Notif.prototype.hide = function () {
		container.removeChild(this.element);
		this.hidden = true;
		return this;
	};

	Notif.prototype.show = function () {
		if (this.options.refresh) {
			var to = this.options.refresh;
			delete this.options.refresh;
			delete this.options.options;
			localStorage["nnp"] = JSON.stringify(this.options);
			if (to === true) {
				window.location.reload();
			} else {
				window.location = window.location.origin + window.location.pathname + to;
				window.location.reload();
			}
			return;
		}

		this.element.classList.add("in");
		window.setTimeout(function () {
			this.element.classList.remove("in");
		}.bind(this), 250);
		container.appendChild(this.element);

		if (this.options.timeout > 0) {
			window.setTimeout(function ()...