Skip to main content

Customize view and page tabs

A Browser window has two types of tabs: page tabs, which switch between pages, and view tabs, which switch between the views within a page. You can customize both so users can find what they need at a glance, even when many tabs are open.

For page tabs and view tabs, you can set minimum and maximum tab widths (HERE Core 42 or later).

For view tabs, in HERE Core UI 45 or later, you can also:

  • Override or hide the favicon on individual view tabs.

  • Set the background color and title text color of individual view tabs, and change them at runtime.

  • Add one or more interactive icon buttons to a view tab, and run your own code when a user clicks them.

  • Let view tabs resize to fit their content, within the minimum and maximum widths you set.

  • Look up the page and window that contain a view with a single call.

In HERE Core UI 45 or later, Browser also hides the close button on inactive view tabs until the user hovers over the tab or moves keyboard focus to it, which leaves more room for the tab title.

These settings are part of the view and window options you already use: createView, updateOptions, and snapshots. You don't need to modify the tab DOM, write preload scripts, or inject CSS.

info

To pin or lock page tabs, see Pin and lock page tabs.

Summary​

CustomizationApplies toWhere you set itCan change at runtime
Tab widthPage tabs and view tabsworkspacePlatform.pageTabDimensions and workspacePlatform.viewTabDimensions on the window optionsNo
Content-based widthView tabsworkspacePlatform.viewTabDimensions.widthMode on the window optionsNo
Favicon override or hideView tabsicon on the view optionsYes, with view.updateOptions()
Background and text colorView tabsworkspacePlatform.viewTab.backgroundColor and workspacePlatform.viewTab.fontColor on the view optionsYes, with view.updateOptions()
Interactive controlsView tabsworkspacePlatform.viewTab.controls on the view optionsYes, with view.updateOptions()
Page and window lookupView tabsplatform.Browser.getPageByViewIdentity()Not applicable

Customize view and page tab width​

In HERE Core 42 or later, Browser can resize page tabs and view tabs automatically, making them wider or narrower within limits you set. This behavior mirrors the behavior found in Chrome. You set the limits separately for each type of tab:

PropertyApplies to
pageTabDimensionsPage tabs
viewTabDimensionsView tabs

Each property takes a minWidth and a maxWidth. Tabs never shrink below minWidth or grow beyond maxWidth. All tabs of the same type have the same width, unless you set view tabs to size to their content.

You can specify sizes in pixels (for example, "100px") or as a percentage of the tab strip width (for example, "20%"). A minWidth below 28px is ignored, and 28px is used instead.

Both properties are optional, and you can set one without the other. If you omit a property, that type of tab uses the default width of 120px for both minWidth and maxWidth.

When there are more tabs than fit in the window:

  • Page tabs keep the existing horizontal scrollbar.
  • View tabs scroll horizontally, with a shadow at the edge of the tab row to show that more tabs are hidden. This replaces the dropdown overflow menu.

The close button (✕) stays available on every tab, whatever its width. For inactive view tabs in HERE Core UI 45 or later, it appears when the user hovers over the tab or focuses it. See Close button visibility.

JavaScript object example​

platform: {
defaultWindowOptions: {
workspacePlatform: {
// Page tab width
pageTabDimensions: {
minWidth: "30px",
maxWidth: "400px"
},
// View tab width
viewTabDimensions: {
minWidth: "50px",
maxWidth: "400px"
}
}
}
}

Platform manifest example​

"platform": {
"defaultWindowOptions": {
"workspacePlatform": {
"pageTabDimensions": {
"minWidth": "30px",
"maxWidth": "400px"
},
"viewTabDimensions": {
"minWidth": "50px",
"maxWidth": "400px"
}
}
}
}

Size view tabs to fit their content​

In HERE Core UI 45 or later, you can let each view tab size itself to its content instead of giving all view tabs the same width. Set viewTabDimensions.widthMode to 'content':

widthMode valueResult
'uniform' (default)All view tabs have the same width, within minWidth and maxWidth.
'content'Each view tab is as wide as its title, favicon, and controls need, within minWidth and maxWidth. Tabs with short titles are narrower than tabs with long titles. Titles that don't fit within maxWidth are truncated.

The default limits depend on the width mode. With 'uniform', both minWidth and maxWidth default to 120px. With 'content', there is no default minWidth beyond the 28px floor that applies in both modes, and maxWidth defaults to 200px.

widthMode is available for view tabs only. Page tabs always have the same width.

