diff --git a/assets/fonts/FiraMono-Regular.ttf b/assets/fonts/FiraMono-Regular.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..67bbd4287d424a70c582c86278bfa742a6bead6c
Binary files /dev/null and b/assets/fonts/FiraMono-Regular.ttf differ
diff --git a/examples/login.rs b/examples/login.rs
index 8942c5176a73155da2c4e7f6a0e2c093a3d3c93c..29119703f6bcf676236b3915bd782dede7ec861d 100644
--- a/examples/login.rs
+++ b/examples/login.rs
@@ -62,12 +62,66 @@ fn setup(mut commands: Commands, window: Query<&Window, With<PrimaryWindow>>) {
                 ..default()
             })
             .insert(CosmicEditPlaceholderBundle {
-                text_setter: PlaceholderText(CosmicText::OneStyle("Password".into())),
+                text_setter: PlaceholderText(CosmicText::OneStyle("Password ●".into())),
                 attrs: PlaceholderAttrs(AttrsOwned::new(
                     Attrs::new().color(bevy_color_to_cosmic(Color::rgb_u8(128, 128, 128))),
                 )),
             })
-            .insert(PasswordInput('\u{1F92B}'));
+            .insert(PasswordInput('●'));
+
+            root.spawn(CosmicEditBundle {
+                max_lines: CosmicMaxLines(1),
+                metrics: CosmicMetrics {
+                    scale_factor: window.scale_factor() as f32,
+                    ..default()
+                },
+                ..default()
+            })
+            .insert(ButtonBundle {
+                style: Style {
+                    // Size and position of text box
+                    width: Val::Px(300.),
+                    height: Val::Px(50.),
+                    margin: UiRect::all(Val::Px(15.0)),
+                    ..default()
+                },
+                background_color: BackgroundColor(Color::WHITE),
+                ..default()
+            })
+            .insert(CosmicEditPlaceholderBundle {
+                text_setter: PlaceholderText(CosmicText::OneStyle("Password 🙊".into())),
+                attrs: PlaceholderAttrs(AttrsOwned::new(
+                    Attrs::new().color(bevy_color_to_cosmic(Color::rgb_u8(128, 128, 128))),
+                )),
+            })
+            .insert(PasswordInput('🙊'));
+
+            root.spawn(CosmicEditBundle {
+                max_lines: CosmicMaxLines(1),
+                metrics: CosmicMetrics {
+                    scale_factor: window.scale_factor() as f32,
+                    ..default()
+                },
+                ..default()
+            })
+            .insert(ButtonBundle {
+                style: Style {
+                    // Size and position of text box
+                    width: Val::Px(300.),
+                    height: Val::Px(50.),
+                    margin: UiRect::all(Val::Px(15.0)),
+                    ..default()
+                },
+                background_color: BackgroundColor(Color::WHITE),
+                ..default()
+            })
+            .insert(CosmicEditPlaceholderBundle {
+                text_setter: PlaceholderText(CosmicText::OneStyle("Password -".into())),
+                attrs: PlaceholderAttrs(AttrsOwned::new(
+                    Attrs::new().color(bevy_color_to_cosmic(Color::rgb_u8(128, 128, 128))),
+                )),
+            })
+            .insert(PasswordInput('-'));
         });
 }
 
