Fish moves default appearance settings into startup configuration
Fish 4.3 changes theme and key-binding defaults from universal to global scope, migrates existing settings, and adds terminal-aware theme selection.
A theme setting can outlive the configuration command that created it. Fish's universal variables make that intentional, but persistence becomes harder to understand when it is also how the shell installs its defaults. Editing a startup file then explains only part of the resulting configuration. Another value may already exist in the universal store and remain there across shell restarts.
Fish 4.3.0, released December 28, changes that arrangement for appearance and key bindings. The release notes identify fish_color_*, fish_pager_color_*, and fish_key_bindings as global by default. Universal variables remain available, including an explicit way to save theme colors there. The change concerns the default ownership of these settings.
I like fish's willingness to make interactive behavior useful before configuration. Persisting those defaults separately can complicate later customization, though. Global values established at startup give a configuration file a clearer role in explaining why a new shell looks and behaves as it does.
Scope determines the lifetime of a preference
The language documentation distinguishes universal variables shared among a user's fish sessions on one computer from globals belonging to one session. Universal values persist on disk. A global value can last throughout a shell's lifetime without changing other running shells.
Scope also determines lookup and assignment. A narrower variable can hide a value of the same name in a broader scope. An assignment without a scope flag updates the existing variable in the smallest applicable scope. Merely removing a command from config.fish therefore does not erase a value previously stored universally.
The set command provides an inspection tool for this distinction. These commands describe the settings and the scopes in which they exist, without changing them.
set --show fish_color_command
set --show fish_key_bindings
Using --global for an assignment selects its scope explicitly. Exporting is a separate property controlled by --export. A shell preference can be globally available to functions in the current session without becoming an environment variable passed to child processes. The new defaults do not require appearance settings to become part of every executed program's environment.
Migration preserves customization in files
The release describes a one-time migration into ~/.config/fish/conf.d/fish_frozen_theme.fish and ~/.config/fish/conf.d/fish_frozen_key_bindings.fish. These files preserve existing choices by setting globals at startup. They are migration output, rather than the recommended permanent place for maintaining those choices. The project recommends moving intentional configuration into config.fish and deleting the frozen files.
The migration implementation has details that the short announcement cannot convey. It checks the initialization version before migrating. For colors, it compares existing universal values with historical defaults. Customized values can produce a frozen theme, while recognized default values need not be preserved as a personal override.
The key-binding migration also accounts for shells that are already running. Older fish versions can recreate the universal fish_key_bindings value when it is erased. The generated file therefore erases that universal at startup as a compatibility measure. A migration is sharing state with processes running the previous behavior, so changing the on-disk representation once is insufficient by itself.
That detail explains why the generated files deserve to be read before removal. They record both the preserved preference and migration logic. Transferring the desired choice into ordinary configuration is more precise than treating every generated line as a setting that must remain indefinitely. Restarting older shells also ends their participation in the previous universal-variable behavior.
Startup order makes the override location useful
The startup configuration runs theme migration in interactive shells, applies the default theme, and then sources configuration snippets. User snippets have precedence over snippets with the same basename in system and vendor locations. This makes the frozen files ordinary participants in startup configuration after their creation.
The language documentation specifies that the user's config.fish runs after those snippets. An intentional choice there can consequently override the values established earlier. The order matters when understanding a migrated installation that still contains a frozen theme. The result depends on execution order as well as scope.
A small configuration can select the default theme and then change one color. This belongs in config.fish when the choice should apply to future sessions.
fish_config theme choose default
set --global fish_color_error red --bold
The second command states a personal override after applying the theme. It does not depend on a value remembered from an earlier interactive invocation. Keeping the desired setting alongside the command that chooses the theme also makes their ordering visible during review.
Themes can follow the terminal's background
The default-scope change accompanies support for themes with light and dark variants. Issue 11580, opened in June, requested theme switching based on the system's appearance. The implemented interface uses the terminal's reported color theme, which is the relevant information for colors drawn inside that terminal.
The theme documentation defines [light], [dark], and [unknown] sections. The last handles terminals that do not report their colors. A theme can therefore specify suitable values for both backgrounds without requiring separate startup configurations. Theme files contain color-variable assignments with a restricted format, rather than arbitrary shell expansions.
fish_config theme choose applies the appropriate variant and can respond when fish_terminal_color_theme changes. An explicit --color-theme argument selects a variant manually. By comparison, fish_config theme save still persists universal color values but does not track later background changes. These commands now express different configuration lifetimes and behavior.
The theme-selection implementation installs a variable-change handler when automatic switching is appropriate. It also tracks variables set by a selected theme. Choosing another theme clears values left by earlier theme selections, while explicit color overrides have a separate identity. The release's narrower clearing behavior helps a theme change coexist with personal settings.
I prefer automatic background matching to duplicating a full configuration for each appearance. It also makes session-local color values sensible. Two terminals can have different backgrounds, so sharing one set of concrete colors across every shell is not always the desired policy.
Sharing changes remains an explicit option
The interactive documentation shows how to propagate a theme choice among sessions without making every color universal. Each session registers a handler for a shared variable, and the handler chooses the named theme locally.
function apply-my-theme --on-variable=my_theme
fish_config theme choose $my_theme
end
After sessions load that handler, an interactive set -U my_theme lava distributes the choice. The shared value is a theme name. Each receiving shell applies it through its own theme machinery, which can account for that terminal's appearance. This preserves the useful synchronization behavior while separating the selection from the resulting color variables.
The handler must already exist in the sessions that should react, so adding it to config.fish and restarting those sessions is part of the documented setup. It is a concrete opt-in policy for shared preferences. Ordinary global settings do not silently acquire the same propagation behavior.
Message language gets its own interface
The new status language command makes another shell preference explicit. It can display available translations and choose an ordered list for fish's own messages. The choice overrides locale environment variables for fish without changing the language used by child processes.
status language list-available
status language set pt_BR fr
In that documented example, fish uses Brazilian Portuguese where a translation exists and then French where available. Messages missing from both catalogs appear in English. status language unset returns selection to the environment. This provides a local control for shell diagnostics without requiring a broader environment change.
Terminal state is refreshed at the prompt
The release also repairs stale working-directory reporting. Issue 12191 explains that reporting only when $PWD changes misses state left by a child process, including an SSH session. The parent shell can regain control in its original directory without its own $PWD changing at all.
The interactive initialization code adds the fish_prompt event to the existing directory-change handler. Fish therefore refreshes its OSC 7 directory report at each fresh prompt as well as on directory changes. The terminal receives the parent shell's location again after command execution, even when that location has remained unchanged inside fish.