Reorganize user preferences page into tabs
What does this MR do and why?
Reorganizes the user Preferences page (/-/profile/preferences) into Pajamas tabs: Appearance, Behavior, Editing, Localization, and Integrations (shown only when an integration is available). The tabbed layout is behind the preferences_tabs feature flag; with the flag disabled, the same sections render on one page without tabs.
Why:
- The page had grown into one long list of unrelated settings. Users who arrive from links elsewhere in the product, such as the Web IDE or the merge request widget, landed in the middle of that list with no sense of where they were.
- Related settings were spread out: layout width sat far from the other appearance settings, and Markdown, editor, and code options were mixed with navigation defaults. Tabs give each area a name, and section headings inside each tab give the fields a hierarchy.
What changes for users:
- Tabs group the existing settings. No setting was added or removed; every field still submits through the same form and the same Save changes button.
- Deep links keep working: a hash naming a tab opens it, a hash naming a field inside a tab opens that tab and scrolls to the field, and the page search shows matches across every tab.
- GitLab Duo and GitLab Orbit settings stay in Behavior in this MR; !257859 moves them to an AI assistance tab.
Implementation notes for reviewers:
- Tabs use the
gl_tabs_nav/gl_tab_link_tohelpers withGlTabsBehavior. Pane ids differ from tab hrefs so a tab hash never triggers native anchor scrolling on reload. Panes stay inside the single remote form, so hidden fields are still submitted. - Hash handling (deep links into a pane, hash changes after load) lives in
GlTabsBehaviorsince !261545 (merged). The page adds only what it alone knows: native validation reveals the tab holding the invalid field throughrevealElement, and the settings search, which now dispatches asettings-search-term-changedDOM event on its root, shows every pane and hides the tab navigation while a term is active. No CSS overrides and no attribute queries in page code. - A tab definition can carry
localsfor its partial, so the Integrations tab computesintegration_viewsonce and hands it to the partial. - Section headings keep the default
SettingsSectionComponentsize and element. - With the flag disabled, each tab's sections are wrapped in an element carrying the tab id, so the anchors the page has always had (
#appearance,#behavior,#localization) keep working. - Docs are regrouped by tab with all existing anchors preserved.
Feature flag: preferences_tabs (gitlab_com_derisk). Rollout issue: #631894.
Stacked merge requests
This is part 4 of the user preferences page tabs series. The MRs are stacked and merge in order; each one is complete on its own.
| # | MR | Scope |
|---|---|---|
| 1 | !257856 (merged) | Fix: apply the layout width preference without a reload (merged) |
| 2 | !257857 (merged) | Shared inputs: heading_classes, labelDescription (merged) |
| 3 | !257858 (merged) | Refactor: extract the Integrations section into its own Vue app (merged) |
| 3b | !261545 (merged) | GlTabsBehavior: hash changes, deep links, revealElement (merged) |
| 4 | !257460 (merged) | Feature: reorganize the preferences page into tabs, behind the preferences_tabs flag |
| 5 | !257859 | Feature (EE): move GitLab Duo and Orbit preferences into an AI assistance tab |
Series split from the original single MR after review feedback in !257460 (merged).
References
- Pajamas tabs: https://design.gitlab.com/components/tabs
Screenshots or screen recordings
Captured in GDK with the flag enabled and Ona turned on, so the Integrations tab is visible.
Before
One long page. GitLab Duo and GitLab Orbit settings sit inside Behavior, between the overview defaults and the Web IDE options.
| Top of the page | Behavior section |
|---|---|
![]() |
![]() |
After
Appearance
Behavior
Editing
Localization
Integrations
Section headings in these screenshots are one step smaller than in the current revision, which uses the component default.
How to set up and validate locally
- Enable the flag:
Feature.enable(:preferences_tabs)inbin/rails console. - Visit
http://127.0.0.1:3000/-/profile/preferencesand switch between tabs. Change a setting in one tab and one in another, select Save changes, and confirm both persisted. - Reload with a tab hash, for example
#localization, and confirm the tab opens with the tab bar still visible. - Open
http://127.0.0.1:3000/-/profile/preferences#user_tab_widthfrom another page and confirm the Editing tab opens and scrolls to Tab width. - In Editing, set Tab width to
-1, switch to Appearance, select Save changes, and confirm the page switches back to Editing and shows the validation message. - Type
Time preferencesin Search page and confirm the matching section appears even though it lives in another tab; clear the search and confirm the tabs return. - To see Integrations, turn on Ona or Sourcegraph in Admin > Settings > General.
- Disable the flag and confirm every section renders on one page, saving still works, and
#behaviorstill scrolls to the Behavior sections.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.






