diff --git a/core/embed/rust/src/ui/model_t1/component/title.rs b/core/embed/rust/src/ui/model_t1/component/frame.rs
similarity index 88%
rename from core/embed/rust/src/ui/model_t1/component/title.rs
rename to core/embed/rust/src/ui/model_t1/component/frame.rs
index 83fe0cbbab..0635af4349 100644
--- a/core/embed/rust/src/ui/model_t1/component/title.rs
+++ b/core/embed/rust/src/ui/model_t1/component/frame.rs
@@ -5,13 +5,13 @@ use crate::ui::{
     geometry::{Offset, Rect},
 };
 
-pub struct Title<T, U> {
+pub struct Frame<T, U> {
     area: Rect,
     title: U,
     content: Child<T>,
 }
 
-impl<T: Component, U: AsRef<[u8]>> Title<T, U> {
+impl<T: Component, U: AsRef<[u8]>> Frame<T, U> {
     pub fn new(area: Rect, title: U, content: impl FnOnce(Rect) -> T) -> Self {
         let (title_area, content_area) = Self::areas(area);
         Self {
@@ -32,7 +32,7 @@ impl<T: Component, U: AsRef<[u8]>> Title<T, U> {
     }
 }
 
-impl<T: Component, U: AsRef<[u8]>> Component for Title<T, U> {
+impl<T: Component, U: AsRef<[u8]>> Component for Frame<T, U> {
     type Msg = T::Msg;
 
     fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
@@ -53,13 +53,13 @@ impl<T: Component, U: AsRef<[u8]>> Component for Title<T, U> {
 }
 
 #[cfg(feature = "ui_debug")]
-impl<T, U> crate::trace::Trace for Title<T, U>
+impl<T, U> crate::trace::Trace for Frame<T, U>
 where
     T: crate::trace::Trace,
     U: crate::trace::Trace + AsRef<[u8]>,
 {
     fn trace(&self, t: &mut dyn crate::trace::Tracer) {
-        t.open("Title");
+        t.open("Frame");
         t.field("title", &self.title);
         t.field("content", &self.content);
         t.close();
diff --git a/core/embed/rust/src/ui/model_t1/component/mod.rs b/core/embed/rust/src/ui/model_t1/component/mod.rs
index 9e614d6794..ec817cc8ed 100644
--- a/core/embed/rust/src/ui/model_t1/component/mod.rs
+++ b/core/embed/rust/src/ui/model_t1/component/mod.rs
@@ -1,11 +1,11 @@
 mod button;
 mod dialog;
+mod frame;
 mod page;
-mod title;
 
 use super::{event, theme};
 
 pub use button::{Button, ButtonContent, ButtonMsg, ButtonPos, ButtonStyle, ButtonStyleSheet};
 pub use dialog::{Dialog, DialogMsg};
+pub use frame::Frame;
 pub use page::ButtonPage;
-pub use title::Title;
diff --git a/core/embed/rust/src/ui/model_t1/layout.rs b/core/embed/rust/src/ui/model_t1/layout.rs
index 535ca19134..9133c54237 100644
--- a/core/embed/rust/src/ui/model_t1/layout.rs
+++ b/core/embed/rust/src/ui/model_t1/layout.rs
@@ -12,7 +12,7 @@ use crate::{
 };
 
 use super::{
-    component::{Button, ButtonPage, Title},
+    component::{Button, ButtonPage, Frame},
     theme,
 };
 
@@ -56,7 +56,7 @@ extern "C" fn ui_layout_new_confirm_action(
         let right = verb
             .map(|label| |area, pos| Button::with_text(area, pos, label, theme::button_default()));
 
-        let obj = LayoutObj::new(Child::new(Title::new(display::screen(), title, |area| {
+        let obj = LayoutObj::new(Child::new(Frame::new(display::screen(), title, |area| {
             ButtonPage::new(
                 area,
                 |area| {
@@ -84,7 +84,7 @@ extern "C" fn ui_layout_new_confirm_text(
         let description: Option<Buffer> =
             kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?;
 
-        let obj = LayoutObj::new(Child::new(Title::new(display::screen(), title, |area| {
+        let obj = LayoutObj::new(Child::new(Frame::new(display::screen(), title, |area| {
             ButtonPage::new(
                 area,
                 |area| {
@@ -194,7 +194,7 @@ arameters! > left:<Button text:Left > right:<Button text:Right > >"#
 
     #[test]
     fn trace_layout_title() {
-        let layout = Child::new(Title::new(display::screen(), "Please confirm", |area| {
+        let layout = Child::new(Frame::new(display::screen(), "Please confirm", |area| {
             Dialog::new(
                 area,
                 |area| {
@@ -210,7 +210,7 @@ arameters! > left:<Button text:Left > right:<Button text:Right > >"#
         }));
         assert_eq!(
             trace(&layout),
-            r#"<Title title:Please confirm content:<Dialog content:<Text content:Testing text layout,
+            r#"<Frame title:Please confirm content:<Dialog content:<Text content:Testing text layout,
 with some text, and
 some more text. And p-
 arameters! > left:<Button text:Left > right:<Button text:Right > > >"#
diff --git a/core/embed/rust/src/ui/model_tt/component/frame.rs b/core/embed/rust/src/ui/model_tt/component/frame.rs
new file mode 100644
index 0000000000..98b1134990
--- /dev/null
+++ b/core/embed/rust/src/ui/model_tt/component/frame.rs
@@ -0,0 +1,80 @@
+use super::theme;
+use crate::ui::{
+    component::{Child, Component, ComponentExt, Event, EventCtx},
+    display,
+    geometry::Rect,
+};
+
+pub struct Frame<T, U> {
+    area: Rect,
+    title: U,
+    content: Child<T>,
+}
+
+impl<T, U> Frame<T, U>
+where
+    T: Component,
+    U: AsRef<[u8]>,
+{
+    pub fn new(area: Rect, title: U, content: impl FnOnce(Rect) -> T) -> Self {
+        let (title_area, content_area) = Self::areas(area);
+        Self {
+            area: title_area,
+            title,
+            content: content(content_area).into_child(),
+        }
+    }
+
+    fn areas(area: Rect) -> (Rect, Rect) {
+        const HEADER_SPACE: i32 = 14;
+        let header_height = theme::FONT_BOLD.line_height() - theme::CONTENT_BORDER;
+
+        let (header_area, content_area) = area.hsplit(header_height);
+        let (_space, header_area) = header_area.vsplit(theme::CONTENT_BORDER);
+        let (_space, content_area) = content_area.hsplit(HEADER_SPACE);
+
+        (header_area, content_area)
+    }
+}
+
+impl<T, U> Component for Frame<T, U>
+where
+    T: Component,
+    U: AsRef<[u8]>,
+{
+    type Msg = T::Msg;
+
+    fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
+        self.content.event(ctx, event)
+    }
+
+    fn paint(&mut self) {
+        display::text(
+            self.area.bottom_left(),
+            self.title.as_ref(),
+            theme::FONT_BOLD,
+            theme::GREY_LIGHT,
+            theme::BG,
+        );
+        self.content.paint();
+    }
+}
+
+#[cfg(feature = "ui_debug")]
+impl<T, U> crate::trace::Trace for Frame<T, U>
+where
+    T: crate::trace::Trace,
+    U: crate::trace::Trace + AsRef<[u8]>,
+{
+    fn trace(&self, t: &mut dyn crate::trace::Tracer) {
+        t.open("Frame");
+        t.field("title", &self.title);
+        t.field("content", &self.content);
+        t.close();
+    }
+
+    fn bounds(&self, sink: &dyn Fn(Rect)) {
+        sink(self.area);
+        self.content.bounds(sink);
+    }
+}
diff --git a/core/embed/rust/src/ui/model_tt/component/mod.rs b/core/embed/rust/src/ui/model_tt/component/mod.rs
index 47c86bdbb7..a94437b9a9 100644
--- a/core/embed/rust/src/ui/model_tt/component/mod.rs
+++ b/core/embed/rust/src/ui/model_tt/component/mod.rs
@@ -1,6 +1,7 @@
 mod button;
 mod confirm;
 mod dialog;
+mod frame;
 mod loader;
 mod page;
 mod passphrase;
@@ -10,6 +11,7 @@ mod swipe;
 pub use button::{Button, ButtonContent, ButtonMsg, ButtonStyle, ButtonStyleSheet};
 pub use confirm::{HoldToConfirm, HoldToConfirmMsg};
 pub use dialog::{Dialog, DialogLayout, DialogMsg};
+pub use frame::Frame;
 pub use loader::{Loader, LoaderMsg, LoaderStyle, LoaderStyleSheet};
 pub use page::SwipePage;
 pub use swipe::{Swipe, SwipeDirection};