Collabora Online Design System
A living reference of the UI components, tokens, and patterns used in Collabora Online, extracted directly from the CSS source files under browser/css/.
Typography
The UI uses a multi-tier font system. The primary font for all chrome is --cool-font. Dialogs and form controls use --jquery-ui-font.
| Token | Value | Usage |
|---|---|---|
--default-font-size | 0.75rem (12px) | Body, labels, controls |
--medium-font-size | 0.875rem (14px) | Context menus, tooltips |
--header-font-size | 1rem (16px) | Section headers, doc title |
--tooltip-font-size | 0.875rem (14px) | Tooltip text |
--overflow-group-font-size | 8.5pt | Notebookbar group labels |
Color Palette
Every colour in Collabora Online is a CSS custom property. The light set lives in color-palette.css, the dark set in color-palette-dark.css as a [data-theme='dark'] override. Switch the tabs below to see the same token in both themes. The Dark tab lists the whole set, so a token the dark file does not override is shown with its inherited light value and an inherits light chip. Values that reference another token are printed as var(--token) → resolved.
Token data generated from online.mirror@eac73f4 on 2026-10-04.
86 tokens from color-palette.css.
Primary / brand
9--blue1-txt-primary-color--color-primary--color-primary-dark--color-primary-darker--color-primary-lighter--color-primary-text--green0-txt-primary-color--orange1-txt-primary-color--yellow0-txt-primary-colorSpreadsheet grid & headers
14--color-border-calc-header--color-border-calc-header-sheetview--color-calc-comment--color-calc-grid--color-calc-header--color-calc-header-hover--color-calc-header-selected--color-calc-header-sheetview--color-calc-header-sheetview-hover--color-calc-header-sheetview-selected--color-text-calc-header--color-text-calc-header-selected--color-text-calc-header-sheetview-selected--column-row-highlightText
8--color-annotation-line--color-date-text--color-hyperlink--color-main-text--color-status-badge--color-text-dark--color-text-darker--color-text-lightBackgrounds
14--color-background-dark--color-background-darker--color-background-document--color-background-hover--color-background-lighter--color-background-slideshow--color-background-tabs-group--color-backstage-background--color-backstage-background-dark--color-canvas--color-main-background--color-overlay--color-slideshow--color-stylesview-backgroundBorders
12--color-border--color-border-dark--color-border-dark-63--color-border-darker--color-border-input--color-border-input-dark--color-border-lighter--color-btn-border--color-btn-border-dis--color-quickfind-border--color-stylesview-border--color-toolbar-borderButtons
5--color-btn-danger-bg--color-btn-danger-hover-bg--color-btn-danger-outline--color-btn-primary-hover-bg--color-presenter-console-btn-hoverSemantic
3--color-error--color-success--color-warningScrollbars
2--color-scrollbar-railway--color-scrollbar-thumbShadows
3--color-box-shadow--color-box-shadow-dark--color-box-shadow-lightEditing helpers
8--color-cursor-blink-background--color-grid-helper-line-dashed--color-grid-helper-line-solid--color-insert-marker-background--color-insert-marker-background-dark--color-smart-guides-helper-line--color-treeview-highlight--color-treeview-highlight-textOther
4--color-status-error--color-status-linked--color-status-neutral--color-status-warningDocument type accents
4Each document type carries an RGB triplet, consumed as rgb(var(--doc-type)) for selected tab underlines, active toolbar buttons and progress bars.
Non-colour tokens
4| Token | Value | Note |
|---|---|---|
--brightness-stylesview | 1 | |
--opacity-icon-disabled | 0.65 | |
--opacity-text-disabled | 0.8 | a label has to stay readable while it is dimmed |
--scrollbar-color | #c0bfbc transparent |
87 tokens, 76 of them overridden by color-palette-dark.css; the rest are inherited from the light palette.
Primary / brand
9--blue1-txt-primary-color--color-primary--color-primary-dark--color-primary-darker--color-primary-lighter--color-primary-text--green0-txt-primary-color--orange1-txt-primary-color--yellow0-txt-primary-colorSpreadsheet grid & headers
14--color-border-calc-header--color-border-calc-header-sheetview--color-calc-comment--color-calc-grid--color-calc-header--color-calc-header-hover--color-calc-header-selected--color-calc-header-sheetview--color-calc-header-sheetview-hover--color-calc-header-sheetview-selected--color-text-calc-header--color-text-calc-header-selected--color-text-calc-header-sheetview-selected--column-row-highlightText
9--color-annotation-line--color-date-text--color-hyperlink--color-main-text--color-status-badge--color-text-dark--color-text-darker--color-text-light--color-text-lighterBackgrounds
14--color-background-dark--color-background-darker--color-background-document--color-background-hover--color-background-lighter--color-background-slideshow--color-background-tabs-group--color-backstage-background--color-backstage-background-dark--color-canvas--color-main-background--color-overlay--color-slideshow--color-stylesview-backgroundBorders
12--color-border--color-border-dark--color-border-dark-63--color-border-darker--color-border-input--color-border-input-dark--color-border-lighter--color-btn-border--color-btn-border-dis--color-quickfind-border--color-stylesview-border--color-toolbar-borderButtons
5--color-btn-danger-bg--color-btn-danger-hover-bg--color-btn-danger-outline--color-btn-primary-hover-bg--color-presenter-console-btn-hoverSemantic
3--color-error--color-success--color-warningScrollbars
2--color-scrollbar-railway--color-scrollbar-thumbShadows
3--color-box-shadow--color-box-shadow-dark--color-box-shadow-lightEditing helpers
8--color-cursor-blink-background--color-grid-helper-line-dashed--color-grid-helper-line-solid--color-insert-marker-background--color-insert-marker-background-dark--color-smart-guides-helper-line--color-treeview-highlight--color-treeview-highlight-textOther
4--color-status-error--color-status-linked--color-status-neutral--color-status-warningDocument type accents
4Set on [data-doctype] in color-palette.css. The dark palette does not override them, so the accents are the same in both themes.
Non-colour tokens
4| Token | Value | Note |
|---|---|---|
--brightness-stylesview | 0.9 | |
--opacity-icon-disabled | 0.65 | |
--opacity-text-disabled | 0.8 | a label has to stay readable while it is dimmed |
--scrollbar-color | #c0bfbc transparent |
Cursors
All cursor types used across Collabora Online, with live previews and links to source. There is no centralised cursor map in the codebase. Cursors are applied inline via style.cursor in TypeScript/JavaScript or via CSS classes. The Uses chip counts occurrences in browser/css/ + inline style.cursor assignments in browser/src/. The Source link points at one representative declaration at a pinned upstream SHA.
main when this page was built (SHA eac73f4) so line numbers stay accurate; the pipeline re-pins to the latest commit on every run.
Standard CSS cursors
| Cursor | Preview | Description | Uses | Source |
|---|---|---|---|---|
auto | hover | Browser chooses cursor automatically | 3 | cool.css:233 |
cell | hover | Cell selection in spreadsheets | 1 | spreadsheet.css:2 |
col-resize | hover | Column width resize | 2 | cool.css:266 |
context-menu | hover | Context menu trigger | 1 | jsdialogs.css:747 |
crosshair | hover | Crosshair selector (auto-fill in spreadsheets) | 4 | leaflet.css:169 |
default | hover | Standard arrow (buttons, toolbar, panels) | 20 | jquery-ui-lightness.css:61 |
e-resize | hover | Resize from the east (right) edge | 5 | jquery-ui-lightness.css:613 |
ew-resize | hover | Horizontal resize (east/west) | 1 | leaflet.css:187 |
grab | hover | Hand ready to grab/drag. Used only as SVG fallback in this codebase, see grab.svg below. | 2 | ShapeHandleAnchorSubSection.ts:30 |
grabbing | hover | Hand actively dragging | 1 | leaflet.css:220 |
help | hover | Help/explainer cursor (info tooltips, AI chat sidebar) | 1 | aichat-sidebar.css:212 |
inherit | hover | Inherit cursor from parent element | 1 | cool.css:946 |
move | hover | Move/drag element (four-arrow) | 7 | leaflet.css:165 |
n-resize | hover | Resize from north (top) edge | 1 | jquery-ui-lightness.css:599 |
ne-resize | hover | Resize from north-east corner | 1 | jquery-ui-lightness.css:648 |
nesw-resize | hover | Diagonal resize (NE / SW) | 1 | leaflet.css:183 |
not-allowed | hover | Action not permitted (disabled buttons) | 5 | backstage.css:166 |
ns-resize | hover | Vertical resize (north/south) | 1 | leaflet.css:195 |
nw-resize | hover | Resize from north-west corner | 1 | jquery-ui-lightness.css:641 |
nwse-resize | hover | Diagonal resize (NW / SE) | 1 | leaflet.css:191 |
pointer | hover | Interactive element (buttons, links) | 78 | selectionMarkers.css:29 |
row-resize | hover | Row height resize | 1 | spreadsheet.css:228 |
s-resize | hover | Resize from south (bottom) edge | 2 | jquery-ui-lightness.css:606 |
se-resize | hover | Resize from south-east corner (table fill) | 2 | jquery-ui-lightness.css:627 |
sw-resize | hover | Resize from south-west corner | 1 | jquery-ui-lightness.css:634 |
text | hover | Text input / text selection (I-beam) | 4 | jquery-ui-lightness.css:862 |
w-resize | hover | Resize from west (left) edge | 4 | jquery-ui-lightness.css:620 |
Custom SVG cursors
Three product-specific cursors shipped as SVG assets in browser/images/cursors/.
| Cursor | SVG | Preview | Description | Uses | Source |
|---|---|---|---|---|---|
crop.svg |
hover |
Image cropping tool, shown when dragging a shape's scaling handle in crop mode | 1 | ||
fill.svg |
hover |
Auto-fill brush, used for paint formatting and the spreadsheet fill handle | 2 | ||
grab.svg |
hover |
Custom grab hand, used for the shape anchor and custom shape handles | 2 |
Cursor declarations live across spreadsheet.css (spreadsheet-specific), leaflet.css (map/pan resize), jquery-ui-lightness.css (UI widget resize), and inline in browser/src/canvas/sections/ (shape/canvas handles).
Anatomy
How each custom cursor is constructed: hotspot, fill, stroke, and the surrounding clear region. The three Figma frames below show the anatomy and the production conventions used across the cursor set.
Infographics
Illustrative SVGs that visualise a choice or state for the user. Distinct from cursors (functional pointer feedback) and toolbar icons (which live in the LibreOffice icon themes). Infographics live next to the surface that uses them, e.g. browser/admin/images/ for the integrator settings panel.
UI mode selector (integrator settings)
Shown in the per-user settings dialog under Customization, where the user picks between the Notebookbar and Compact toolbar layouts. The two illustrations are paired as clickable cards; the active card gets the .selected ring (blue tint + border). Rendered at 300px wide, scaling responsively. See AdminIntegratorSettings.ts:1443 (renderCompactModeToggle) and adminIntegratorSettings.css:669 for the styling.
Notebookbar.svg
Tabbed ribbon-style toolbar (the default). Shows a coloured tab strip with one tab active, plus a grid of icon-sized rectangles standing in for the per-tab tool clusters.
Compact.svg
Classic single-row toolbar with a menubar above (the “Compact” mode). Mirrors the Notebookbar illustration's footprint so the two cards align side-by-side.
Conventions for new infographics: author at the rendered display size (here 900×600 viewBox, displayed at 300×200), keep the Collabora primary blue #0369a2 for emphasis strokes, use the neutral greys (#e6e6e6, #f2f2f2, #f9f9f9) for the chrome, and pair illustrations that share a footprint so they can be compared directly.
Icons
The Collabora Online icon theme started as Colibre, designed by Andreas Kainz. Hundreds of additions and tweaks from many contributors have landed since, and the set no longer follows the original Colibre conventions in every detail. So the reference for what an icon should look like today is the current Collabora Online guidelines below:
Icons per Application
Which icons appear in which application, grouped by UI surface, so a designer can quickly see the icon set each app exposes. The same icon may repeat across apps — that is expected. Icons load from the online.mirror at the pinned build commit. The Theme tabs swap between browser/images and browser/images/dark, which is the same directory swap COOL does in dark mode, so the dark set can be checked on a dark surface; an icon with no dark counterpart keeps its light asset and is outlined. The compact toolbar is the shared top toolbar (per-app visibility varies); sidebar panel membership is approximate (the exact deck is configured in core).
Icon data generated from online.mirror@eac73f4 on 2026-10-04.
Components
Primary Button
The main call-to-action button, used for the default action in dialog boxes (e.g., "OK", "Apply"). Class: .button-primary
| Property | Value |
|---|---|
| Height | 32px |
| Min Width | 62px |
| Padding | 0 16px |
| Border Radius | var(--border-radius) = 4px |
| Border | 1px solid #0063b1 |
| Background | #0b87e7 (--color-primary) |
| Text Color | #fff |
| Font Size | 0.75rem (12px) |
| Margin | 5px |
Secondary Button
Used for cancel, close, and non-primary dialog actions. Also the default style for all generic <button> elements in dialogs. Class: .button-secondary
| Property | Value |
|---|---|
| Height | 32px |
| Min Width | 62px |
| Padding | 0 16px |
| Border Radius | 4px |
| Border | 1px solid #cecece (--color-border-dark) |
| Background | #e8e8e8 (--color-background-dark) |
| Text Color | #333333 |
| Margin | 5px |
Icon Toolbar Button
Used in the toolbar and notebookbar for icon-only actions (Bold, Italic, Underline, etc.). The selected state is tinted with the document-type accent color. Class: .unotoolbutton
| Property | Value |
|---|---|
| Size (button) | 28px × 28px (--btn-size) |
| Icon Size | 24px × 24px (--btn-img-size) |
| Padding | 2px (--btn-padding) |
| Border Radius | 4px |
| Border (default) | 1px solid transparent |
| Border (hover) | 1px solid #b6b6b6 (--color-btn-border) |
| Background (hover) | #f1f1f1 (--color-background-tabs-group) |
| Selected Background | rgba(var(--doc-type), 0.1) |
| Selected Border | 1px solid rgb(var(--doc-type)) |
| Disabled Opacity | 0.65 |
| Margin (in toolbar) | auto 2px |
Menu Button
A button with a dropdown arrow, used for selection controls in dialogs and sidebars. Class: .jsdialog.menubutton
| Property | Value |
|---|---|
| Padding | 8px 10px |
| Border Radius | 4px |
| Border | 1px solid #cecece (--color-border-dark) |
| Background | #e8e8e8 (--color-background-dark) |
| Font Size | calc(0.75rem * 1.2) (~14.4px) |
| Font Family | --jquery-ui-font |
| Arrow | CSS triangle: 4px border, 5px top |
Text Input
Standard single-line text field used in dialogs. Class: .jsdialog.ui-edit
| Property | Value |
|---|---|
| Height | 32px |
| Line Height | 28px |
| Border Radius | 4px |
| Border | 1px solid #cecece (--color-border-dark) |
| Background | #e8e8e8 (--color-background-dark) |
| Padding Inline | 4px |
| Focus border | #0b87e7 (--color-primary) |
Spinfield
Numeric input with step controls, used for measurements and quantities. Class: .spinfieldcontainer .spinfield
| Property | Value |
|---|---|
| Height | 28px |
| Min Width | 70px |
| Border Radius | 4px |
| Border | 1px solid #cecece |
| Background | #e8e8e8 |
| Padding | 4px 0 4px 4px |
| Container margin-end | 5px |
Checkbox
Custom-styled checkbox with SVG icons in the actual app. Class: .jsdialog.checkbutton
| Property | Value |
|---|---|
| Checkbox Size | 20px × 20px |
| Checkbox Margin | 1px 8px |
| Border Radius | 3px |
| Label Font Size | 0.75rem (12px) |
| Label Line Height | 28px |
Radio Button
Mutually exclusive option selector. Class: .radiobutton.jsdialog
| Property | Value |
|---|---|
| Radio Size | 18px × 18px |
| Radio Margin | 1px 8px |
| Dot Size (checked) | 8px |
| Border Radius | 50% |
Select / Listbox
Native select dropdown used in dialogs and sidebar. Class: .ui-listbox-container .ui-listbox
| Property | Value |
|---|---|
| Height (dialog) | 32px |
| Height (toolbar) | 24px (1.5rem) |
| Min Width | 70px (toolbar) / 100px (dialog) |
| Border Radius | 4px |
| Border | 1px solid #cecece |
| Background | #e8e8e8 |
| Padding | 0 14px 0 4px |
Combobox
Editable input combined with a dropdown list. Class: .ui-combobox
| Property | Value |
|---|---|
| Height (dialog) | 32px |
| Border Radius | 4px |
| Border | 1px solid #b6b6b6 (--color-border) |
| Input Background | #fff (--color-background-lighter) |
| Button width | 28px |
Tabs
Three tab variants exist, all marking selection with a doc-type accent. Dialog tabs colour the selected label in the doc-type colour and underline it. Notebookbar tabs keep the label colour and mark the selected tab with an inset bottom border. The vertical rail is the dialog variant for tab counts that no longer read as a row: the tabs stack on the inline-start side and the underline becomes a left-edge accent bar.
Dialog Tabs
The tabcontrol that splits a dialog into tab pages. It has two layouts: a horizontal row of tabs above the tab page, and a vertical rail beside it.
The tabcontrol picks the layout itself, so a designer does not choose between the two. It takes the rail when either holds:
- it has more than four tabs (
VERTICAL_TABS_THRESHOLDin Widget.TabControl.tsx) — four is the largest count that still reads as a row; or - one of its pages offers fill types, whatever the tab count. Slide Properties has three tabs and takes the rail, because its Background page is one of those. The page is recognised by the
lbnone(“None”) tab of its fill row, and gets the fill checkbox described below.
A tabcontrol nested inside a tab page never takes the rail. The rail belongs to the primary tabs of the dialog, so the fill types on the Area page keep their row however many of them there are.
| Property | Dialog Tab (horizontal) |
|---|---|
| Height | 34px (--header-height) |
| Border | 1px solid transparent, radius var(--border-radius) |
| Font | var(--cool-font), var(--default-font-size); the selected tab keeps the same weight |
| Label colour | var(--color-text-dark) |
| Padding | 0 1em; the first visible tab drops to padding-inline-start: 0 |
| Margin | 0 12px 4px 0 (margin-inline-end: 12px, margin-block: 0 4px) |
| Selected background | var(--color-background-lighter) |
| Selected text colour | rgba(var(--doc-type), 1); in dark mode var(--color-main-text), with the underline still doc-type |
| Selected indicator | underline 2px solid rgba(var(--doc-type), 1), offset 6px |
| Hover | rgba(var(--doc-type), 1) on var(--color-background-darker) |
| Focus | outline: 2px solid var(--color-primary-dark), offset -2px (inset, so it cannot escape the tab) |
| Hidden tab | display: none — a tab dropped by context takes no space |
Rail and panel are two columns of one grid row, so the tab control is as tall as the taller of the two: the rail stays fully visible when the selected page is short, and a tall page stretches the rail to the full height, which keeps the divider running the whole way down. The panel column is auto rather than 1fr, so it sizes the dialog to the widest page and still stretches into free space; the dialog measures each tab page from the panel exactly as it does in the horizontal layout.
| Property | Vertical Rail |
|---|---|
| Layout | display: grid, grid-template-columns: var(--dialog-tab-rail-width) auto; rail and panel both in grid-row: 1 |
| Rail width | var(--dialog-tab-rail-width) (150px) |
| Rail height | 100% of the row, overflow-y: auto once the tabs outgrow it |
| Rail divider | border-inline-end: 1px solid var(--color-border-lighter) |
| Gap between tabs | 2px |
| Tab height | auto, min-height: var(--header-height) (a wrapped label grows the row) |
| Tab padding | 6px block, 8px inline |
| Tab alignment | justify-content: flex-start, text-align: start |
| Selected indicator | border-inline-start: 3px solid rgba(var(--doc-type), 1) |
| Selected corner | 0 var(--border-radius) var(--border-radius) 0 |
| Panel | grid-column: 2, padding-left: 16px |
Only the inline-start edge of a vertical tab carries a border, and every tab carries it transparent, so selecting one neither shifts it sideways nor lets another edge miter into the accent and cut its ends into slants. Long labels wrap inside the fixed width rather than overflowing it.
The fill checkbox and the disabled tab
A page that offers fill types carries a checkbox on its rail row, so the rail says whether that page is filled next to its name. Clearing the checkbox sets the fill to None and leaves the page; the tab then goes dim and stops responding, because there is nothing on the page left to edit. Setting the checkbox opens the page again on the fill type that was showing before. The None type is no longer a tab of the fill row at all: it is hidden, and the checkbox stands in for it.
The dim tab is marked aria-disabled="true" rather than being a disabled button, so it stays reachable by keyboard and an assistive technology can read its name. Hovering it rings the checkbox beside it, which is the one control still live on that row, and shows the tooltip “Add a fill to edit the Area” — naming what the user gains rather than which widget to press. The label is substituted, so the same row serves the Background tab of Slide Properties and the Area tab of a shape or page style.
| Property | Fill row |
|---|---|
| Row | .ui-tab-row, display: flex, align-items: center, role="presentation" |
| Tab in the row | flex: 1, min-width: 0 (so the label still wraps) |
| Checkbox | flex: none, trailing edge of the row, margin-inline-end: var(--spacing-m) (8px) |
| Disabled tab | color: var(--color-text-light), transparent background, cursor: default; no hover response |
| Disabled marker | aria-disabled="true" on the tab, never the disabled attribute |
| Checkbox hint ring | outline: 2px solid var(--color-primary-dark), offset 1px, while the row of a disabled tab is hovered |
| Tooltip | data-cooltip on the row: Add a fill to edit the {0}, {0} = the tab label |
| Hidden “None” tab | display: none plus aria-hidden="true" |
In dark mode the disabled tab comes out brighter than a live one. The rule reaches for --color-text-light, which the dark palette defines as #fff, while an enabled tab is --color-text-dark (#e8e8e8). The inversion is in the palette, not in the tab, so it dims every disabled label the same way and belongs to a palette fix.
The hint ring on the checkbox is --color-primary-dark, the same colour the tab focus ring uses, so the two read alike at a glance. A doc-type ring would keep them apart.
The tooltip is attached to the row, so hovering the checkbox shows the hint written for the tab beside it.
Notebookbar Tabs
The tab row across the top of the notebookbar, switching between the command groups of one document. It is always horizontal, whatever the tab count.
| Property | Notebookbar Tab |
|---|---|
| Height | 34px (full nav height) |
| Border Radius | 0 (none) |
| Padding | 0 clamp(4px, 1vw, 1em) |
| Selected text colour | unchanged label colour |
| Selected indicator | inset 0 -2px rgb(var(--doc-type)) |
| Margin | 0 |
TreeView
Scrollable list of entries with optional icons, used in dialogs for file browsers, style lists, etc. Class: .ui-treeview
| Property | Value |
|---|---|
| Border | 1px solid #b6b6b6 |
| Border Radius | 4px |
| Min Size | 150px × 100px |
| Max Height (dialog) | 150px |
| Line Height | 24px |
| Icon Size | 16px (--btn-img-size-s) |
| Selected Background | #0b87e7 (--color-primary) |
| Hover Background | #c0bfbc (--color-background-darker) |
Context Menu
Right-click popup menu. Class: .context-menu-list
| Property | Value |
|---|---|
| Border | 1px solid #cecece |
| Border Radius | 4px |
| Box Shadow | 0 0 3px rgba(77,77,77,0.5) |
| Item Font Size | 0.875rem (14px) |
| Item Padding | 5px 22px |
| Separator | 1px solid #f1f1f1 |
Tooltip
Hover tooltip for toolbar buttons and controls. Class: .cooltip-text
| Property | Value |
|---|---|
| Padding | 7px 9px |
| Border Radius | 6px |
| Box Shadow | 0 0 4px rgba(77,77,77,0.5) |
| Font Size | 0.875rem (14px) |
| Background | #fff |
| Z-index | 2147483647 (max) |
Progress Bar
Thin progress indicator tinted with the document-type accent. Class: .ui-progressbar
Writer (blue) 65%
Calc (green) 40%
Impress (orange) 80%
| Property | Value |
|---|---|
| Height | 5px |
| Track Color | rgba(var(--doc-type), 0.2) |
| Bar Color | rgb(var(--doc-type)) |
| Border Radius | 3px |
Color Picker
Grid of color swatches for font/background color selection. Class: .ui-color-picker
| Property | Value |
|---|---|
| Swatch Size | 16px × 16px |
| Grid Gap | 4px |
| Padding | 4px |
| Hover Outline | 2px solid #0b87e7, offset 1px |
Badge
Small status indicator used in the status bar. Class: .ui-badge
| Property | Value |
|---|---|
| Padding | 5px |
| Border Radius | 4px |
| Background | #c0bfbc (--color-background-darker) |
| Font Size | 0.75rem (12px) |
Separator
Visual dividers for toolbars and dialogs.
Horizontal separator:
| Property | Vertical | Horizontal |
|---|---|---|
| Size | 1px × 14px | 100% × 1px |
| Color | #c0bfbc | #f1f1f1 |
| Margin | 0 3px | 0 |
Snackbar
A notification bar at the bottom of the document, for a message that does not
interrupt the work. One bar shows at a time and the rest queue behind it. Built by
Util.SnackbarController.ts
and styled in jsdialogs.css.
Class: .snackbar. The bar is #323232 in the light and the dark theme
alike, so nothing in it reacts to the theme.
| Property | Value | Token |
|---|---|---|
| Bar background | #323232, both themes | hardcoded, no token yet |
| Bar radius | 4px | --border-radius |
| Bar shadow | 0 2px 8px rgba(0,0,0,.25) | none |
| Bar minimum height | 48px | from padding plus the 32px button row |
| Padding | 8px, and 12px at the label edge | --spacing-m |
| Gap between children | 8px | --spacing-m |
| Label colour and size | #fff, 1rem | --header-font-size |
| Action label | #83beec, weight 600, 1rem | --color-primary-lighter |
| Button hover | #ffffff15 | none |
| Focus ring | 2px solid rgb(var(--doc-type)), offset 2px | --doc-type |
| Icon dismiss button | 28px round, border-radius: 50% | --btn-size |
| Icon | lc_cancel.svg at 16px, painted white by filter: brightness(0) invert(1) | none |
| Text dismiss button | the string "Dismiss", no icon | none |
| Default timeout | 10 s. A timeout of -1 keeps the bar up until it is dismissed | none |
Contrast on the bar: the white label is 12.8:1 and the action colour is 6.4:1,
both well past the 4.5:1 of WCAG 1.4.3. The focus ring is the weak point, because
rgb(var(--doc-type)) measures 1.8:1 in Calc, 2.0:1 in Impress, 2.2:1 in Writer and
2.5:1 in Draw against #323232, where WCAG 1.4.11 asks for 3:1.
Two rules when styling it. First, the dismiss control is a
button inside a wrapper div, and
Widget.PushButton.ts
gives the wrapper the control id and the button that id plus -button, so both
elements end in "button". Select the button by its .ui-pushbutton class, because an
[id$='button'] selector also matches the wrapper and paints a square behind the round
button. Second, the generic desktop hover in
device-desktop.css
excludes .snackbar * on purpose: without that exclusion it paints every snackbar
button --color-background-lighter, which is white in the light theme, and the white
icon disappears into it.
Accessibility, still open. The 10 s dismissal does not pause on hover or on focus, which WCAG 2.2.1 asks for at level A. The action button takes its accessible name from the message rather than from its own label, which fails WCAG 2.5.3. The icon dismiss is 28px where WCAG 2.5.5 asks for 44px, and the desktop component is used on phones too. The focus ring contrast above fails WCAG 1.4.11.
Status bar
The bar pinned at the bottom of the document viewport. Hosts document-wide indicators (page number, zoom, word count, language, sheet selector in Calc, slide indicator in Impress) and the user presence list for collaborative editing. See the Figma reference for the canonical layout.
Dialogs
Collabora Online uses a single dialog framework (jsdialog, see Control.JSDialog.js + jsdialogs.css) but the framework hosts three behaviourally distinct kinds of surface, classified by two dimensions: whether the backdrop is dimmed (lightbox / nonlightbox) and whether background interaction is blocked (modal / nonmodal / non-dialog). See the classification thread on the forum for the original rubric.
| Kind | Backdrop | Background interaction | Movable | Outside click | CSS marker |
|---|---|---|---|---|---|
| Modal | Lightbox (dimmed) | Blocked | No | Closes only if .cancellable (info dialogs) | .jsdialog-window.modalpopup + .jsdialog-overlay |
| Nonmodal | Nonlightbox | Allowed | Yes (drag titlebar) | Does not close | .jsdialog-window:not(.modalpopup) |
| Popover | Nonlightbox | Allowed | No (anchored) | Closes | popupParent: '_POPOVER_' on the JSDialog instance |
A dialog whose body needs more than one page splits it with the dialog tabcontrol, specified under Dialog Tabs. It uses a horizontal tab row up to four tabs and the vertical rail beyond that, or the rail at any tab count when one of its pages offers fill types.
Anatomy
Every dialog (regardless of kind) shares the same three regions: a title bar with the title and a close affordance, a body with the content, and a button area at the foot. Popovers are the exception, because they have no title bar or button area and are just a body anchored to the trigger.
Body content with the inputs, labels and options the dialog needs to ask the user about.
- Title bar. Short noun phrase naming the dialog. On nonmodal dialogs the title bar is also the drag handle.
- Close affordance. The × on the right; Esc is the keyboard equivalent. For destructive dialogs prefer an explicit Cancel button.
- Body. A single column. Group related fields with a separator or a fieldset, not visual frames.
- Secondary actions (left). Help, Reset, advanced toggles. Never put a destructive action here.
- Primary action (right). The default action sits rightmost (Western convention; mirrored in RTL). Cancel sits just left of the primary action.
Modal (lightbox)
A dimmed backdrop blocks the document underneath. The dialog is centred and cannot be moved. Use when: the user must commit to a decision before they can keep working (settings, confirmations, blocking errors). Outside-click closes only when the dialog has the cancellable flag (typical for purely informational dialogs).
You have unsaved changes in Untitled 1. Do you want to save them before closing?
| Example | Dialog id / class | Source |
|---|---|---|
| About dialog | #modal-dialog-about-dialog-box |
jsdialogs.css:159 |
| Online Help | #modal-dialog-online-help-content-box |
jsdialogs.css:160 |
| Keyboard Shortcuts | #modal-dialog-keyboard-shortcuts-content-box |
jsdialogs.css:161 |
| Busy indicator | busypopup (type: modalpopup) |
Control.UIManager.ts:488 |
| LibreOffice native dialogs (Format Cells, Insert Special Character, …) | .lokdialog_container.modalpopup |
jsdialogs.css:26 |
Nonmodal (draggable)
There is no backdrop, so the document underneath stays interactive. The dialog can be dragged by its title bar and stays open until the user dismisses it. Use when: the dialog is a tool the user keeps glancing at while editing (Find & Replace, Accept/Reject Changes, Sort criteria). Outside clicks do not close it.
3 pending changes by Caolán. Drag this dialog out of the way while you review the document.
| Example | Dialog id / class | Source |
|---|---|---|
| Accept / Reject Changes | AcceptRejectChangesDialog |
jsdialogs.css:62 |
| Sort Key Window (Sort, Calc) | SortKeyWindow |
jsdialogs.css:199 |
Note: the nonmodal surface is the rarest of the three. Most older floating tool palettes (Find & Replace, advanced filters) still come from the LibreOffice native dialog path and so behave nonmodally without explicitly opting in. New work tends to land as either modal jsdialog or popover.
Popover (non-dialog)
An anchored panel for inline pickers and dropdowns, with content only and no title bar or button area. Opted-in via the JSDialog property popupParent: '_POPOVER_' (and clickToClose: '_POPOVER_'). Use when: the user is making a small bounded choice tied to a specific control (font color, autocomplete suggestion, autofilter values). Outside click dismisses.
| Example | Dialog id / class | Source |
|---|---|---|
| Auto-complete popup | AutoCompletePopup |
AutoCompletePopup.ts:56 |
| Content-control dropdown (Writer) | contentControlModalpopup |
ContentControlDropdownSubSection.ts:94 |
| Formula usage popup (Calc) | extends AutoCompletePopup |
Control.FormulaUsagePopup.ts:35 |