await platform.Browser.createWindow({
workspacePlatform: {
viewTabDimensions: {
widthMode: "content",
minWidth: "60px",
maxWidth: "200px"
}
}
});

Override or hide the view tab icon​

By default, a view tab shows the favicon of the page loaded in the view, as long as favicons are enabled for the window with experimental.showFavicons. To let your platform choose the icon instead, set the icon property on the view options. The icon property takes precedence over the window-level favicon setting for that view.

icon valueResult
OmittedInherit the window setting. If showFavicons is enabled, the tab shows the page favicon, then the default favicon, then no icon. If showFavicons is disabled, the tab shows no icon.
A URLAlways show the image at that URL, even if showFavicons is disabled.
'hide'Always hide the icon, even if showFavicons is enabled. The tab title expands into the space the icon used.
'unset'Remove a previous override and inherit the window setting again. Same as omitted.

An icon URL or 'hide' stays in effect when the page in the view later changes its own favicon.

import * as WorkspacePlatform from "@openfin/workspace-platform";

const platform = WorkspacePlatform.getCurrentSync();

// Always show your own icon on this tab
const view = await platform.createView({
url: "https://intranet.example.com/app",
icon: "https://intranet.example.com/app-mark.png"
});

// Hide the icon; the title uses the freed space
await view.updateOptions({ icon: "hide" });

// Return to the window-level favicon setting
await view.updateOptions({ icon: "unset" });

When you call updateOptions() without an icon property, the current icon doesn't change.

note

The view icon property controls the view tab only. It is separate from the window icon property, which sets the taskbar icon.

Set view tab colors​

To color-code a view tab, set these properties in workspacePlatform.viewTab on the view options:

PropertyWhat it colors
backgroundColorThe tab background.
fontColorThe tab title text. It does not change the color of the close or lock icons.

The two properties are independent, so you can set one without the other. Each takes a CSS color value for each tab state:

StateWhen it applies
defaultThe tab is inactive.
hoverThe user's pointer is over the tab, and the tab is not active.
activeThe tab is the selected tab in its stack.
focusThe tab is active, and its view has focus.

Every state is optional. Any state you omit uses the color from the current theme, set by the color.coreui.browserViewTab tokens described in Granular color theming. The colors apply only to that view's tab; other tabs keep the theme colors.

const view = await platform.createView({
url: "https://intranet.example.com/app",
workspacePlatform: {
viewTab: {
backgroundColor: {
default: "#0f172a",
hover: "#1e293b",
active: "#334155",
focus: "#475569"
},
fontColor: {
default: "#cbd5e1",
active: "#ffffff",
focus: "#ffffff"
}
}
}
});

To change colors at runtime, for example when your application state changes, call updateOptions(). The states you pass are merged with the existing colors, so you only need to pass the states that change:

// Only the active background changes; every other color keeps its value
await view.updateOptions({
workspacePlatform: {
viewTab: {
backgroundColor: { active: "#dc2626" }
}
}
});

Because colors are merged, leaving out a state doesn't remove its color. To return a state to the theme color, set it to an empty string:

// Return the active background to the theme color
await view.updateOptions({
workspacePlatform: {
viewTab: {
backgroundColor: { active: "" }
}
}
});
caution

Choose background and text colors that contrast with each other in every state, in both light and dark themes.

Add interactive controls to view tabs​

You can add icon buttons, called controls, to a view tab. For example, you might add a status indicator, a notification badge, or a button that opens a color-channel picker. Declare the controls in workspacePlatform.viewTab.controls on the view options, and register a custom action to run when a user clicks one.

Each control has these properties:

PropertyRequiredDescription
iconUrlNoThe icon to display. Either a single URL, or an object with light and dark URLs. See Use theme-aware icons.
tooltipYesText shown on hover. Also used as the accessible name of the control.
actionYesThe custom action to run on click: { id, customData? }. The id must match a key in the customActions you pass to WorkspacePlatform.init().
positionNoWhere the control sits in the tab. Default: { relativeTo: 'title', placement: 'before' }. See Position controls.
disabledNoIf true, the control is displayed but can't be clicked or focused.

Each control provides a square space, 15×15 pixels, the same size as the favicon. Icons are displayed as you provide them; Browser doesn't tint them.

Controls have a fixed size. On a tab that is too narrow to fit all of its controls, the controls farthest to the right are cut off. To avoid this, set a minWidth that leaves room for your controls, or use content-based width.

