Custom Canvas UI
Custom Canvas is a feature that lets your users build their own dashboards inside your product. This page covers styling and translating its interface; see also Deploying Custom Canvas.
The UI components powering Custom Canvas (panels, forms, buttons, cards) are built on Remarkable UI (opens in a new tab). These components are currently fixed - you can't swap them out or modify their behavior - but you can style them to match your brand using CSS variables.
How to Style Custom Canvas
Add variables to theme.styles in your theme:
// embeddable.theme.ts
const themeProvider = (clientContext: any, parentTheme: Theme): Theme => {
return defineTheme(parentTheme, {
styles: {
"--em-sem-background": "#f8f9fa",
"--em-sem-text": "#1a1a2e",
},
});
};For full theming details, see Theming Remarkable Pro and How Styling Works.
Translating the Interface
Custom Canvas ships its labels in English - "Add a chart", "Select a template", validation messages, and so on. The drill-down modal does the same. You can replace any of them from your theme, under theme.embeddableApp.i18n.
This is separate from Remarkable Pro's internationalization, which lives under theme.i18n and covers your components. theme.embeddableApp belongs to the platform itself, so it works the same way whether or not you use Remarkable Pro.
How to translate
// embeddable.theme.ts
const themeProvider = (clientContext: any, parentTheme: Theme) => {
return defineTheme(parentTheme, {
embeddableApp: {
i18n: {
language: clientContext?.language || "en",
translations: {
de: {
customCanvas: {
addChart: "Diagramm hinzufĂĽgen",
templateSelection: {
title: "Vorlage auswählen",
populateTitle: "{{templateName}} ausfĂĽllen",
},
validation: {
valueRequired: "Wert ist erforderlich",
},
},
drillDown: {
step: "Schritt {{number}}",
},
},
},
},
},
});
};Things to notice
embeddableApp.i18n.languageselects the active catalogue, and defaults toen. The platform reserves no Client Context fields of its own, so map your own locale field onto it, as above.- Translate only the keys you need. Anything you leave out falls back to the built-in English, so a partial catalogue is a valid catalogue.
{{...}}are placeholders, filled in when the label is rendered. You can move them:Populate {{templateName}}becomes{{templateName}} ausfĂĽllenin German. Never assemble a label by joining pieces together - word order differs between languages.- Each
<em-beddable>resolves its own labels, so several embeddables on one page can run different languages.
To try it in the Builder, add presets to your client-contexts.cc.yml:
# src/embeddable.com/presets/client-contexts.cc.yml
- name: German
clientContext:
language: de
- name: Spanish
clientContext:
language: esSwitching between them swaps the language of the whole interface:

