+
+
+ )
+}
+
+export default ThemeSettings
diff --git a/pkg/webui/console/views/user-settings/index.js b/pkg/webui/console/views/user-settings/index.js
index 4b97a6d9bf..e8745cd9e9 100644
--- a/pkg/webui/console/views/user-settings/index.js
+++ b/pkg/webui/console/views/user-settings/index.js
@@ -24,6 +24,7 @@ import GenericNotFound from '@ttn-lw/lib/components/full-view-error/not-found'
import ProfileSettings from '@console/views/user-settings-profile'
import UserApiKeys from '@console/views/user-api-keys'
import ChangePassword from '@console/views/user-settings-password'
+import ThemeSettings from '@console/views/user-settings-theme'
import EmailNotificationsSettings from '@console/views/user-settings-email-notifications'
import SessionManagement from '@console/views/user-settings-sessions'
import OAuthClientAuthorizations from '@console/views/user-settings-oauth-client-authorizations'
@@ -43,6 +44,7 @@ const UserSettings = () => {
+
diff --git a/pkg/webui/containers/collaborator-form/index.js b/pkg/webui/containers/collaborator-form/index.js
index 4c649f5e45..f75cffce7b 100644
--- a/pkg/webui/containers/collaborator-form/index.js
+++ b/pkg/webui/containers/collaborator-form/index.js
@@ -232,6 +232,7 @@ const CollaboratorForm = props => {
type="button"
icon={IconTrash}
disabled={deleteDisabled}
+ secondary
danger
naked
message={
diff --git a/pkg/webui/lib/shared-messages.js b/pkg/webui/lib/shared-messages.js
index 665eaf6a46..7a8941cce3 100644
--- a/pkg/webui/lib/shared-messages.js
+++ b/pkg/webui/lib/shared-messages.js
@@ -215,6 +215,7 @@ export default defineMessages({
devices: 'End devices',
devicesShort: 'Devices',
disabled: 'Disabled',
+ discardChanges: 'Discard changes',
disconnected: 'Disconnected',
documentation: 'Documentation',
downlink: 'Downlink',
@@ -239,6 +240,7 @@ export default defineMessages({
emailAddressValidation: 'Treat email address as validated',
emailAddressValidationDescription:
'Enable this option if you do not need this user to validate the email address',
+ emailNotifications: 'Email notifications',
emailPlaceholder: 'mail@example.com',
empty: 'Empty',
enabled: 'Enabled',
@@ -594,6 +596,7 @@ export default defineMessages({
technicalContact: 'Technical contact',
tenantId: 'Tenant ID',
termsAndCondition: 'Terms and conditions',
+ theme: 'Theme',
time: 'Time',
token: 'Token',
tokenDelete: 'Token delete',
diff --git a/pkg/webui/locales/en.json b/pkg/webui/locales/en.json
index 2cba82c25c..ef97ec7cf6 100644
--- a/pkg/webui/locales/en.json
+++ b/pkg/webui/locales/en.json
@@ -541,7 +541,6 @@
"console.containers.email-notifications-form.index.requiresAdminAction": "Requires admin action, can't be unselected",
"console.containers.email-notifications-form.index.unsubscribeFromEverything": "Turn off all email notifications",
"console.containers.email-notifications-form.index.unsubscribeDescription": "You will continue to receive notifications in the console.",
- "console.containers.email-notifications-form.index.discardChanges": "Discard changes",
"console.containers.email-notifications-form.index.updateEmailPreferences": "Updated email preferences",
"console.containers.freq-plans-select.utils.warning": "Frequency plans unavailable",
"console.containers.freq-plans-select.utils.none": "Do not set a frequency plan",
@@ -823,6 +822,10 @@
"console.containers.user-data-form.edit.deleteConfirmMessage": "Please type in this user's user ID to confirm.",
"console.containers.user-data-form.edit.updateSuccess": "User updated",
"console.containers.user-data-form.edit.deleteSuccess": "User deleted",
+ "console.containers.user-settings-theme.index.updateTheme": "Theme updated",
+ "console.containers.user-settings-theme.index.light": "Light",
+ "console.containers.user-settings-theme.index.dark": "Dark",
+ "console.containers.user-settings-theme.index.system": "System",
"console.containers.users-table.index.invite": "Invite user",
"console.containers.users-table.index.revokeInvitation": "Revoke this invitation",
"console.containers.users-table.index.sentAt": "Sent",
@@ -1023,7 +1026,7 @@
"console.views.gateway-general-settings.messages.techContactDescription": "Technical contact information for this gateway. Typically used to indicate who to contact with technical/security questions about the gateway.",
"console.views.gateway-general-settings.messages.deleteGatewayDefaultMessage": "This will PERMANENTLY DELETE THE ENTITY ITSELF AND ALL ASSOCIATED ENTITIES, including collaborator associations. It will also NOT BE POSSIBLE TO REUSE THE ENTITY ID until purged by an admin but the EUI can be reregistered later with a different ID.",
"console.views.organization-add.index.orgDescription": "Organizations are used to group multiple users and assigning collective rights for them. An organization can then be set as collaborator of applications or gateways. This makes it easy to grant or revoke rights to entities for a group of users.{break} Learn more in our guide on Organization Management.",
- "console.views.user-settings-email-notifications.index.emailNotifications": "Email notifications",
+ "console.views.user-settings-email-notifications.index.emailNotificationsSettings": "Email notifications settings",
"console.views.user-settings-email-notifications.index.customizeEmailNotifications": "Customize the notifications for which you receive emails. To see all your notifications, head to the notifications panel.",
"console.views.user-settings-oauth-auth-settings.index.deleteButton": "Revoke authorization",
"console.views.user-settings-oauth-auth-settings.index.deleteSuccess": "This authorization was successfully revoked",
@@ -1032,6 +1035,7 @@
"console.views.user-settings-oauth-auths.index.authorizationSettings": "Authorization settings",
"console.views.user-settings-oauth-auths.index.accessTokens": "Active access tokens",
"console.views.user-settings-profile.index.profileEdit": "Edit profile",
+ "console.views.user-settings-theme.index.customizeTheme": "Customize your interface theme.",
"containers.collaborator-form.index.collaboratorIdPlaceholder": "Type to choose a collaborator",
"containers.collaborator-form.index.memberIdPlaceholder": "Type to choose a member",
"containers.collaborator-form.index.memberDeleteSuccess": "Member removed",
@@ -1374,6 +1378,7 @@
"lib.shared-messages.devices": "End devices",
"lib.shared-messages.devicesShort": "Devices",
"lib.shared-messages.disabled": "Disabled",
+ "lib.shared-messages.discardChanges": "Discard changes",
"lib.shared-messages.disconnected": "Disconnected",
"lib.shared-messages.documentation": "Documentation",
"lib.shared-messages.downlink": "Downlink",
@@ -1396,6 +1401,7 @@
"lib.shared-messages.emailAddressDescription": "Primary email address used for logging in; this address is not publicly visible",
"lib.shared-messages.emailAddressValidation": "Treat email address as validated",
"lib.shared-messages.emailAddressValidationDescription": "Enable this option if you do not need this user to validate the email address",
+ "lib.shared-messages.emailNotifications": "Email notifications",
"lib.shared-messages.emailPlaceholder": "mail@example.com",
"lib.shared-messages.empty": "Empty",
"lib.shared-messages.enabled": "Enabled",
@@ -1732,6 +1738,7 @@
"lib.shared-messages.technicalContact": "Technical contact",
"lib.shared-messages.tenantId": "Tenant ID",
"lib.shared-messages.termsAndCondition": "Terms and conditions",
+ "lib.shared-messages.theme": "Theme",
"lib.shared-messages.time": "Time",
"lib.shared-messages.token": "Token",
"lib.shared-messages.tokenDelete": "Token delete",
diff --git a/pkg/webui/locales/ja.json b/pkg/webui/locales/ja.json
index cfa1b85339..c366068a14 100644
--- a/pkg/webui/locales/ja.json
+++ b/pkg/webui/locales/ja.json
@@ -541,7 +541,6 @@
"console.containers.email-notifications-form.index.requiresAdminAction": "",
"console.containers.email-notifications-form.index.unsubscribeFromEverything": "",
"console.containers.email-notifications-form.index.unsubscribeDescription": "",
- "console.containers.email-notifications-form.index.discardChanges": "",
"console.containers.email-notifications-form.index.updateEmailPreferences": "",
"console.containers.freq-plans-select.utils.warning": "",
"console.containers.freq-plans-select.utils.none": "",
@@ -823,6 +822,10 @@
"console.containers.user-data-form.edit.deleteConfirmMessage": "",
"console.containers.user-data-form.edit.updateSuccess": "",
"console.containers.user-data-form.edit.deleteSuccess": "",
+ "console.containers.user-settings-theme.index.updateTheme": "",
+ "console.containers.user-settings-theme.index.light": "",
+ "console.containers.user-settings-theme.index.dark": "",
+ "console.containers.user-settings-theme.index.system": "",
"console.containers.users-table.index.invite": "",
"console.containers.users-table.index.revokeInvitation": "",
"console.containers.users-table.index.sentAt": "",
@@ -1023,7 +1026,7 @@
"console.views.gateway-general-settings.messages.techContactDescription": "",
"console.views.gateway-general-settings.messages.deleteGatewayDefaultMessage": "",
"console.views.organization-add.index.orgDescription": "",
- "console.views.user-settings-email-notifications.index.emailNotifications": "",
+ "console.views.user-settings-email-notifications.index.emailNotificationsSettings": "",
"console.views.user-settings-email-notifications.index.customizeEmailNotifications": "",
"console.views.user-settings-oauth-auth-settings.index.deleteButton": "",
"console.views.user-settings-oauth-auth-settings.index.deleteSuccess": "",
@@ -1032,6 +1035,7 @@
"console.views.user-settings-oauth-auths.index.authorizationSettings": "",
"console.views.user-settings-oauth-auths.index.accessTokens": "",
"console.views.user-settings-profile.index.profileEdit": "",
+ "console.views.user-settings-theme.index.customizeTheme": "",
"containers.collaborator-form.index.collaboratorIdPlaceholder": "",
"containers.collaborator-form.index.memberIdPlaceholder": "",
"containers.collaborator-form.index.memberDeleteSuccess": "",
@@ -1374,6 +1378,7 @@
"lib.shared-messages.devices": "エンドデバイス",
"lib.shared-messages.devicesShort": "",
"lib.shared-messages.disabled": "無効化",
+ "lib.shared-messages.discardChanges": "",
"lib.shared-messages.disconnected": "切断されます",
"lib.shared-messages.documentation": "ドキュメンテーション",
"lib.shared-messages.downlink": "ダウンリンク",
@@ -1396,6 +1401,7 @@
"lib.shared-messages.emailAddressDescription": "",
"lib.shared-messages.emailAddressValidation": "",
"lib.shared-messages.emailAddressValidationDescription": "",
+ "lib.shared-messages.emailNotifications": "",
"lib.shared-messages.emailPlaceholder": "",
"lib.shared-messages.empty": "空の状態",
"lib.shared-messages.enabled": "有効化",
@@ -1732,6 +1738,7 @@
"lib.shared-messages.technicalContact": "技術連絡先",
"lib.shared-messages.tenantId": "テナントID",
"lib.shared-messages.termsAndCondition": "ご利用規約",
+ "lib.shared-messages.theme": "",
"lib.shared-messages.time": "時間",
"lib.shared-messages.token": "トークン",
"lib.shared-messages.tokenDelete": "トークン削除",
diff --git a/pkg/webui/styles/main.styl b/pkg/webui/styles/main.styl
index dc90b5f109..39b9a9bd98 100644
--- a/pkg/webui/styles/main.styl
+++ b/pkg/webui/styles/main.styl
@@ -53,7 +53,7 @@ ul li
hr
border-normal()
- background-color: var(--c-border-neutral-light)
+ background-color: light-dark(var(--c-border-neutral-light), var(--c-border-neutral-normal))
border: 0
height: 1px
diff --git a/pkg/webui/styles/utilities/tokens.styl b/pkg/webui/styles/utilities/tokens.styl
index fd63d80cd8..842305e93c 100644
--- a/pkg/webui/styles/utilities/tokens.styl
+++ b/pkg/webui/styles/utilities/tokens.styl
@@ -46,5 +46,12 @@ make-utilities($name, $theme)
.focus\\:c-{$key}:focus
border-color: $value !important
+// Define color theme and generate variables accordingly.
:global
- make-utilities('tokens', 'theme-light')
+ .dark
+ color-scheme: dark
+ make-utilities('tokens', 'theme-dark')
+
+ .light
+ color-scheme: light
+ make-utilities('tokens', 'theme-light')
diff --git a/pkg/webui/styles/variables/css-variables.styl b/pkg/webui/styles/variables/css-variables.styl
index ea2b82226f..994e65a5a8 100644
--- a/pkg/webui/styles/variables/css-variables.styl
+++ b/pkg/webui/styles/variables/css-variables.styl
@@ -19,11 +19,19 @@ make-variables($name, $theme)
for $key, $value in $dict[$theme][shadow]
{'--shadow-'}{$key}: $value
-// Generate CSS variables based on token definitions.
-
:root, :before, :after
- make-variables('tokens', 'theme-light')
+ color-scheme: light dark
--grid-column-gap: $ls.s
--grid-row-gap: $ls.s
--container-vertical-padding: $ls.s
+
+// Define color theme and generate variables accordingly.
+:global
+ .dark
+ color-scheme: dark
+ make-variables('tokens', 'theme-dark')
+
+ .light
+ color-scheme: light
+ make-variables('tokens', 'theme-light')
diff --git a/pkg/webui/styles/variables/tokens.styl b/pkg/webui/styles/variables/tokens.styl
index f3c08ebf8f..990af516d4 100644
--- a/pkg/webui/styles/variables/tokens.styl
+++ b/pkg/webui/styles/variables/tokens.styl
@@ -49,6 +49,8 @@ $tokens = {
'bg-brand-semilight': $c.tts-075, // Background for highlight elements.
'bg-brand-normal': $c.tts-500, // Background for highlight elements.
'bg-brand-normal-hover': $c.tts-600, // Background for highlight elements on hover.
+ 'bg-brand-bold': $c.tts-800,
+ 'bg-brand-heavy': $c.tts-900,
// Semantic
@@ -69,12 +71,22 @@ $tokens = {
'bg-success-normal': $c.success-600, // Background for success highlight elements.
'bg-warning-normal': $c.warning-600, // Background for warning highlight elements.
- 'bg-error-normal': $c.error-600, // Background for error highlight elements.
+ 'bg-error-normal': $c.error-500, // Background for error highlight elements.
'bg-info-normal': $c.information-600, // Background for info highlight elements.
+ 'bg-error-bold': $c.error-075, // Background for error secondary button.
+ 'bg-info-bold': $c.information-075,
+ 'bg-warning-bold': $c.warning-075,
+ 'bg-success-bold': $c.success-075,
+
+ 'bg-error-heavy': $c.error-100, // Background for error secondary button on hover.
+ 'bg-info-heavy': $c.information-050,
+ 'bg-warning-heavy': $c.warning-050,
+ 'bg-success-heavy': $c.success-025,
+
'bg-success-normal-hover': $c.success-700, // Background for success highlight elements on hover.
'bg-warning-normal-hover': $c.warning-700, // Background for warning highlight elements on hover.
- 'bg-error-normal-hover': $c.error-700, // Background for error highlight elements on hover.
+ 'bg-error-normal-hover': $c.error-600, // Background for error highlight elements on hover.
'bg-info-normal-hover': $c.information-700, // Background for info highlight elements on hover.
'bg-success-normal-disabled': $c.success-200, // Background for success highlight elements on disabled.
@@ -92,6 +104,7 @@ $tokens = {
'text-neutral-extralight': $c.neutral-400, // Text color for disable text or darker text on dark background.
'text-neutral-light': $c.neutral-600, // Text color for second level information text.
'text-neutral-semilight': $c.neutral-700, // Text color for enable text in a menu.
+ 'text-neutral-bold': $c.neutral-800,
'text-neutral-heavy': $c.neutral-900, // Text color for primary information or text when hover.
// Brand
@@ -99,6 +112,8 @@ $tokens = {
'text-brand-normal': $c.tts-500, // Text color for highlight information.
'text-brand-normal-hover': $c.tts-600, // Text color for highlight information on hover.
'text-brand-normal-active': $c.tts-700, // Text color for highlight information on active.
+ 'text-brand-semilight': $c.tts-400,
+ 'text-brand-light': $c.tts-300,
// Semantic
@@ -130,6 +145,7 @@ $tokens = {
// Brand
+ 'icon-brand-light': $c.tts-300,
'icon-brand-normal': $c.tts-500, // Icon color for highlight icon color.
'icon-brand-normal-hover': $c.tts-600, // Icon color for highlight icon color on hover.
'icon-brand-normal-active': $c.tts-700, // Icon color for highlight icon color on active.
@@ -154,7 +170,6 @@ $tokens = {
'border-neutral-bold': $c.neutral-700, // Border for divider lines and default element borders on dark surface.
// Brand
-
'border-brand-normal': $c.tts-600, // Border for input element borders on focus.
'border-brand-semilight': $c.tts-100, // Border for element borders on active.
@@ -170,6 +185,11 @@ $tokens = {
'border-error-light': $c.error-200, // Border for error element borders on regular elements like tags or banners.
'border-info-light': $c.information-200, // Border for info element borders on regular elements like tags or banners.
+ 'border-info-extralight': $c.information-100,
+ 'border-error-extralight': $c.error-100,
+ 'border-warning-extralight': $c.warning-200,
+ 'border-success-extralight': $c.success-100,
+
// Gradients
'gradient-neutral-light-01': $c.neutral-100, // First part of gradient for default input borders.
@@ -199,20 +219,25 @@ $tokens = {
'bg-neutral-min': $c.neutral-black, // Background color for panels, dropdowns, inputs, tab, table, ...
'bg-neutral-extralight': $c.neutral-1025, // Background for navigation element items.
- 'bg-neutral-extralight-hover': $c.neutral-900, // Background color for panels, dropdowns, inputs, tab, table, ... on hover.
+ 'bg-neutral-extralight-hover': $c.neutral-1000, // Background color for panels, dropdowns, inputs, tab, table, ... on hover.
'bg-neutral-light': $c.neutral-900, // Background for navigation element items on hover.
'bg-neutral-semilight': $c.neutral-900, // Background for the console dashboard.
'bg-neutral-normal': $c.neutral-700, // Background for the console dashboard on hover.
'bg-neutral-semibold': $c.neutral-600,
'bg-neutral-bold': $c.neutral-700,
'bg-neutral-heavy': $c.neutral-050, // Background for navigation element items when active.
+ 'bg-neutral-heavy-hover': $c.neutral-200, // Background for navigation element items when active on hover.
// Brand
'bg-brand-extralight': $c.neutral-1000, // Background for navigation elements background.
'bg-brand-light': $c.neutral-800, // ?
- 'bg-brand-normal': $c.tts-400, // Background for highlight elements.
- 'bg-brand-normal-hover': $c.tts-500, // Background for highlight elements on hover.
+ 'bg-brand-semilight': $c.tts-075,
+ 'bg-brand-normal': $c.tts-500, // Background for highlight elements.
+ 'bg-brand-normal-hover': $c.tts-600, // Background for highlight elements on hover.
+ 'bg-brand-semibold': $c.tts-400, // ?
+ 'bg-brand-bold': $c.tts-200, // Background for tertiary.
+ 'bg-brand-heavy': $c.tts-100, // Background for tertiary button on hover.
// Semantic
@@ -228,18 +253,28 @@ $tokens = {
'bg-success-normal': $c.success-400, // Background for success highlight elements.
'bg-warning-normal': $c.warning-400, // Background for warning highlight elements.
- 'bg-error-normal': $c.error-400, // Background for error highlight elements.
- 'bg-info-normal': $c.info-400, // Background for info highlight elements.
+ 'bg-error-normal': $c.error-500, // Background for error highlight elements.
+ 'bg-info-normal': $c.information-400, // Background for info highlight elements.
'bg-success-normal-hover': $c.success-500, // Background for success highlight elements on hover.
'bg-warning-normal-hover': $c.warning-500, // Background for warning highlight elements on hover.
- 'bg-error-normal-hover': $c.error-500, // Background for error highlight elements on hover.
- 'bg-info-normal-hover': $c.info-500, // Background for info highlight elements on hover.
+ 'bg-error-normal-hover': $c.error-600, // Background for error highlight elements on hover.
+ 'bg-info-normal-hover': $c.information-500, // Background for info highlight elements on hover.
+
+ 'bg-error-bold': $c.error-1000, // Background for error secondary button.
+ 'bg-info-bold': $c.information-1025,
+ 'bg-warning-bold': $c.warning-1025,
+ 'bg-success-bold': $c.success-1025,
+
+ 'bg-error-heavy': $c.error-1025, // Background for error secondary button on hover.
+ 'bg-info-heavy': $c.information-1050,
+ 'bg-warning-heavy': $c.warning-1050,
+ 'bg-success-heavy': $c.success-1050,
'bg-success-normal-disabled': $c.success-100, // Background for success highlight elements on disabled.
'bg-warning-normal-disabled': $c.warning-100, // Background for warning highlight elements on disabled.
'bg-error-normal-disabled': $c.error-100, // Background for error highlight elements on disabled.
- 'bg-info-normal-disabled': $c.info-100, // Background for info highlight elements on disabled.
+ 'bg-info-normal-disabled': $c.information-100, // Background for info highlight elements on disabled.
// TEXT COLORS
// ===========
@@ -254,6 +289,8 @@ $tokens = {
// Brand
+ 'text-brand-extralight': $c.tts-800, // Text color for tertiary button label.
+ 'text-brand-light': $c.tts-600, // ?
'text-brand-normal': $c.tts-400, // Text color for highlight information.
'text-brand-normal-hover': $c.tts-500, // Text color for highlight information on hover.
'text-brand-normal-active': $c.tts-300, // Text color for highlight information on active.
@@ -288,6 +325,7 @@ $tokens = {
// Brand
+ 'icon-brand-light': $c.tts-300,
'icon-brand-normal': $c.tts-400, // Icon color for highlight icon color.
'icon-brand-normal-hover': $c.tts-500, // Icon color for highlight icon color on hover.
'icon-brand-normal-active': $c.tts-600, // Icon color for highlight icon color on active.
@@ -317,16 +355,21 @@ $tokens = {
// Semantic
- 'border-success-normal': $c.success-400, // Border for success element borders.
- 'border-warning-normal': $c.warning-400, // Border for warning element borders.
- 'border-error-normal': $c.error-400, // Border for error element borders.
- 'border-info-normal': $c.information-400, // Border for info element borders.
+ 'border-error-extralight': $c.error-900, // Border for info element borders on regular elements like tags or banners.
+ 'border-info-extralight': $c.information-800,
+ 'border-warning-extralight': $c.warning-800,
+ 'border-success-extralight': $c.success-900,
'border-success-light': $c.success-800, // Border for success element borders on regular elements like tags or banners.
'border-warning-light': $c.warning-800, // Border for warning element borders on regular elements like tags or banners.
'border-error-light': $c.error-800, // Border for error element borders on regular elements like tags or banners.
'border-info-light': $c.information-800, // Border for info element borders on regular elements like tags or banners.
+ 'border-success-normal': $c.success-400, // Border for success element borders.
+ 'border-warning-normal': $c.warning-400, // Border for warning element borders.
+ 'border-error-normal': $c.error-400, // Border for error element borders.
+ 'border-info-normal': $c.information-400, // Border for info element borders.
+
// Gradients
'gradient-neutral-light-01': $c.neutral-300, // First part of gradient for default input borders.