Controls require the version 2 layout engine, which is the Browser default. If a window opts back in to the version 1 engine, its tabs don't show controls, and the controls option is ignored without an error.

Declare controls and handle clicks​

When a user clicks a control, Browser calls the matching custom action in your platform provider. The payload has callerType set to CustomActionCallerType.ViewTabControl and includes these properties:

PropertyDescription
viewIdentityThe identity of the view whose tab was clicked.
windowIdentityThe identity of the Browser window that contains the tab.
customDataThe customData from the control's action.
x, yThe client coordinates of the click, relative to the Browser window.

Controls don't have their own IDs. If several controls use the same action, use customData to tell them apart.

import * as WorkspacePlatform from "@openfin/workspace-platform";

await WorkspacePlatform.init({
browser: { /* ... */ },
customActions: {
"pin-view": async (payload: WorkspacePlatform.CustomActionPayload) => {
if (payload.callerType !== WorkspacePlatform.CustomActionCallerType.ViewTabControl) {
return;
}
const { viewIdentity, customData } = payload;
// Your logic, for example pin the view identified by viewIdentity
}
}
});

const platform = WorkspacePlatform.getCurrentSync();

await platform.createView({
url: "https://intranet.example.com/app",
workspacePlatform: {
viewTab: {
controls: [
{
iconUrl: "https://cdn.example.com/pin.svg",
tooltip: "Pin view",
action: { id: "pin-view", customData: { appId: "orders" } }
}
]
}
}
});

Clicking a control doesn't select or close the tab. As with the close button, pressing a control and dragging still moves the tab.

Position controls​

You can place a control before or after one of two anchors in the tab: the title or the favicon. Set position.relativeTo to 'title' or 'favicon', and set position.placement to 'before' or 'after'.

The tab contents are laid out in this order:

[before favicon] [favicon] [after favicon / before title] [title] [after title] [close]

When several controls share the same position, they are displayed in the order they appear in the controls array.

The favicon anchor still exists when the favicon is hidden, so a control's position stays the same.

controls: [
{
// Immediately to the left of the title
position: { relativeTo: "title", placement: "before" },
iconUrl: "https://cdn.example.com/status.svg",
tooltip: "Status",
action: { id: "show-status" }
},
{
// Immediately to the right of the title
position: { relativeTo: "title", placement: "after" },
iconUrl: "https://cdn.example.com/alert.svg",
tooltip: "Alerts",
action: { id: "show-alerts" }
},
{
// At the leading edge of the tab, before the favicon
position: { relativeTo: "favicon", placement: "before" },
iconUrl: "https://cdn.example.com/pin.svg",
tooltip: "Pin",
action: { id: "pin-view" }
}
]

Use theme-aware icons​

To display a different icon in light and dark color schemes, set iconUrl to an object with light and dark URLs. Browser switches icons when the color scheme changes. A single URL string is used in both schemes.

{
iconUrl: {
light: "https://cdn.example.com/pin-light.svg",
dark: "https://cdn.example.com/pin-dark.svg"
},
tooltip: "Pin",
action: { id: "pin-view" }
}

Update or remove controls at runtime​

To add, remove, or swap controls when your application state changes, call updateOptions() with a new controls array. Unlike backgroundColor, the controls array is replaced, not merged. Pass the complete list of controls you want the tab to show.

// Replace the tab's controls with a notification badge
await view.updateOptions({
workspacePlatform: {
viewTab: {
controls: [
{
iconUrl: "https://cdn.example.com/badge-3.svg",
tooltip: "3 new notifications",
action: { id: "show-notifications" }
}
]
}
}
});

// Remove all controls
await view.updateOptions({
workspacePlatform: { viewTab: { controls: [] } }
});

Open a menu from a control​

Browser doesn't provide a built-in dropdown for controls. To show a menu, color picker, or pop-up window, open it from your custom action. Use the x and y coordinates in the payload to position it next to the control.

customActions: {
"choose-channel": async (payload: WorkspacePlatform.CustomActionPayload) => {
if (payload.callerType !== WorkspacePlatform.CustomActionCallerType.ViewTabControl) {
return;
}
const browserWindow = fin.Window.wrapSync(payload.windowIdentity);
const { result, data } = await browserWindow.showPopupMenu({
template: [
{ label: "Red channel", data: "#dc2626" },
{ label: "Green channel", data: "#16a34a" },
{ label: "Blue channel", data: "#2563eb" }
],
x: payload.x,
y: payload.y
});

if (result === "clicked") {
await fin.View.wrapSync(payload.viewIdentity).updateOptions({
workspacePlatform: { viewTab: { backgroundColor: { default: data } } }
});
}
}
}