Changing the English wording
The built-in English is a catalogue like any other. If you only want the interface to match your product's terminology, override it without translating anything:
embeddableApp: {
i18n: {
language: "en",
translations: {
en: {
customCanvas: { addChart: "Add a widget" },
},
},
},
},Your en catalogue also acts as the base for every other language. A label you reword there shows up in German too, unless your de catalogue gives it its own value - so terminology changes only need writing once.
Labels resolve in this order:
- the catalogue for
language - your
encatalogue - the built-in English
What isn't translated here
- Template names and descriptions are authored in the Builder and are shown exactly as written there.
- Your components' own labels - titles, axis labels, column headers and data values - belong to your component library. On Remarkable Pro, translate them under
theme.i18n.
Type safety
@embeddable.com/core exports the types for this namespace, so your editor autocompletes the keys and a mistyped key fails the build rather than silently rendering English:
// embeddable.theme.ts
import { defineTheme, EmbeddableTheme } from "@embeddable.com/core";
import { Theme } from "@embeddable.com/remarkable-pro";
type AppTheme = Theme & EmbeddableTheme;
const themeProvider = (clientContext: any, parentTheme: Theme): AppTheme =>
defineTheme<AppTheme>(parentTheme, {
embeddableApp: { i18n: { /* ... */ } },
});
export default themeProvider;Requires @embeddable.com/core 2.15.0 or later.
Key reference
Keys are grouped by the part of the interface they appear in. The English value is what renders when you don't provide a translation.
Canvas
| Key | English default |
|---|---|
customCanvas.addChart | Add a chart |
customCanvas.editDashboard | Edit dashboard |
customCanvas.done | Done |
customCanvas.emptyStateTitle | Start building your dashboard |
customCanvas.loading | Loading your dashboard |
customCanvas.editingUnavailableOnSmallScreens | Editing is only available on larger screens. |
Template selection
| Key | English default |
|---|---|
customCanvas.templateSelection.title | Select a template |
customCanvas.templateSelection.populateTitle | Populate {{templateName}} |
customCanvas.templateSelection.search | Search templates |
customCanvas.templateSelection.selectTemplate | Select {{templateName}} template |
customCanvas.templateSelection.add | Add |
customCanvas.templateSelection.noResultsTitle | 0 results |
customCanvas.templateSelection.noResultsMessage | Clear your search and try again. |
customCanvas.templateSelection.goBack | Go back |
customCanvas.templateSelection.close | Close |
customCanvas.templateSelection.loadingForm | Loading form… |
customCanvas.templateSelection.formUnavailable | Form unavailable |
Configurator
defaultCategory is the heading shown above inputs whose component declares no category of its own.
| Key | English default |
|---|---|
customCanvas.configurator.cancel | Cancel |
customCanvas.configurator.addToDashboard | Add to dashboard |
customCanvas.configurator.saveChanges | Save changes |
customCanvas.configurator.previewPlaceholder | Populate the required fields to see a preview |
customCanvas.configurator.required | Required |
customCanvas.configurator.selectDataset | Select a dataset |
customCanvas.configurator.selectField | Select {{field}} |
customCanvas.configurator.booleanTrue | True |
customCanvas.configurator.booleanFalse | False |
customCanvas.configurator.moreOptions | More options |
customCanvas.configurator.lessOptions | Less options |
customCanvas.configurator.defaultCategory | Chart settings |
Dimension and measure fields
| Key | English default |
|---|---|
customCanvas.fields.selectDimension | Select a dimension |
customCanvas.fields.selectMeasure | Select a measure |
customCanvas.fields.selectDimensionOrMeasure | Select a dimension or measure |
customCanvas.fields.selectGranularity | Select a granularity |
customCanvas.fields.selectGranularities | Select granularities |
customCanvas.fields.granularity | Granularity |
customCanvas.fields.selectDatasetFirst | Select a dataset first |
customCanvas.fields.noDimensions | No dimensions available |
customCanvas.fields.noMeasures | No measures available |
customCanvas.fields.noDimensionsOrMeasures | No dimensions or measures available |
Validation messages
| Key | English default |
|---|---|
customCanvas.validation.valueRequired | Value is required |
customCanvas.validation.filterRequired | Filter is required |
customCanvas.validation.operatorRequired | Operator is required |
customCanvas.validation.sortByRequired | Sort by is required |
customCanvas.validation.limitRequired | Limit is required |
customCanvas.validation.limitNotANumber | Must be a valid number |
customCanvas.validation.limitBelowMinimum | Minimum limit is {{min}} |
customCanvas.validation.limitAboveMaximum | Limit can not exceed {{max}} |
customCanvas.validation.filtersHaveErrors | One or more filters have validation errors |
customCanvas.validation.sortsHaveErrors | One or more sorts have validation errors |
Widget actions
| Key | English default |
|---|---|
customCanvas.widgetActions.edit | Edit widget |
customCanvas.widgetActions.clone | Clone widget |
customCanvas.widgetActions.delete | Delete widget |
customCanvas.widgetActions.resize | Resize widget |
Duplicate dialog
| Key | English default |
|---|---|
customCanvas.duplicateDialog.title | Duplicate this chart? |
customCanvas.duplicateDialog.message | This will create a copy of the selected chart with the same settings. You can rename and adjust it by clicking edit. |
customCanvas.duplicateDialog.confirm | Yes, duplicate |
customCanvas.duplicateDialog.cancel | No, cancel |
Dataset settings
| Key | English default |
|---|---|
customCanvas.datasetSettings.filter | Filter |
customCanvas.datasetSettings.sort | Sort |
customCanvas.datasetSettings.limit | Limit |
customCanvas.datasetSettings.addFilter | Add Filter |
customCanvas.datasetSettings.addSort | Add Sort |
customCanvas.datasetSettings.filterPlaceholder | Add a filter |
customCanvas.datasetSettings.selectOperator | Select operator |
customCanvas.datasetSettings.selectValue | Select value |
customCanvas.datasetSettings.selectTime | Select time |
customCanvas.datasetSettings.selectRange | Select range |
customCanvas.datasetSettings.enterValue | Enter value |
customCanvas.datasetSettings.enterLimit | Enter limit |
customCanvas.datasetSettings.sortBy | Sort by |
customCanvas.datasetSettings.sortAscending | Sort ascending |
customCanvas.datasetSettings.sortDescending | Sort descending |
customCanvas.datasetSettings.selectSortByFirst | Select sort by first |
customCanvas.datasetSettings.deleteFilter | Delete |
customCanvas.datasetSettings.deleteSort | Delete sort |
customCanvas.datasetSettings.filterNotInDataModel | The filter "{{member}}" is not included in the selected data model |
customCanvas.datasetSettings.sortNotInDataModel | The sort "{{sortBy}}" is not included in the selected data model |
Relative date presets
These are the labels shown in filter dropdowns. The underlying values are sent to your data model unchanged, so translating a label doesn't affect filtering.
| Key | English default |
|---|---|
customCanvas.datasetSettings.timePresets.today | Today |
customCanvas.datasetSettings.timePresets.yesterday | Yesterday |
customCanvas.datasetSettings.timePresets.oneWeekAgo | 1 week ago |
customCanvas.datasetSettings.timePresets.oneMonthAgo | 1 month ago |
customCanvas.datasetSettings.timePresets.threeMonthsAgo | 3 months ago |
customCanvas.datasetSettings.timePresets.sixMonthsAgo | 6 months ago |
customCanvas.datasetSettings.timePresets.oneYearAgo | 1 year ago |
customCanvas.datasetSettings.timePresets.thisWeek | This week |
customCanvas.datasetSettings.timePresets.lastWeek | Last week |
customCanvas.datasetSettings.timePresets.last7Days | Last 7 days |
customCanvas.datasetSettings.timePresets.thisMonth | This month |
customCanvas.datasetSettings.timePresets.lastMonth | Last month |
customCanvas.datasetSettings.timePresets.last30Days | Last 30 days |
customCanvas.datasetSettings.timePresets.thisQuarter | This quarter |
customCanvas.datasetSettings.timePresets.lastQuarter | Last quarter |
customCanvas.datasetSettings.timePresets.last6Months | Last 6 months |
customCanvas.datasetSettings.timePresets.last12Months | Last 12 months |
customCanvas.datasetSettings.timePresets.thisYear | This year |
customCanvas.datasetSettings.timePresets.lastYear | Last year |
Drill-down
| Key | English default |
|---|---|
drillDown.goBack | Go back |
drillDown.close | Close |
drillDown.closeModal | Close modal |
drillDown.breadcrumbNavigation | Breadcrumb navigation |
drillDown.step | Step {{number}} |
drillDown.loading | Loading... |
drillDown.loadError | Failed to load embeddable |
Common Customizations
Override only what you need. Start with these high-impact variables.
Brand Colors
Set the main background and text colors that define your look:
| Variable | Description | Default |
|---|---|---|
--em-background-color-default | Main background color | #ffffff |
--em-background-color-subtle | Secondary/muted backgrounds | #f8f9fa |
--em-foreground-color-default | Primary text color | #1a1a2e |
--em-primary-btn-background-color-default | Primary button background | Brand blue |
Cards
Chart cards are the containers for each visualization:
| Variable | Description |
|---|---|
--em-card-border-radius-default | Corner rounding for cards |
--em-card-padding-default | Inner spacing |
--em-card-title-color-default | Card title text color |
--em-card-title-font-weight | Card title boldness |
--em-box-shadow-default-color | Card shadow color |
--em-box-shadow-default-blur | Card shadow blur radius |
Buttons
Style the action buttons throughout the interface:
| Variable | Description |
|---|---|
--em-primary-btn-background-color-default | Primary button fill |
--em-primary-btn-background-color-disabled | Disabled state fill |
--em-secondary-btn-background-color-default | Secondary button fill |
--em-secondary-btn-background-color-hover | Secondary button hover |
--em-icon-btn-background-color-default | Icon button fill |
--em-icon-btn-background-color-hover | Icon button hover |
Forms & Inputs
Customize dropdowns, text inputs, and form sections:
| Variable | Description |
|---|---|
--em-form-background-color-default | Form panel background |
--em-form-border-radius-default | Form panel corners |
--em-form-header-font-font-color | Form section header color |
--em-text-input-label-color-default | Input label color |
--em-select-menu-label-color-default | Dropdown label color |
Typography
Fine-tune font sizes and weights:
| Variable | Description |
|---|---|
--em-font-size-sm | Small text size |
--em-font-size-md | Medium text size |
--em-font-weight-medium | Medium font weight |
--em-line-height-sm | Small line height |
Custom Canvas Overlay
The overlay is the modal that hosts the template selector and the chart configurator. Its variables sit in their own --em-canvas-overlay-* namespace, so overriding them doesn't affect the rest of your dashboard.
Coverage and box
| Variable | Description | Default |
|---|---|---|
--em-canvas-overlay-position | fixed (viewport) or absolute (parent container) | fixed |
--em-canvas-overlay-inset | Inset for the modal and the dim | 0 |
--em-canvas-overlay-max-width | Maximum content width | 78rem |
--em-canvas-overlay-padding | Inner spacing of the content area | 0 2rem 2.25rem |
--em-canvas-overlay-border-radius | Corner rounding of the content area | 1.75rem |
--em-canvas-overlay-positionacceptsfixed(default - the modal and the dim cover the viewport) orabsolute(they cover only the embeddable's region). When usingabsolute, the embeddable's parent must be a positioned ancestor, e.g.position: relative.--em-canvas-overlay-insetaccepts any CSSinsetshorthand (5rem,1rem 2rem,1rem 2rem 3rem 4rem) and insets the modal and the dim layer together.
Colors
| Variable | Description | Default |
|---|---|---|
--em-canvas-overlay-backdrop-background | Dim layer behind the modal | var(--em-overlay-background) |
--em-canvas-overlay-content-background | Content panel fill | transparent |
--em-canvas-overlay-title-color | Overlay title color | var(--em-sem-text--inverted) |
--em-canvas-overlay-action-button-background | Back / close button fill | var(--em-buttonicon-background--secondary) |
--em-canvas-overlay-action-button-color | Back / close icon color | var(--em-buttonicon-color--secondary) |
- the background is split in two:
--em-canvas-overlay-backdrop-backgroundis the dim behind the modal,--em-canvas-overlay-content-backgroundis the panel holding the topbar, template grid and configurator. The content panel defaults totransparent, which is why cards appear to float over the dim. - the action-button variables only apply to the overlay's back and close buttons - other icon buttons keep their own styling.
Configurator actions bar
| Variable | Description | Default |
|---|---|---|
--em-canvas-overlay-actions-justify-content | Horizontal position of the cancel / confirm pill: flex-start, center or flex-end | center |
--em-canvas-overlay-actions-position | sticky floats the pill while scrolling, static lets it scroll with the content | sticky |
For the complete set, see Canvas Overlay in the full variable reference below.
Full Variable Reference
Below is the complete list of supported CSS variables, organized by category.
Backgrounds & Surfaces
--em-background-color-default
--em-background-color-neutral
--em-background-color-subtle
--em-background-inverted
--em-foreground-color-default
--em-foreground-color-inverted
--em-content-inverted
--em-gray-800
--em-gray-900Cards
--em-card-border-radius-default
--em-card-padding-default
--em-card-title-color-default
--em-card-title-font-weight
--em-card-title-font-line-height
--em-card-title-padding-default
--em-card-subtitle-color-default
--em-card-subtitle-font-font-family
--em-card-info-font-font-family
--em-card-info-font-size
--em-card-info-font-line-height
--em-card-info-font-title-weight
--em-card-empty-title-font-color-default
--em-card-loader-icon-size-height
--em-card-loader-icon-size-widthButtons
--em-primary-btn-background-color-default
--em-primary-btn-background-color-disabled
--em-primary-btn-label-color-disabled
--em-primary-btn-label-font-small-size
--em-secondary-btn-background-color-default
--em-secondary-btn-background-color-hover
--em-icon-btn-background-color-default
--em-icon-btn-background-color-hover
--em-icon-btn-border-radius-default
--em-icon-btn-icon-color-default
--em-icon-btn-icon-color-call-to-action
--em-icon-btn-icon-size-height
--em-icon-btn-icon-size-width
--em-icon-btn-size-height
--em-icon-btn-size-width
--em-btn-island-background-color-defaultForms & Inputs
--em-form-background-color-default
--em-form-border-radius-default
--em-form-padding-default
--em-form-padding-small
--em-form-padding-large
--em-form-font-font-size-title
--em-form-font-font-weight
--em-form-font-line-height
--em-form-header-font-font-color
--em-form-header-font-font-family
--em-form-header-font-font-size
--em-form-header-font-font-weight
--em-form-header-font-line-height
--em-form-header-icon-color-default
--em-form-section-font-font-color
--em-form-section-font-font-family
--em-form-section-font-font-size
--em-form-section-font-font-weight
--em-form-section-font-line-height
--em-form-group-background-color-hover
--em-form-group-padding-default
--em-form-group-padding-small
--em-form-group-icon-size-height
--em-form-group-icon-size-width
--em-text-input-label-color-default
--em-text-input-size-min-width
--em-select-menu-label-color-default
--em-select-menu-label-font-size
--em-select-menu-label-font-weight
--em-select-list-item-isolate-background-color-default
--em-select-list-item-isolate-border-radius-default
--em-select-list-item-isolate-label-color-default
--em-select-list-item-isolate-label-font-size
--em-select-list-item-isolate-label-font-weight
--em-select-list-item-isolate-padding-defaultTypography
--em-font-size-xs
--em-font-size-sm
--em-font-size-md
--em-font-weight-medium
--em-line-height-sm
--em-line-height-lBorders & Shadows
--em-border-radius-200
--em-border-radius-400
--em-border-radius-500
--em-border-width-0
--em-border-width-default
--em-box-shadow-default-blur
--em-box-shadow-default-color
--em-box-shadow-default-position-x
--em-box-shadow-default-position-y
--em-box-shadow-default-spreadSpacing
--em-padding-100
--em-padding-200
--em-padding-300
--em-padding-400
--em-padding-800Empty States
--em-empty-state-background-color-default
--em-empty-state-icon-color-default
--em-empty-state-icon-size-height
--em-empty-state-icon-size-width
--em-empty-state-padding-defaultLightbox & Overlays
--em-lightbox-font-color-default
--em-lightbox-font-font-family
--em-lightbox-font-font-size
--em-lightbox-font-font-weight
--em-lightbox-font-line-height
--em-overlay-zStatus Colors
--em-status-error
--em-status-negative-foreground
--em-switch-thumb-background-colorCanvas Overlay
Variables for the Custom Canvas overlay. Defaults and accepted values for the most-used ones are listed in the sub-section above.
Coverage and placement:
--em-canvas-overlay-position
--em-canvas-overlay-insetContent box:
--em-canvas-overlay-width
--em-canvas-overlay-max-width
--em-canvas-overlay-height
--em-canvas-overlay-max-height
--em-canvas-overlay-padding
--em-canvas-overlay-border-radiusBackgrounds:
--em-canvas-overlay-backdrop-background
--em-canvas-overlay-content-backgroundTopbar title and action buttons (back, close):
--em-canvas-overlay-title-color
--em-canvas-overlay-action-button-background
--em-canvas-overlay-action-button-color
--em-canvas-overlay-action-button-border-color
--em-canvas-overlay-action-button-border-width
--em-canvas-overlay-action-button-border-radiusConfigurator actions bar:
--em-canvas-overlay-actions-justify-content
--em-canvas-overlay-actions-position
--em-canvas-overlay-actions-grid-column
--em-canvas-overlay-actions-grid-row
--em-canvas-overlay-actions-justify-self
--em-canvas-overlay-actions-align-selfTemplate grid:
--em-canvas-overlay-grid-template-columns
--em-canvas-overlay-grid-template-columns--tablet
--em-canvas-overlay-grid-template-columns--mobile
--em-canvas-overlay-grid-auto-rows
--em-canvas-overlay-grid-auto-flow
--em-canvas-overlay-grid-gap
--em-canvas-overlay-grid-align-itemsConfigurator layout and form panel:
--em-canvas-overlay-configurator-grid-template-columns
--em-canvas-overlay-configurator-grid-template-columns--tablet
--em-canvas-overlay-configurator-grid-template-columns--mobile
--em-canvas-overlay-configurator-gap
--em-canvas-overlay-configurator-form-background
--em-canvas-overlay-configurator-form-padding
--em-canvas-overlay-configurator-form-padding-bottom
--em-canvas-overlay-configurator-form-border-radius
--em-canvas-overlay-configurator-form-gapTips
- Override sparingly: Change only what's needed to match your brand. Fewer overrides means easier maintenance.
- Test both themes: If you support light and dark modes, verify your changes work in both contexts.
- Check accessibility: Ensure sufficient color contrast between text and backgrounds.