HTML
CSS
JS
Result
Skip Results Iframe
EDIT ON
Live
Live
Live
This Pen is owned by
web.dev
on
CodePen
.
See more by @web-dot-dev on CodePen
External CSS
https://codepen.io/web-dot-dev/pen/XWqWYgB.css
https://codepen.io/web-dot-dev/pen/ZExZWBQ.css
External JavaScript
https://codepen.io/web-dot-dev/pen/XWqWYgB.js
https://codepen.io/web-dot-dev/pen/ZExZWBQ.js
{"__browser":{"country":"US","device":"unknown_device","mobile":false,"name":"unknown browser","platform":"unknown_platform","version":"0"},"__constants":{},"__CPDATA":{"domain_iframe":"https://cdpn.io","environment":"production","host":"codepen.io","iframe_allow":"accelerometer; ambient-light-sensor; camera; display-capture; encrypted-media; geolocation; gyroscope; microphone; midi; payment; vr; web-share","iframe_sandbox":"allow-forms allow-modals allow-pointer-lock allow-popups allow-same-origin allow-scripts allow-top-navigation-by-user-activation allow-downloads allow-presentation"},"__graphql":{"data":{"data":null,"errors":[{"message":"Cannot return null for non-nullable field Query.sessionUser"}]},"url":"https://codepen.io/graphql","api":"cprails"},"__pay_stripe_public_key":"pk_live_2GndomDfiklqpSNQn8FrGuwZSMIMzha7DkLJqlYe7IR0ihKAlKdiHg68JJc5eVPt68rzAjzAAVXcUwjySHRCsgjQQ00gtRBUFNH","__pay_braintree_env":"production","__boomboom":{"serve_url":"https://cdpn.io/cpe/boomboom","store_url":"https://codepen.io/cpe/boomboom/store"},"__pageType":"embed","__item":"{\"editor_settings\":{\"auto_run\":true,\"autocomplete\":false,\"code_folding\":true,\"css_pre_processor\":\"none\",\"css_prefix\":\"neither\",\"css_starter\":\"neither\",\"emmet_active\":true,\"font_size\":14,\"font_type\":\"system\",\"format_on_save\":true,\"html_pre_processor\":\"none\",\"indent_with\":\"spaces\",\"js_pre_processor\":\"none\",\"key_bindings\":\"normal\",\"line_numbers\":true,\"line_wrapping\":true,\"match_brackets\":true,\"snippets\":{\"markupSnippets\":{},\"stylesheetSnippets\":{}},\"tab_size\":2,\"theme\":\"twilight\",\"id\":\"xxWeQjy\",\"auto_save\":true},\"hashid\":\"xxWeQjy\",\"itemType\":\"pen\",\"resources\":[{\"url\":\"https://codepen.io/web-dot-dev/pen/XWqWYgB.css\",\"order\":0,\"resource_type\":\"css\",\"par_content\":\"\"},{\"url\":\"https://codepen.io/web-dot-dev/pen/XWqWYgB.js\",\"order\":0,\"resource_type\":\"js\",\"par_content\":\"\"},{\"url\":\"https://codepen.io/web-dot-dev/pen/ZExZWBQ.css\",\"order\":1,\"resource_type\":\"css\",\"par_content\":\"\"},{\"url\":\"https://codepen.io/web-dot-dev/pen/ZExZWBQ.js\",\"order\":1,\"resource_type\":\"js\",\"par_content\":\"\"}],\"tags\":[],\"id\":62737974,\"user_id\":5928893,\"html\":\"<!-- Toasts acts like a container here without a backdrop -->\\n<button class=\\\"button ripple\\\">Make Toast 🍞</button>\\n<div popover=\\\"manual\\\" class=\\\"toasts\\\">\\n <ul class=\\\"toasts__drawer\\\"></ul>\\n</div>\",\"css\":\"@layer normalize, open-props, base, mdl, demo;\\n\\n@layer demo {\\n .toasts {\\n /* You can't use these as you need to use top and left...*/\\n /* bottom: 0;\\n right: 0; */\\n left: 100%;\\n top: 100%;\\n transform: translate(-100%, -100%);\\n padding: var(--size-4);\\n border: 0;\\n overflow: auto;\\n margin: 0;\\n max-height: 100vh;\\n background: 0;\\n overflow-x: hidden;\\n }\\n\\n .toasts__slice {\\n list-style-type: none;\\n margin: 0;\\n animation: enter 250ms;\\n transition: transform 0.2s;\\n padding: 0;\\n }\\n\\n .toasts__slice-control {\\n background: var(--surface-1);\\n white-space: nowrap;\\n box-shadow: var(--shadow-3);\\n border: 0;\\n color: var(--gray-9);\\n border-left: var(--size-2) solid var(--border, var(--green-3));\\n border-radius: var(--radius-2);\\n text-align: left;\\n background: var(--background, var(--green-0));\\n display: flex;\\n align-items: center;\\n gap: 1ch;\\n max-width: 75vw;\\n min-width: 285px;\\n position: relative;\\n padding: var(--size-4);\\n outline-color: var(--border);\\n height: auto;\\n }\\n\\n @keyframes enter {\\n 0% {\\n transform: scale(0);\\n }\\n }\\n\\n .toasts__slice:is(:hover, :focus) {\\n transform: translateY(-5%);\\n }\\n\\n .toasts__slice:active {\\n transform: translateY(5%);\\n }\\n\\n .toasts__slice--success {\\n --background: var(--green-0);\\n --border: var(--green-3);\\n }\\n .toasts__slice--destructive {\\n --background: var(--red-0);\\n --border: var(--red-3);\\n }\\n .toasts__slice--warning {\\n --background: var(--yellow-0);\\n --border: var(--yellow-3);\\n }\\n .toasts__slice--info {\\n --background: var(--blue-0);\\n --border: var(--blue-3);\\n }\\n .toasts__slice--timed {\\n --background: var(--indigo-0);\\n --border: var(--indigo-3);\\n }\\n\\n .toasts__slice svg {\\n fill: var(--border);\\n stroke: 0;\\n width: 3ch;\\n min-width: 3ch;\\n aspect-ratio: 1;\\n }\\n\\n .toast__close {\\n width: 1ch;\\n aspect-ratio: 1;\\n min-width: 1ch;\\n }\\n\\n .toast__text {\\n white-space: normal;\\n flex: 1;\\n text-align: center;\\n }\\n\\n .toasts__drawer {\\n display: grid;\\n gap: var(--size-4);\\n padding: 0;\\n }\\n}\\n\\n@layer base {\\n *,\\n *:after,\\n *:before {\\n box-sizing: border-box;\\n }\\n\\n :where([popover]) {\\n margin: auto;\\n border-width: initial;\\n border-style: solid;\\n }\\n\\n body {\\n display: grid;\\n place-items: center;\\n min-height: 100vh;\\n font-family: \\\"Google Sans\\\", sans-serif, system-ui;\\n background: var(--surface-2);\\n }\\n}\\n\",\"js\":\"const TOASTER = document.querySelector(\\\"button\\\");\\n\\nconst TOASTS = document.querySelector(\\\".toasts\\\");\\nconst DRAWER = TOASTS.querySelector(\\\".toasts__drawer\\\");\\n\\nconst removeSlice = (slice, timeoutId) => () => {\\n const ANIMATION = slice.animate(\\n {\\n transform: \\\"translateX(200%) scale(0)\\\"\\n },\\n {\\n duration: 250,\\n easing: \\\"ease-in\\\"\\n }\\n );\\n ANIMATION.finished.then(() => {\\n slice.remove();\\n if (timeoutId) clearTimeout(timeoutId);\\n // I mean, is there a penalty in having the :top-layer always open\\n // if it doesn't obstruct?\\n if (DRAWER.children.length === 0) TOASTS.hidePopover();\\n });\\n};\\n\\nconst TOAST_TYPES = {\\n SUCCESS: \\\"success\\\",\\n INFO: \\\"info\\\",\\n TIMED: \\\"timed\\\",\\n WARNING: \\\"warning\\\",\\n DESTRUCTIVE: \\\"destructive\\\"\\n};\\n\\nconst TOAST_OBJECTS = [\\n {\\n type: TOAST_TYPES.SUCCESS,\\n text: \\\"You made this happen! 🎉\\\",\\n icon: `<svg viewBox=\\\"0 0 512 512\\\" title=\\\"check-double\\\">\\n <path d=\\\"M505 174.8l-39.6-39.6c-9.4-9.4-24.6-9.4-33.9 0L192 374.7 80.6 263.2c-9.4-9.4-24.6-9.4-33.9 0L7 302.9c-9.4 9.4-9.4 24.6 0 34L175 505c9.4 9.4 24.6 9.4 33.9 0l296-296.2c9.4-9.5 9.4-24.7.1-34zm-324.3 106c6.2 6.3 16.4 6.3 22.6 0l208-208.2c6.2-6.3 6.2-16.4 0-22.6L366.1 4.7c-6.2-6.3-16.4-6.3-22.6 0L192 156.2l-55.4-55.5c-6.2-6.3-16.4-6.3-22.6 0L68.7 146c-6.2 6.3-6.2 16.4 0 22.6l112 112.2z\\\" />\\n</svg>`\\n },\\n {\\n type: TOAST_TYPES.INFO,\\n text: () => `It's currently ${new Date().toTimeString().slice(0, 8)} 🕰`,\\n icon: `<svg viewBox=\\\"0 0 192 512\\\" title=\\\"info\\\">\\n <path d=\\\"M20 424.229h20V279.771H20c-11.046 0-20-8.954-20-20V212c0-11.046 8.954-20 20-20h112c11.046 0 20 8.954 20 20v212.229h20c11.046 0 20 8.954 20 20V492c0 11.046-8.954 20-20 20H20c-11.046 0-20-8.954-20-20v-47.771c0-11.046 8.954-20 20-20zM96 0C56.235 0 24 32.235 24 72s32.235 72 72 72 72-32.235 72-72S135.764 0 96 0z\\\" />\\n</svg>`\\n },\\n {\\n type: TOAST_TYPES.WARNING,\\n text: \\\"You haven't finished yet 👀\\\",\\n icon: `<svg viewBox=\\\"0 0 576 512\\\" title=\\\"exclamation-triangle\\\">\\n <path d=\\\"M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z\\\" />\\n</svg>`\\n },\\n {\\n type: TOAST_TYPES.DESTRUCTIVE,\\n text: \\\"Something went wrong 😱\\\",\\n icon: `<svg viewBox=\\\"0 0 512 512\\\" title=\\\"thumbs-down\\\">\\n <path d=\\\"M0 56v240c0 13.255 10.745 24 24 24h80c13.255 0 24-10.745 24-24V56c0-13.255-10.745-24-24-24H24C10.745 32 0 42.745 0 56zm40 200c0-13.255 10.745-24 24-24s24 10.745 24 24-10.745 24-24 24-24-10.745-24-24zm272 256c-20.183 0-29.485-39.293-33.931-57.795-5.206-21.666-10.589-44.07-25.393-58.902-32.469-32.524-49.503-73.967-89.117-113.111a11.98 11.98 0 0 1-3.558-8.521V59.901c0-6.541 5.243-11.878 11.783-11.998 15.831-.29 36.694-9.079 52.651-16.178C256.189 17.598 295.709.017 343.995 0h2.844c42.777 0 93.363.413 113.774 29.737 8.392 12.057 10.446 27.034 6.148 44.632 16.312 17.053 25.063 48.863 16.382 74.757 17.544 23.432 19.143 56.132 9.308 79.469l.11.11c11.893 11.949 19.523 31.259 19.439 49.197-.156 30.352-26.157 58.098-59.553 58.098H350.723C358.03 364.34 384 388.132 384 430.548 384 504 336 512 312 512z\\\" />\\n</svg>`\\n },\\n {\\n type: TOAST_TYPES.TIMED,\\n text: \\\"Timed toast ⏱\\\",\\n icon: `<svg viewBox=\\\"0 0 512 512\\\" title=\\\"history\\\">\\n <path d=\\\"M504 255.531c.253 136.64-111.18 248.372-247.82 248.468-59.015.042-113.223-20.53-155.822-54.911-11.077-8.94-11.905-25.541-1.839-35.607l11.267-11.267c8.609-8.609 22.353-9.551 31.891-1.984C173.062 425.135 212.781 440 256 440c101.705 0 184-82.311 184-184 0-101.705-82.311-184-184-184-48.814 0-93.149 18.969-126.068 49.932l50.754 50.754c10.08 10.08 2.941 27.314-11.313 27.314H24c-8.837 0-16-7.163-16-16V38.627c0-14.254 17.234-21.393 27.314-11.314l49.372 49.372C129.209 34.136 189.552 8 256 8c136.81 0 247.747 110.78 248 247.531zm-180.912 78.784l9.823-12.63c8.138-10.463 6.253-25.542-4.21-33.679L288 256.349V152c0-13.255-10.745-24-24-24h-16c-13.255 0-24 10.745-24 24v135.651l65.409 50.874c10.463 8.137 25.541 6.253 33.679-4.21z\\\" />\\n</svg>`\\n }\\n];\\n\\nconst makeToast = () => {\\n // Create a toast...\\n const { icon, text, type } = TOAST_OBJECTS[\\n Math.floor(Math.random() * TOAST_OBJECTS.length)\\n ];\\n\\n const slice = document.createElement(\\\"li\\\");\\n const sliceControl = document.createElement(\\\"button\\\");\\n sliceControl.className = \\\"toasts__slice-control\\\";\\n sliceControl.title = \\\"Remove notification\\\";\\n slice.appendChild(sliceControl);\\n slice.classList.add(\\\"toasts__slice\\\");\\n slice.classList.add(`toasts__slice--${type}`);\\n sliceControl.innerHTML = icon;\\n sliceControl.innerHTML += `<span class=\\\"toast__text\\\">${\\n typeof text === \\\"function\\\" ? text() : text\\n }</span>`;\\n sliceControl.innerHTML += `<svg class=\\\"toast__close\\\" viewBox=\\\"0 0 352 512\\\" title=\\\"times\\\">\\n <path d=\\\"M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z\\\" />\\n</svg>`;\\n // Add it to the drawer\\n DRAWER.appendChild(slice);\\n TOASTS.scrollTo({ left: 0, top: DRAWER.scrollHeight, behavior: \\\"smooth\\\" });\\n // Show the drawer\\n if (!TOASTS.matches(\\\":open\\\")) TOASTS.showPopover();\\n // Dummy types\\n let timeoutId;\\n if (type === TOAST_TYPES.TIMED) {\\n timeoutId = setTimeout(\\n removeSlice(slice, undefined),\\n Math.random() * 4000 + 1000\\n );\\n }\\n // Event Slicing\\n slice.addEventListener(\\\"click\\\", removeSlice(slice, timeoutId));\\n // Toast will either have a timeout or require user intervention\\n // When a toast closes, check if the drawer should be closed\\n // That is essentially removing the top layer\\n};\\n\\nTOASTER.addEventListener(\\\"click\\\", makeToast);\\n\",\"html_pre_processor\":\"none\",\"css_pre_processor\":\"none\",\"js_pre_processor\":\"none\",\"html_classes\":\"popup-support\",\"css_starter\":\"neither\",\"js_library\":null,\"created_at\":\"2022-08-24T16:58:32.666Z\",\"updated_at\":\"2022-11-04T11:46:16.594Z\",\"title\":\"14. Using Pop-ups as Toast notifications\",\"description\":\"\",\"slug_hash\":\"xxWeQjy\",\"head\":\"\",\"private\":false,\"has_animation\":true,\"team_id\":0,\"css_prefix\":\"neither\",\"template\":false,\"parent_id\":0,\"comments_count\":0,\"custom_screenshot_filename\":null,\"loves_count\":0,\"pick\":false,\"popularity_score\":0,\"views_count\":0,\"pick_visible_at\":null,\"cpid\":\"0182d0ca-915a-76ea-8dab-fb11c47ac8d3\",\"is_new_editor_pen\":false,\"pen_hash\":null}","__processorsMap":{"autoprefixer":"autoprefixer-10","babel":"babel-7","coffeescript":"coffeescript-2","format-1":"format-1","flutter":"flutter-1","haml":"haml-4","less":"less-3","lint-1":"lint-1","livescript":"livescript-1","markdown":"markdown-11","postcss":"postcss-7","pug":"pug-2","sass":"sass-1","scss":"sass-1","sass-ruby-3":"sass-ruby-3","sass-ruby-compass-3":"sass-ruby-compass-3","slim":"slim-3","stylus":"stylus-0","typescript":"typescript-4","vue":"vue-2","vue3":"vue-3"},"__favicon_mask_icon":"https://cpwebassets.codepen.io/assets/favicon/logo-pin-b4b4269c16397ad2f0f7a01bcdf513a1994f4c94b8af2f191c09eb0d601762b1.svg","__favicon_shortcut_icon":"https://cpwebassets.codepen.io/assets/favicon/favicon-aec34940fbc1a6e787974dcd360f2c6b63348d4b1f4e06c77743096d55480f33.ico","__path_to_iframe_console_runner":"https://cpwebassets.codepen.io/assets/editor/iframe/iframeConsoleRunner-6d8bf8b4b479137260842506acbb12717dace0823c023e08b96360e60b0840d9.js","__path_to_iframe_refresh_css":"https://cpwebassets.codepen.io/assets/editor/iframe/iframeRefreshCSS-44fe83e49b63affec96918c9af88c0d80b209a862cf87ac46bc933074b8c557d.js","__path_to_iframe_runtime_errors":"https://cpwebassets.codepen.io/assets/editor/iframe/iframeRuntimeErrors-4f205f2c14e769b448bcf477de2938c681660d5038bc464e3700256713ebe261.js","__path_to_processor_worker":"https://cpwebassets.codepen.io/assets/packs/router.js","__path_to_stop_execution_on_timeout":"https://cpwebassets.codepen.io/assets/common/stopExecutionOnTimeout-2c7831bb44f98c1391d6a4ffda0e1fd302503391ca806e7fcc7b9b87197aec26.js","__pen_normalize_css_url":"https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css","__pen_prefix_free_url":"https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js","__pen_reset_css_url":"https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css","__path_to_infinite_loop_detection":"https://cpwebassets.codepen.io/assets/packs/js/infiniteLoopDetection-f0d5935eb4c2121dfc41.js"}