Right-click the tab title​

Right-clicking a view tab opens the view tab context menu. To add your own options to that menu, override openViewTabContextMenu. See Customize Browser features.

Close button visibility​

In HERE Core UI 45 or later, the close button (✕) on an inactive view tab is hidden until the user hovers over the tab or moves keyboard focus to it. The space the close button would use is available for the tab title. The active tab always displays its close button.

View tabs on locked pages keep their existing behavior: the close button is never shown. Controls you add to tabs on locked pages are still displayed and can still be clicked.

Look up the page and window for a view​

To find the page and window that contain a view, call platform.Browser.getPageByViewIdentity(). It returns the window identity and the page, including page.pageId and page.title, in a single call, so you don't need to walk the layout tree.

For example, you can use it to give every view tab on the same page the same color:

const pageColors: Record<string, string> = {
"trading-page": "#1e3a8a",
"research-page": "#14532d"
};

async function colorTabByPage(viewIdentity: OpenFin.Identity) {
const { windowIdentity, page } = await platform.Browser.getPageByViewIdentity(viewIdentity);
const color = pageColors[page.pageId];
if (!color) {
return;
}
await fin.View.wrapSync(viewIdentity).updateOptions({
workspacePlatform: { viewTab: { backgroundColor: { default: color } } }
});
}

Set defaults for all views and windows​

To apply the same tab customization to every view or window, set it in BrowserInitConfig when you call WorkspacePlatform.init():

  • defaultViewOptions.workspacePlatform.viewTab sets the default background colors, text colors, and controls for every view.
  • defaultWindowOptions.workspacePlatform.pageTabDimensions and viewTabDimensions set the default page tab and view tab widths for every window. viewTabDimensions also sets the default widthMode.

Values you pass when you create a view or window override these defaults.

await WorkspacePlatform.init({
browser: {
defaultWindowOptions: {
workspacePlatform: {
pageTabDimensions: {
minWidth: "30px",
maxWidth: "400px"
},
viewTabDimensions: {
widthMode: "content",
minWidth: "60px",
maxWidth: "200px"
}
}
},
defaultViewOptions: {
workspacePlatform: {
viewTab: {
controls: [
{
iconUrl: "https://cdn.example.com/pin.svg",
tooltip: "Pin view",
action: { id: "pin-view" }
}
]
}
}
}
},
customActions: {
"pin-view": async (payload) => { /* ... */ }
}
});

Snapshots​

The icon and workspacePlatform.viewTab values are saved with the view. They are included in the result of platform.getViewSnapshot() and are restored when a snapshot is applied.

const snapshot = await platform.getViewSnapshot(view.identity);
// snapshot.icon === "hide"
// snapshot.workspacePlatform.viewTab.backgroundColor.active === "#dc2626"

Controls are saved as configuration only. Your customActions handlers are not part of the snapshot, so they must be registered in WorkspacePlatform.init() whenever the platform starts.

Accessibility​

Controls are part of the existing keyboard navigation for view tabs:

  • Users reach controls with the arrow keys, in the same way as the close button. Controls are not separate Tab-key stops.

  • In the ViewTabElements keyboard navigation settings, controls are 'active-tab-custom-control' (controls on the active tab) and 'inactive-tab-custom-control' (controls on other tabs). If you provide your own arrowNavigation list, include these values so keyboard users can reach controls.

  • Enter or Space runs the control's action. It does not select or close the tab.

  • Each control has the role button and uses its tooltip as its accessible name.

  • Disabled controls are skipped by keyboard navigation.

  • The favicon is decorative and is hidden from screen readers. The tab's accessible name is still its title.

  • The close button on an inactive tab appears when the tab receives keyboard focus, so keyboard users can reach it.

  • In Windows high-contrast mode, system colors take precedence over custom tab colors.

Platforms that don't use Browser​

If you build your own platform windows or use HERE Core Web layouts instead of HERE Core UI Browser, you can add controls to view tabs with the experimental renderCustomTabControls option of fin.Platform.Layout.create(). It works like renderCustomHeaderControls: your callback receives each tab element, and you add and manage your own DOM nodes. For details, see Add custom controls to view tabs. The icon view option described in Override or hide the view tab icon is also available on those platforms. In HERE Core Web layouts, you can set icon in the layout or snapshot configuration only, not with updateOptions().