diff --git a/packages/builder/package.json b/packages/builder/package.json
index 3aae8af2d6..78e539bf60 100644
--- a/packages/builder/package.json
+++ b/packages/builder/package.json
@@ -63,7 +63,7 @@
}
},
"dependencies": {
- "@budibase/bbui": "^1.47.0",
+ "@budibase/bbui": "^1.50.1",
"@budibase/client": "^0.3.1",
"@budibase/colorpicker": "^1.0.1",
"@budibase/svelte-ag-grid": "^0.0.16",
diff --git a/packages/builder/src/builderStore/index.js b/packages/builder/src/builderStore/index.js
index 101f875e96..6317640955 100644
--- a/packages/builder/src/builderStore/index.js
+++ b/packages/builder/src/builderStore/index.js
@@ -1,11 +1,13 @@
import { getStore } from "./store"
import { getBackendUiStore } from "./store/backend"
import { getAutomationStore } from "./store/automation/"
+import { getThemeStore } from "./store/theme"
import analytics from "analytics"
export const store = getStore()
export const backendUiStore = getBackendUiStore()
export const automationStore = getAutomationStore()
+export const themeStore = getThemeStore()
export const initialise = async () => {
try {
diff --git a/packages/builder/src/builderStore/store/localStorage.js b/packages/builder/src/builderStore/store/localStorage.js
new file mode 100644
index 0000000000..aaf10460b6
--- /dev/null
+++ b/packages/builder/src/builderStore/store/localStorage.js
@@ -0,0 +1,43 @@
+import { get, writable } from "svelte/store"
+
+export const localStorageStore = (localStorageKey, initialValue) => {
+ const store = writable(initialValue, () => {
+ // Hydrate from local storage when we get a new subscriber
+ hydrate()
+
+ // Listen for local storage changes and keep store in sync
+ const storageListener = ({ key }) => key === localStorageKey && hydrate()
+ window.addEventListener("storage", storageListener)
+ return () => window.removeEventListener("storage", storageListener)
+ })
+
+ // New store setter which updates the store and localstorage
+ const set = value => {
+ store.set(value)
+ localStorage.setItem(localStorageKey, JSON.stringify(value))
+ }
+
+ // New store updater which updates the store and localstorage
+ const update = updaterFn => set(updaterFn(get(store)))
+
+ // Hydrates the store from localstorage
+ const hydrate = () => {
+ const localValue = localStorage.getItem(localStorageKey)
+ if (localValue == null) {
+ set(initialValue)
+ } else {
+ try {
+ store.set(JSON.parse(localValue))
+ } catch {
+ set(initialValue)
+ }
+ }
+ }
+
+ // Patch the default svelte store functions with our overrides
+ return {
+ ...store,
+ set,
+ update,
+ }
+}
diff --git a/packages/builder/src/builderStore/store/theme.js b/packages/builder/src/builderStore/store/theme.js
new file mode 100644
index 0000000000..df94a4c3d5
--- /dev/null
+++ b/packages/builder/src/builderStore/store/theme.js
@@ -0,0 +1,38 @@
+import { localStorageStore } from "./localStorage"
+
+export const getThemeStore = () => {
+ const themeElement = document.documentElement
+ const initialValue = {
+ darkMode: false,
+ hue: 208,
+ saturation: 9,
+ lightness: 16,
+ }
+
+ const store = localStorageStore("bb-theme", initialValue)
+
+ // Sets a CSS variable on the root document element
+ function setCSSVariable(prop, value) {
+ themeElement.style.setProperty(prop, value)
+ }
+
+ // Resets the custom theme to the default dark theme.
+ // The reset option is only available when dark theme is on, which is why it
+ // sets dark mode to true here
+ store.reset = () => {
+ store.set({
+ ...initialValue,
+ darkMode: true,
+ })
+ }
+
+ // Update theme when store changes
+ store.subscribe(theme => {
+ themeElement.classList[theme.darkMode ? "add" : "remove"]("dark")
+ setCSSVariable("--theme-hue", Math.round(theme.hue))
+ setCSSVariable("--theme-saturation", `${Math.round(theme.saturation)}%`)
+ setCSSVariable("--theme-brightness", `${Math.round(theme.lightness)}%`)
+ })
+
+ return store
+}
diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/Arrow.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/Arrow.svelte
index 4dab01d6f1..0e0c6bb83a 100644
--- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/Arrow.svelte
+++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/Arrow.svelte
@@ -4,8 +4,10 @@
viewBox="0 0 9 75"
fill="none"
xmlns="http://www.w3.org/2000/svg">
-
-
+
+
diff --git a/packages/builder/src/components/common/Spinner.svelte b/packages/builder/src/components/common/Spinner.svelte
index 75f3700af3..cd390375c0 100644
--- a/packages/builder/src/components/common/Spinner.svelte
+++ b/packages/builder/src/components/common/Spinner.svelte
@@ -5,7 +5,7 @@
-
+
diff --git a/packages/builder/src/components/start/AppCard.svelte b/packages/builder/src/components/start/AppCard.svelte
index d6ee4b9591..6a6941b67d 100644
--- a/packages/builder/src/components/start/AppCard.svelte
+++ b/packages/builder/src/components/start/AppCard.svelte
@@ -19,7 +19,7 @@
diff --git a/packages/builder/src/components/start/TemplateList.svelte b/packages/builder/src/components/start/TemplateList.svelte
index 14551adf1d..e4c7ce92d1 100644
--- a/packages/builder/src/components/start/TemplateList.svelte
+++ b/packages/builder/src/components/start/TemplateList.svelte
@@ -52,7 +52,7 @@
}
.templates-card {
- background-color: var(--white);
+ background-color: var(--background);
padding: var(--spacing-xl);
border-radius: var(--border-radius-m);
border: var(--border-dark);
diff --git a/packages/builder/src/components/userInterface/BindableInput.svelte b/packages/builder/src/components/userInterface/BindableInput.svelte
index bb73dfd80d..76624abd06 100644
--- a/packages/builder/src/components/userInterface/BindableInput.svelte
+++ b/packages/builder/src/components/userInterface/BindableInput.svelte
@@ -18,9 +18,9 @@
diff --git a/packages/builder/src/components/userInterface/BindingDropdown.svelte b/packages/builder/src/components/userInterface/BindingDropdown.svelte
index 10ccbd4256..e62f76e4b7 100644
--- a/packages/builder/src/components/userInterface/BindingDropdown.svelte
+++ b/packages/builder/src/components/userInterface/BindingDropdown.svelte
@@ -86,7 +86,7 @@
.controls {
margin-top: var(--spacing-m);
display: grid;
- align-items: baseline;
+ align-items: center;
grid-gap: var(--spacing-l);
grid-template-columns: 1fr auto auto;
}
diff --git a/packages/builder/src/components/userInterface/EventsEditor/EventsEditor.svelte b/packages/builder/src/components/userInterface/EventsEditor/EventsEditor.svelte
index 14e6656381..4df9a0d62e 100644
--- a/packages/builder/src/components/userInterface/EventsEditor/EventsEditor.svelte
+++ b/packages/builder/src/components/userInterface/EventsEditor/EventsEditor.svelte
@@ -67,7 +67,7 @@
display: flex;
justify-content: center;
align-items: center;
- background: white;
+ background: var(--background);
color: var(--ink);
font-size: 14px;
font-weight: 500;
@@ -98,7 +98,7 @@
.handler-container {
display: grid;
grid-template-columns: repeat(2, 1fr);
- border: 2px solid #f9f9f9;
+ border: 2px solid var(--grey-1);
height: 80px;
width: 100%;
}
diff --git a/packages/builder/src/components/userInterface/Feedback/FeedbackNavLink.svelte b/packages/builder/src/components/userInterface/Feedback/FeedbackNavLink.svelte
index 62429bf554..6cba6417d4 100644
--- a/packages/builder/src/components/userInterface/Feedback/FeedbackNavLink.svelte
+++ b/packages/builder/src/components/userInterface/Feedback/FeedbackNavLink.svelte
@@ -18,9 +18,11 @@
-
-
-
+
diff --git a/packages/builder/src/components/userInterface/FlatButton.svelte b/packages/builder/src/components/userInterface/FlatButton.svelte
index 0d7cd280d7..4bac03d4a6 100644
--- a/packages/builder/src/components/userInterface/FlatButton.svelte
+++ b/packages/builder/src/components/userInterface/FlatButton.svelte
@@ -27,7 +27,7 @@
align-items: center;
justify-content: center;
text-align: center;
- background: white;
+ background: var(--background);
color: var(--grey-7);
border-radius: var(--border-radius-m);
font-size: var(--font-size-xs);
diff --git a/packages/builder/src/components/userInterface/LayoutTemplateControls.svelte b/packages/builder/src/components/userInterface/LayoutTemplateControls.svelte
index e88d379dc1..619b6a11d3 100644
--- a/packages/builder/src/components/userInterface/LayoutTemplateControls.svelte
+++ b/packages/builder/src/components/userInterface/LayoutTemplateControls.svelte
@@ -49,7 +49,7 @@
diff --git a/packages/builder/src/components/userInterface/PagesList.svelte b/packages/builder/src/components/userInterface/PagesList.svelte
index 51fcce3889..c26004e44d 100644
--- a/packages/builder/src/components/userInterface/PagesList.svelte
+++ b/packages/builder/src/components/userInterface/PagesList.svelte
@@ -46,7 +46,7 @@
padding: 0 var(--spacing-m);
height: 32px;
text-align: center;
- background: #ffffff;
+ background: var(--background);
color: var(--grey-7);
border-radius: 5px;
font-size: var(--font-size-xs);
diff --git a/packages/builder/src/components/userInterface/PropertyControl.svelte b/packages/builder/src/components/userInterface/PropertyControl.svelte
index 823dcf6183..e9dfb0abfc 100644
--- a/packages/builder/src/components/userInterface/PropertyControl.svelte
+++ b/packages/builder/src/components/userInterface/PropertyControl.svelte
@@ -95,9 +95,12 @@
name={key} />
{#if bindable && control === Input && !key.startsWith('_')}
-