Skip to content

Commit 5bce369

Browse files
authored
fix: clean up styles & colors and define in styles.md (#2401)
New style guide: # Headers, primary, and secondary text - **Headers:** Use `bold`. For markdown with various header levels, leave in the `#` signs. - **Primary text:** Default. - **Secondary text:** Use `dim`. # Foreground colors - **Default:** Most of the time, just use the default foreground color. `reset` can help get it back. - **Selection:** Use ANSI `blue`. (Ed & AE want to make this cyan too, but we'll do that in a followup since it's riskier in different themes.) - **User input tips and status indicators:** Use ANSI `cyan`. - **Success and additions:** Use ANSI `green`. - **Errors, failures and deletions:** Use ANSI `red`. - **Codex:** Use ANSI `magenta`. # Avoid - Avoid custom colors because there's no guarantee that they'll contrast well or look good on various terminal color themes. - Avoid ANSI `black`, `white`, `yellow` as foreground colors because the terminal theme will do a better job. (Use `reset` if you need to in order to get those.) The exception is if you need contrast rendering over a manually colored background. (There are some rules to try to catch this in `clippy.toml`.) # Testing Tested in a variety of light and dark color themes in Terminal, iTerm2, and Ghostty.
1 parent a269754 commit 5bce369

File tree

10 files changed

+62
-50
lines changed

10 files changed

+62
-50
lines changed

AGENTS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ Before finalizing a change to `codex-rs`, run `just fmt` (in `codex-rs` director
1212
1. Run the test for the specific project that was changed. For example, if changes were made in `codex-rs/tui`, run `cargo test -p codex-tui`.
1313
2. Once those pass, if any changes were made in common, core, or protocol, run the complete test suite with `cargo test --all-features`.
1414

15+
## TUI style conventions
16+
17+
See `codex-rs/tui/styles.md`.
18+
1519
## TUI code conventions
1620

1721
- Use concise styling helpers from ratatui’s Stylize trait.

codex-rs/clippy.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,9 @@
11
allow-expect-in-tests = true
2-
allow-unwrap-in-tests = true
2+
allow-unwrap-in-tests = true
3+
disallowed-methods = [
4+
{ path = "ratatui::style::Color::Rgb", reason = "Use ANSI colors, which work better in various terminal themes." },
5+
{ path = "ratatui::style::Color::Indexed", reason = "Use ANSI colors, which work better in various terminal themes." },
6+
{ path = "ratatui::style::Stylize::white", reason = "Avoid hardcoding white; prefer default fg or dim/bold. Exception: Disable this rule if rendering over a hardcoded ANSI background." },
7+
{ path = "ratatui::style::Stylize::black", reason = "Avoid hardcoding black; prefer default fg or dim/bold. Exception: Disable this rule if rendering over a hardcoded ANSI background." },
8+
{ path = "ratatui::style::Stylize::yellow", reason = "Avoid yellow; prefer other colors in `tui/styles.md`." },
9+
]

codex-rs/tui/src/bottom_pane/selection_popup_common.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ pub(crate) fn render_rows(
101101
if Some(i) == state.selected_idx {
102102
cell = cell.style(
103103
Style::default()
104-
.fg(Color::Yellow)
104+
.fg(Color::Blue)
105105
.add_modifier(Modifier::BOLD),
106106
);
107107
} else if *is_current {

codex-rs/tui/src/colors.rs

Lines changed: 0 additions & 4 deletions
This file was deleted.

codex-rs/tui/src/history_cell.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use crate::colors::LIGHT_BLUE;
21
use crate::diff_render::create_diff_summary;
32
use crate::exec_command::relativize_to_home;
43
use crate::exec_command::strip_bash_lc_and_escape;
@@ -252,12 +251,13 @@ fn new_parsed_command(
252251
lines.push(Line::from(spans));
253252
}
254253
Some(o) if o.exit_code == 0 => {
255-
lines.push(Line::from("✓ Completed".green().bold()));
254+
lines.push(Line::from(vec!["✓".green(), " Completed".into()]));
256255
}
257256
Some(o) => {
258-
lines.push(Line::from(
259-
format!("✗ Failed (exit {})", o.exit_code).red().bold(),
260-
));
257+
lines.push(Line::from(vec![
258+
"✗".red(),
259+
format!(" Failed (exit {})", o.exit_code).into(),
260+
]));
261261
}
262262
};
263263

@@ -304,7 +304,7 @@ fn new_parsed_command(
304304
let prefix = if j == 0 { first_prefix } else { " " };
305305
lines.push(Line::from(vec![
306306
Span::styled(prefix, Style::default().add_modifier(Modifier::DIM)),
307-
Span::styled(line_text.to_string(), Style::default().fg(LIGHT_BLUE)),
307+
line_text.to_string().dim(),
308308
]));
309309
}
310310
}

codex-rs/tui/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The standalone `codex-tui` binary prints a short help message before the
33
// alternate‑screen mode starts; that file opts‑out locally via `allow`.
44
#![deny(clippy::print_stdout, clippy::print_stderr)]
5+
#![deny(clippy::disallowed_methods)]
56
use app::App;
67
use codex_core::BUILT_IN_OSS_MODEL_PROVIDER_ID;
78
use codex_core::config::Config;
@@ -28,7 +29,6 @@ mod bottom_pane;
2829
mod chatwidget;
2930
mod citation_regex;
3031
mod cli;
31-
mod colors;
3232
mod common;
3333
pub mod custom_terminal;
3434
mod diff_render;

codex-rs/tui/src/onboarding/auth.rs

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use ratatui::prelude::Widget;
99
use ratatui::style::Color;
1010
use ratatui::style::Modifier;
1111
use ratatui::style::Style;
12+
use ratatui::style::Stylize;
1213
use ratatui::text::Line;
1314
use ratatui::text::Span;
1415
use ratatui::widgets::Paragraph;
@@ -19,8 +20,6 @@ use codex_login::AuthMode;
1920

2021
use crate::app_event::AppEvent;
2122
use crate::app_event_sender::AppEventSender;
22-
use crate::colors::LIGHT_BLUE;
23-
use crate::colors::SUCCESS_GREEN;
2423
use crate::onboarding::onboarding_screen::KeyboardHandler;
2524
use crate::onboarding::onboarding_screen::StepStateProvider;
2625
use crate::shimmer::shimmer_spans;
@@ -131,19 +130,17 @@ impl AuthModeWidget {
131130

132131
let line1 = if is_selected {
133132
Line::from(vec![
134-
Span::styled(
135-
format!("{} {}. ", caret, idx + 1),
136-
Style::default().fg(LIGHT_BLUE).add_modifier(Modifier::DIM),
137-
),
138-
Span::styled(text.to_owned(), Style::default().fg(LIGHT_BLUE)),
133+
format!("{} {}. ", caret, idx + 1).blue().dim(),
134+
text.to_string().blue(),
139135
])
140136
} else {
141137
Line::from(format!(" {}. {text}", idx + 1))
142138
};
143139

144140
let line2 = if is_selected {
145141
Line::from(format!(" {description}"))
146-
.style(Style::default().fg(LIGHT_BLUE).add_modifier(Modifier::DIM))
142+
.fg(Color::Blue)
143+
.add_modifier(Modifier::DIM)
147144
} else {
148145
Line::from(format!(" {description}"))
149146
.style(Style::default().add_modifier(Modifier::DIM))
@@ -197,12 +194,7 @@ impl AuthModeWidget {
197194
lines.push(Line::from(" If the link doesn't open automatically, open the following link to authenticate:"));
198195
lines.push(Line::from(vec![
199196
Span::raw(" "),
200-
Span::styled(
201-
state.auth_url.as_str(),
202-
Style::default()
203-
.fg(LIGHT_BLUE)
204-
.add_modifier(Modifier::UNDERLINED),
205-
),
197+
state.auth_url.as_str().blue().underlined(),
206198
]));
207199
lines.push(Line::from(""));
208200
}
@@ -218,8 +210,7 @@ impl AuthModeWidget {
218210

219211
fn render_chatgpt_success_message(&self, area: Rect, buf: &mut Buffer) {
220212
let lines = vec![
221-
Line::from("✓ Signed in with your ChatGPT account")
222-
.style(Style::default().fg(SUCCESS_GREEN)),
213+
Line::from("✓ Signed in with your ChatGPT account").fg(Color::Green),
223214
Line::from(""),
224215
Line::from("> Before you start:"),
225216
Line::from(""),
@@ -233,8 +224,7 @@ impl AuthModeWidget {
233224
])
234225
.style(Style::default().add_modifier(Modifier::DIM)),
235226
Line::from(""),
236-
Line::from(" Codex can make mistakes")
237-
.style(Style::default().fg(Color::White)),
227+
Line::from(" Codex can make mistakes"),
238228
Line::from(" Review the code it writes and commands it runs")
239229
.style(Style::default().add_modifier(Modifier::DIM)),
240230
Line::from(""),
@@ -248,7 +238,7 @@ impl AuthModeWidget {
248238
])
249239
.style(Style::default().add_modifier(Modifier::DIM)),
250240
Line::from(""),
251-
Line::from(" Press Enter to continue").style(Style::default().fg(LIGHT_BLUE)),
241+
Line::from(" Press Enter to continue").fg(Color::Blue),
252242
];
253243

254244
Paragraph::new(lines)
@@ -257,19 +247,15 @@ impl AuthModeWidget {
257247
}
258248

259249
fn render_chatgpt_success(&self, area: Rect, buf: &mut Buffer) {
260-
let lines = vec![
261-
Line::from("✓ Signed in with your ChatGPT account")
262-
.style(Style::default().fg(SUCCESS_GREEN)),
263-
];
250+
let lines = vec![Line::from("✓ Signed in with your ChatGPT account").fg(Color::Green)];
264251

265252
Paragraph::new(lines)
266253
.wrap(Wrap { trim: false })
267254
.render(area, buf);
268255
}
269256

270257
fn render_env_var_found(&self, area: Rect, buf: &mut Buffer) {
271-
let lines =
272-
vec![Line::from("✓ Using OPENAI_API_KEY").style(Style::default().fg(SUCCESS_GREEN))];
258+
let lines = vec![Line::from("✓ Using OPENAI_API_KEY").fg(Color::Green)];
273259

274260
Paragraph::new(lines)
275261
.wrap(Wrap { trim: false })

codex-rs/tui/src/onboarding/trust_directory.rs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ use ratatui::widgets::Paragraph;
1818
use ratatui::widgets::WidgetRef;
1919
use ratatui::widgets::Wrap;
2020

21-
use crate::colors::LIGHT_BLUE;
22-
2321
use crate::onboarding::onboarding_screen::KeyboardHandler;
2422
use crate::onboarding::onboarding_screen::StepStateProvider;
2523

@@ -77,13 +75,7 @@ impl WidgetRef for &TrustDirectoryWidget {
7775
|idx: usize, option: TrustDirectorySelection, text: &str| -> Line<'static> {
7876
let is_selected = self.highlighted == option;
7977
if is_selected {
80-
Line::from(vec![
81-
Span::styled(
82-
format!("> {}. ", idx + 1),
83-
Style::default().fg(LIGHT_BLUE).add_modifier(Modifier::DIM),
84-
),
85-
Span::styled(text.to_owned(), Style::default().fg(LIGHT_BLUE)),
86-
])
78+
Line::from(format!("> {}. {text}", idx + 1)).blue()
8779
} else {
8880
Line::from(format!(" {}. {}", idx + 1, text))
8981
}

codex-rs/tui/src/shimmer.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,14 @@ pub(crate) fn shimmer_spans(text: &str) -> Vec<Span<'static>> {
4646
let brightness = 0.4 + 0.6 * t;
4747
let level = (brightness * 255.0).clamp(0.0, 255.0) as u8;
4848
let style = if has_true_color {
49-
Style::default()
50-
.fg(Color::Rgb(level, level, level))
51-
.add_modifier(Modifier::BOLD)
49+
// Allow custom RGB colors, as the implementation is thoughtfully
50+
// adjusting the level of the default foreground color.
51+
#[allow(clippy::disallowed_methods)]
52+
{
53+
Style::default()
54+
.fg(Color::Rgb(level, level, level))
55+
.add_modifier(Modifier::BOLD)
56+
}
5257
} else {
5358
color_for_level(level)
5459
};

codex-rs/tui/styles.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Headers, primary, and secondary text
2+
3+
- **Headers:** Use `bold`. For markdown with various header levels, leave in the `#` signs.
4+
- **Primary text:** Default.
5+
- **Secondary text:** Use `dim`.
6+
7+
# Foreground colors
8+
9+
- **Default:** Most of the time, just use the default foreground color. `reset` can help get it back.
10+
- **Selection:** Use ANSI `blue`. (Ed & AE want to make this cyan too, but we'll do that in a followup since it's riskier in different themes.)
11+
- **User input tips and status indicators:** Use ANSI `cyan`.
12+
- **Success and additions:** Use ANSI `green`.
13+
- **Errors, failures and deletions:** Use ANSI `red`.
14+
- **Codex:** Use ANSI `magenta`.
15+
16+
# Avoid
17+
18+
- Avoid custom colors because there's no guarantee that they'll contrast well or look good in various terminal color themes.
19+
- Avoid ANSI `black` & `white` as foreground colors because the default terminal theme color will do a better job. (Use `reset` if you need to in order to get those.) The exception is if you need contrast rendering over a manually colored background.
20+
- Avoid ANSI `yellow` because for now the style guide doesn't use it. Prefer a foreground color mentioned above.
21+
22+
(There are some rules to try to catch this in `clippy.toml`.)

0 commit comments

Comments
 (0)