@@ -104,7 +158,7 @@ fn print_changed_input(mut evr_type: EventReader<CosmicTextChanged>) {
 }
 
 fn main() {
-    let font_bytes: &[u8] = include_bytes!("../assets/fonts/VictorMono-Regular.ttf");
+    let font_bytes: &[u8] = include_bytes!("../assets/fonts/FiraMono-Regular.ttf");
     let font_config = CosmicFontConfig {
         fonts_dir_path: None,
         font_bytes: Some(vec![font_bytes]),
diff --git a/src/input.rs b/src/input.rs
index 3e1b84ebf95d310d32fa158b3401d89edb4c6a8d..5c32b205864c174a19c729264ad420f13c797e2c 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -22,8 +22,8 @@ use wasm_bindgen_futures::JsFuture;
 use crate::{
     get_node_cursor_pos, get_timestamp, get_x_offset_center, get_y_offset_center,
     save_edit_history, CosmicAttrs, CosmicEditHistory, CosmicEditor, CosmicFontSystem,
-    CosmicMaxChars, CosmicMaxLines, CosmicTextChanged, CosmicTextPosition, Focus, ReadOnly,
-    XOffset,
+    CosmicMaxChars, CosmicMaxLines, CosmicText, CosmicTextChanged, CosmicTextPosition, Focus,
+    PasswordInput, ReadOnly, XOffset,
 };
 
 #[derive(Resource)]
@@ -49,12 +49,14 @@ pub(crate) fn input_mouse(
     buttons: Res<Input<MouseButton>>,
     mut cosmic_edit_query: Query<(
         &mut CosmicEditor,
+        &CosmicAttrs,
         &GlobalTransform,
         &CosmicTextPosition,
         Entity,
         &XOffset,
         Option<&mut Node>,
         Option<&mut Sprite>,
+        Option<&PasswordInput>,
     )>,
     mut font_system: ResMut<CosmicFontSystem>,
     mut scroll_evr: EventReader<MouseWheel>,
@@ -86,13 +88,41 @@ pub(crate) fn input_mouse(
     let primary_window = windows.single();
     let scale_factor = primary_window.scale_factor() as f32;
     let (camera, camera_transform) = camera_q.iter().find(|(c, _)| c.is_active).unwrap();
-    for (mut editor, node_transform, text_position, entity, x_offset, node_opt, sprite_opt) in
-        &mut cosmic_edit_query.iter_mut()
+    for (
+        mut editor,
+        attrs,
+        node_transform,
+        text_position,
+        entity,
+        x_offset,
+        node_opt,
+        sprite_opt,
+        password_opt,
+    ) in &mut cosmic_edit_query.iter_mut()
     {
         if active_editor.0 != Some(entity) {
             continue;
         }
 
+        // hijack buffer contents
+        let current_text = editor.get_text();
+        let current_select_opt = editor.0.select_opt().clone();
+        let current_cursor = editor.0.cursor().clone();
+
+        // intercept text for password inputs
+        if let Some(password) = password_opt {
+            if !current_text.is_empty() {
+                editor.set_text(
+                    CosmicText::OneStyle(format!("{}", password.0).repeat(current_text.len())),
+                    attrs.0.clone(),
+                    &mut font_system.0,
+                );
+
+                editor.0.set_select_opt(current_select_opt);
+                editor.0.set_cursor(current_cursor);
+            }
+        }
+
         let (width, height, is_ui_node) = match node_opt {
             Some(node) => (node.size().x, node.size().y, true),
             None => {
@@ -209,6 +239,18 @@ pub(crate) fn input_mouse(
                 }
             }
         }
+
+        // reset intercepted text
+        if password_opt.is_some() && !current_text.is_empty() {
+            editor.set_text(
+                crate::CosmicText::OneStyle(current_text),
+                attrs.0.clone(),
+                &mut font_system.0,
+            );
+
+            editor.0.set_select_opt(current_select_opt);
+            editor.0.set_cursor(current_cursor);
+        }
     }
 }
 
diff --git a/src/render.rs b/src/render.rs
index 5451d97b3393436bb5b5d65b14f997c4ca838d77..80b63e021f651b80f9c1dbbb835f8bc5e9d69b54 100644
--- a/src/render.rs
+++ b/src/render.rs
@@ -72,6 +72,8 @@ pub(crate) fn cosmic_edit_redraw_buffer(
         }
 
         let current_text = cosmic_editor.get_text();
+        let current_select_opt = cosmic_editor.0.select_opt().clone();
+        let current_cursor = cosmic_editor.0.cursor().clone();
 
         // intercept text for password inputs
         if let Some(password) = password_opt {
@@ -81,6 +83,9 @@ pub(crate) fn cosmic_edit_redraw_buffer(
                     attrs.0.clone(),
                     &mut font_system.0,
                 );
+
+                cosmic_editor.0.set_select_opt(current_select_opt);
+                cosmic_editor.0.set_cursor(current_cursor);
             }
         }
 
@@ -286,6 +291,9 @@ pub(crate) fn cosmic_edit_redraw_buffer(
                 attrs.0.clone(),
                 &mut font_system.0,
             );
+
+            cosmic_editor.0.set_select_opt(current_select_opt);
+            cosmic_editor.0.set_cursor(current_cursor);
         }
     }
 }