ui/styles/focus_ring.rs
1//! Focus ring wrapper with a true offset gap.
2//!
3//! GPUI has no `ring`/`ring-offset` utility. To reproduce Tailwind's
4//! `ring-2 ring-offset-2` look (a ring separated from the element by a gap),
5//! we WRAP the focusable content in an outer bordered container with inner
6//! padding. The wrapper keeps its size in both states (transparent border when
7//! unfocused) so focus does not shift layout.
8
9use gpui::{Div, Hsla, IntoElement, ParentElement, Styled, div, px, transparent_black};
10
11use crate::styles::palette;
12
13/// Wraps `content` in a gapped focus ring of `color`, shown only when
14/// `focused`. Layout is stable across focus states.
15pub fn focus_ring(content: impl IntoElement, focused: bool, color: Hsla) -> Div {
16 let ring_color = if focused { color } else { transparent_black() };
17 div()
18 .rounded_lg()
19 .border_2()
20 .border_color(ring_color)
21 .p(px(2.))
22 .child(content)
23}
24
25/// Focus ring in the primary accent color (default for inputs/buttons).
26pub fn focus_ring_primary(content: impl IntoElement, focused: bool) -> Div {
27 focus_ring(content, focused, palette::primary(500))
28}
29
30/// Focus ring in the danger color, for invalid/error fields.
31pub fn focus_ring_error(content: impl IntoElement, focused: bool) -> Div {
32 focus_ring(content, focused, palette::danger(500))
33}