diff --git a/frontend/src/app/main/ui/ds.cljs b/frontend/src/app/main/ui/ds.cljs
index 85268cf8d..c9848fb71 100644
--- a/frontend/src/app/main/ui/ds.cljs
+++ b/frontend/src/app/main/ui/ds.cljs
@@ -14,6 +14,7 @@
[app.main.ui.ds.foundations.typography :refer [typography-list]]
[app.main.ui.ds.foundations.typography.heading :refer [heading*]]
[app.main.ui.ds.foundations.typography.text :refer [text*]]
+ [app.main.ui.ds.notifications.toast :refer [toast*]]
[app.main.ui.ds.product.loader :refer [loader*]]
[app.main.ui.ds.storybook :as sb]))
@@ -27,6 +28,7 @@
:Loader loader*
:RawSvg raw-svg*
:Text text*
+ :Toast toast*
;; meta / misc
:meta #js {:icons (clj->js (sort icon-list))
:svgs (clj->js (sort raw-svg-list))
diff --git a/frontend/src/app/main/ui/ds/_borders.scss b/frontend/src/app/main/ui/ds/_borders.scss
index a424603d1..e8a856074 100644
--- a/frontend/src/app/main/ui/ds/_borders.scss
+++ b/frontend/src/app/main/ui/ds/_borders.scss
@@ -8,5 +8,6 @@
// TODO: create actual tokens once we have them from design
$br-8: px2rem(8);
+$br-circle: 50%;
$b-1: px2rem(1);
diff --git a/frontend/src/app/main/ui/ds/_sizes.scss b/frontend/src/app/main/ui/ds/_sizes.scss
index f27838b6a..5752bc10c 100644
--- a/frontend/src/app/main/ui/ds/_sizes.scss
+++ b/frontend/src/app/main/ui/ds/_sizes.scss
@@ -7,4 +7,6 @@
@use "./utils.scss" as *;
// TODO: create actual tokens once we have them from design
+$sz-16: px2rem(16);
$sz-32: px2rem(32);
+$sz-224: px2rem(224);
diff --git a/frontend/src/app/main/ui/ds/buttons/buttons.mdx b/frontend/src/app/main/ui/ds/buttons/buttons.mdx
index 3bc00dc93..ebbfa61a7 100644
--- a/frontend/src/app/main/ui/ds/buttons/buttons.mdx
+++ b/frontend/src/app/main/ui/ds/buttons/buttons.mdx
@@ -2,7 +2,7 @@ import { Canvas, Meta } from '@storybook/blocks';
import * as ButtonStories from "./button.stories";
import * as IconButtonStories from "./icon_button.stories";
-
+
# Buttons
diff --git a/frontend/src/app/main/ui/ds/notifications/notifications.mdx b/frontend/src/app/main/ui/ds/notifications/notifications.mdx
new file mode 100644
index 000000000..6ea282a3e
--- /dev/null
+++ b/frontend/src/app/main/ui/ds/notifications/notifications.mdx
@@ -0,0 +1,43 @@
+import { Canvas, Meta } from '@storybook/blocks';
+import * as ToastStories from "./toast.stories";
+
+
+
+# Notifications
+
+`toast*` and `context-notification` accept a `level` prop to indicate the type of message. If this prop is `nil`,
+the default variant will be shown:
+
+
+
+Info:
+
+
+
+Warning:
+
+
+
+Error:
+
+
+
+Success:
+
+
+
+## Toasts
+
+Toast notifications are temporary floating elements that provide information
+that requires user acknowledgement.
+
+### Usage guidelines (design)
+
+#### Where to use
+
+Displayed in the top right corner of the page, in absolute position.
+
+#### When to use
+
+When the notification is temporary and is related or affects the whole context
+of the page the user is viewing or interacting with.
diff --git a/frontend/src/app/main/ui/ds/notifications/toast.cljs b/frontend/src/app/main/ui/ds/notifications/toast.cljs
new file mode 100644
index 000000000..343ec6fc5
--- /dev/null
+++ b/frontend/src/app/main/ui/ds/notifications/toast.cljs
@@ -0,0 +1,41 @@
+;; This Source Code Form is subject to the terms of the Mozilla Public
+;; License, v. 2.0. If a copy of the MPL was not distributed with this
+;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
+;;
+;; Copyright (c) KALEIDOS INC
+
+(ns app.main.ui.ds.notifications.toast
+ (:require-macros
+ [app.common.data.macros :as dm]
+ [app.main.style :as stl])
+ (:require
+ [app.main.ui.ds.foundations.assets.icon :as i]
+ [rumext.v2 :as mf]))
+
+(def levels (set '("info" "warning" "error" "success")))
+
+(def ^:private icons-by-level
+ {"info" i/help
+ "warning" i/msg-neutral
+ "error" i/delete-text
+ "success" i/status-tick})
+
+(mf/defc toast*
+ {::mf/props :obj}
+ [{:keys [class level children on-close] :rest props}]
+ (assert (or (nil? level) (contains? levels level)) "expected valid level or nil")
+ (assert (or (nil? on-close) (fn? on-close)))
+ (let [class (dm/str (stl/css-case :toast true
+ :toast-info (= level "info")
+ :toast-warning (= level "warning")
+ :toast-error (= level "error")
+ :toast-success (= level "success")) " " class)
+ icon-id (or (get icons-by-level level) i/msg-neutral)
+ props (mf/spread-props props {:class class})]
+ [:> "aside" props
+ [:*
+ [:> i/icon* {:id icon-id :class (stl/css :icon)}]
+ children
+ ;; TODO: this should be a buttom from the DS, but this variant is not designed yet.
+ ;; https://tree.taiga.io/project/penpot/task/8492
+ [:> "button" {:on-click on-close :aria-label "Close" :class (stl/css :close-button)} [:> i/icon* {:id i/close}]]]]))
\ No newline at end of file
diff --git a/frontend/src/app/main/ui/ds/notifications/toast.scss b/frontend/src/app/main/ui/ds/notifications/toast.scss
new file mode 100644
index 000000000..8aec8b198
--- /dev/null
+++ b/frontend/src/app/main/ui/ds/notifications/toast.scss
@@ -0,0 +1,75 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+//
+// Copyright (c) KALEIDOS INC
+
+@use "../_sizes.scss" as *;
+@use "../_borders.scss" as *;
+@use "../typography.scss" as *;
+
+.toast {
+ @include use-typography("body-small");
+
+ --toast-bg-color: var(--color-background-primary);
+ --toast-fg-color: var(--color-foreground-primary);
+ --toast-border-color: var(--color-background-quaternary);
+ --toast-icon-color: var(--color-foreground-secondary);
+
+ min-width: $sz-224;
+ height: $sz-32;
+ background-color: var(--toast-bg-color);
+ border: $b-1 solid var(--toast-border-color);
+ border-radius: $br-8;
+ padding: 0 var(--sp-s);
+
+ display: inline-grid;
+ grid-template-columns: auto 1fr auto;
+ column-gap: var(--sp-s);
+ align-items: center;
+
+ color: var(--toast-fg-color);
+}
+
+.toast-info {
+ --toast-bg-color: var(--color-background-info);
+ --toast-fg-color: var(--color-foreground-primary);
+ --toast-border-color: var(--color-accent-info);
+ --toast-icon-color: var(--color-accent-info);
+}
+
+.toast-error {
+ --toast-bg-color: var(--color-background-error);
+ --toast-fg-color: var(--color-foreground-primary);
+ --toast-border-color: var(--color-accent-error);
+ --toast-icon-color: var(--color-accent-error);
+}
+
+.toast-warning {
+ --toast-bg-color: var(--color-background-warning);
+ --toast-fg-color: var(--color-foreground-primary);
+ --toast-border-color: var(--color-accent-warning);
+ --toast-icon-color: var(--color-accent-warning);
+}
+
+.toast-success {
+ --toast-bg-color: var(--color-background-success);
+ --toast-fg-color: var(--color-foreground-primary);
+ --toast-border-color: var(--color-accent-success);
+ --toast-icon-color: var(--color-accent-success);
+}
+
+.icon {
+ color: var(--toast-icon-color);
+}
+
+.close-button {
+ appearance: none;
+ width: $sz-16;
+ height: $sz-16;
+ display: inline-grid;
+ place-content: center;
+ border: none;
+ background: var(--toast-bg-color);
+ color: var(--toast-icon-color);
+}
diff --git a/frontend/src/app/main/ui/ds/notifications/toast.stories.jsx b/frontend/src/app/main/ui/ds/notifications/toast.stories.jsx
new file mode 100644
index 000000000..34e835b9a
--- /dev/null
+++ b/frontend/src/app/main/ui/ds/notifications/toast.stories.jsx
@@ -0,0 +1,78 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+//
+// Copyright (c) KALEIDOS INC
+
+import * as React from "react";
+import Components from "@target/components";
+
+const { Toast } = Components;
+const { icons } = Components.meta;
+
+export default {
+ title: "Notifications/Toast",
+ component: Toast,
+ argTypes: {
+ children: {
+ control: { type: "text" },
+ },
+ },
+ args: {
+ children: "Lorem ipsum",
+ onClose: () => {
+ alert("Close callback");
+ },
+ },
+ parameters: {
+ controls: {
+ exclude: ["onClose"],
+ },
+ },
+ render: ({ ...args }) => ,
+};
+
+export const Default = {};
+
+export const WithLongerText = {
+ args: {
+ children:
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent lorem ante, bibendum sed ex.",
+ },
+};
+
+export const Info = {
+ args: {
+ level: "info",
+ },
+ parameters: {
+ controls: { exclude: ["level", "onClose"] },
+ },
+};
+
+export const Error = {
+ args: {
+ level: "error",
+ },
+ parameters: {
+ controls: { exclude: ["level", "onClose"] },
+ },
+};
+
+export const Warning = {
+ args: {
+ level: "warning",
+ },
+ parameters: {
+ controls: { exclude: ["level", "onClose"] },
+ },
+};
+
+export const Success = {
+ args: {
+ level: "success",
+ },
+ parameters: {
+ controls: { exclude: ["level", "onClose"] },
+ },
+};