diff --git a/.gitignore b/.gitignore
index 5b366bb78eb..179470c16af 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,3 +25,4 @@ docker-compose.override.yml
*~
src/schema.rs.orig
+.DS_Store
diff --git a/app/app.js b/app/app.js
index e45e8400e94..7bc0534665f 100644
--- a/app/app.js
+++ b/app/app.js
@@ -5,6 +5,9 @@ import Resolver from 'ember-resolver';
import config from './config/environment';
import * as Sentry from './sentry';
+import { theme } from './utils/theme';
+
+theme.loadSettingTheme();
if (typeof FastBoot === 'undefined') {
// eslint-disable-next-line unicorn/prefer-add-event-listener
diff --git a/app/components/crate-downloads-list.module.css b/app/components/crate-downloads-list.module.css
index 1a3c849b094..c1c9e7b406a 100644
--- a/app/components/crate-downloads-list.module.css
+++ b/app/components/crate-downloads-list.module.css
@@ -8,8 +8,8 @@
}
.link {
- color: #525252;
- background-color: #edebdd;
+ color: var(--download-list-link);
+ background-color: var(--main-bg-dark);
font-size: 90%;
padding: 20px 10px;
display: flex;
diff --git a/app/components/crate-row.module.css b/app/components/crate-row.module.css
index b5d7ad5a1a8..8fcd5130541 100644
--- a/app/components/crate-row.module.css
+++ b/app/components/crate-row.module.css
@@ -1,10 +1,9 @@
.crate-row {
- --shadow: 0 1px 3px hsla(51, 90%, 42%, .35);
-
display: flex;
flex-wrap: wrap;
padding: 15px 25px;
- background-color: white;
+ background-color: var(--item-background);
+ color: var(--item);
border-radius: 5px;
box-shadow: var(--shadow);
transition: all 300ms;
diff --git a/app/components/dependency-list/row.module.css b/app/components/dependency-list/row.module.css
index ef9f7dda703..3174c830650 100644
--- a/app/components/dependency-list/row.module.css
+++ b/app/components/dependency-list/row.module.css
@@ -4,20 +4,19 @@
--range-color: var(--grey900);
--crate-color: var(--grey700);
--placeholder-opacity: 0.35;
- --shadow: 0 1px 3px hsla(51, 90%, 42%, .35);
-
+
display: flex;
align-items: center;
position: relative;
font-size: 18px;
padding: 15px 25px;
- background-color: white;
+ background-color: var(--item-background);
border-radius: 5px;
box-shadow: var(--shadow);
transition: all 300ms;
&:hover, &.focused {
- background-color: var(--hover-bg-color);
+ background-color: var(--item-hover);
transition: all 0ms;
}
diff --git a/app/components/dropdown/menu-item.module.css b/app/components/dropdown/menu-item.module.css
index 05d2baacd38..3eaac6a968e 100644
--- a/app/components/dropdown/menu-item.module.css
+++ b/app/components/dropdown/menu-item.module.css
@@ -1,15 +1,16 @@
.item {
- > a, button {
+ > a, span, button {
font-size: 90%;
width: 100%;
display: inline-flex;
text-align: start;
padding: 8px 10px;
text-decoration: none;
+ cursor: pointer;
color: var(--main-color) !important;
&:hover {
- background: #5e5e5e;
+ background: var(--menu-hover);
color: white !important;
}
}
diff --git a/app/components/dropdown/menu.module.css b/app/components/dropdown/menu.module.css
index 35a0655e48b..a7c159200e1 100644
--- a/app/components/dropdown/menu.module.css
+++ b/app/components/dropdown/menu.module.css
@@ -2,7 +2,7 @@
margin: 0;
text-align: left;
padding: 0;
- background: white;
+ background: var(--item-background);
border: 1px solid var(--gray-border);
list-style: none;
overflow: hidden;
diff --git a/app/components/email-input.module.css b/app/components/email-input.module.css
index 75f30210d17..fb1fe6f27ab 100644
--- a/app/components/email-input.module.css
+++ b/app/components/email-input.module.css
@@ -6,7 +6,7 @@
.row {
width: 100%;
- border: 1px solid #d5d3cb;
+ border: 1px solid var(--gray-border);
border-bottom-width: 0;
padding: 10px 20px;
display: flex;
diff --git a/app/components/front-page-list/item.module.css b/app/components/front-page-list/item.module.css
index 396e7b2aae5..7f6b278aa0c 100644
--- a/app/components/front-page-list/item.module.css
+++ b/app/components/front-page-list/item.module.css
@@ -1,5 +1,4 @@
.link {
- --shadow: 0 2px 3px hsla(51, 50%, 44%, .35);
display: flex;
align-items: center;
@@ -7,11 +6,11 @@
height: 60px;
margin: 8px 0;
padding: 0 15px;
- background-color: white;
- color: #525252;
+ background-color: var(--item-background);
+ color: var(--item);
text-decoration: none;
border-radius: 5px;
- box-shadow: var(--shadow);
+ box-shadow: var(--shadow2);
transition: background-color 300ms;
&:focus-visible {
@@ -20,14 +19,14 @@
}
&:hover, &:focus-visible {
- color: #525252;
- background-color: hsl(58deg 72% 97%);
+ color: var(--download-list-link);
+ background-color: var(--item-hover);
transition: background-color 0ms;
}
&:active {
transform: translateY(2px);
- --shadow: inset 0 0 0 1px hsla(51, 50%, 44%, .15);
+ box-shadow: inset 0 0 0 1px hsla(51, 50%, 44%, .15);;
}
}
diff --git a/app/components/front-page-list/item/placeholder.module.css b/app/components/front-page-list/item/placeholder.module.css
index 7be83a33b3c..8402216b57c 100644
--- a/app/components/front-page-list/item/placeholder.module.css
+++ b/app/components/front-page-list/item/placeholder.module.css
@@ -1,5 +1,4 @@
.link {
- --shadow: 0 2px 3px hsla(51, 50%, 44%, .35);
--placeholder-bg: hsla(59, 19%, 50%, 1.0);
--placeholder-bg2: hsla(59, 19%, 50%, 0.7);
@@ -9,10 +8,10 @@
height: 60px;
margin: 8px 0;
padding: 0 15px;
- background-color: white;
- color: #525252;
+ background-color: var(--item-background);
+ color: var(--download-list-link);
border-radius: 5px;
- box-shadow: var(--shadow);
+ box-shadow: var(--shadow2);
cursor: wait;
}
diff --git a/app/components/header.hbs b/app/components/header.hbs
index c515c3c00c5..e60c448f032 100644
--- a/app/components/header.hbs
+++ b/app/components/header.hbs
@@ -32,6 +32,17 @@
Browse All Crates
|
+
+
+ Theme
+
+
+ Sync with system
+ Light theme
+ Dark theme
+
+
+ |
Docs
diff --git a/app/components/header.js b/app/components/header.js
index ec3ed3cf82d..2dc10d5a55f 100644
--- a/app/components/header.js
+++ b/app/components/header.js
@@ -2,6 +2,8 @@ import { action } from '@ember/object';
import { inject as service } from '@ember/service';
import Component from '@glimmer/component';
+import { theme } from '../utils/theme';
+
export default class Header extends Component {
@service header;
@service router;
@@ -20,4 +22,16 @@ export default class Header extends Component {
},
});
}
+
+ @action useSystemTheme() {
+ theme.useSystemTheme();
+ }
+
+ @action useLightTheme() {
+ theme.useLightTheme();
+ }
+
+ @action useDarkTheme() {
+ theme.useDarkTheme();
+ }
}
diff --git a/app/components/owned-crate-row.module.css b/app/components/owned-crate-row.module.css
index 2468be7745c..fb0ec36855d 100644
--- a/app/components/owned-crate-row.module.css
+++ b/app/components/owned-crate-row.module.css
@@ -2,7 +2,7 @@
display: flex;
align-items: center;
background-color: #fff;
- border: 1px solid #d5d3cb;
+ border: 1px solid var(--gray-border);
padding: 20px 30px;
font-weight: bold;
cursor: pointer;
@@ -24,12 +24,12 @@
width: 36px;
height: 36px;
margin-left: 10px;
- border: 2px solid #d5d3cb;
+ border: 2px solid var(--gray-border);
border-radius: 50%;
.checked & {
- background-color: #cfc487;
- border-color: #cfc487;
+ background-color: var(--owned-crate-checked);
+ border-color: var(--owned-crate-checked);
}
}
diff --git a/app/components/rendered-html.module.css b/app/components/rendered-html.module.css
index ae18157e9b8..bad93ad0463 100644
--- a/app/components/rendered-html.module.css
+++ b/app/components/rendered-html.module.css
@@ -20,8 +20,9 @@
p {
code {
- background-color: #fff;
- padding: 0 2px;
+ background-color: var(--code);
+ padding: 0 4px;
+ border-radius: 2px;
}
}
@@ -35,7 +36,7 @@
overflow-x: auto;
th, td {
- border: 1px solid #dfe2e5;
+ border: 1px solid var(--rendered-html-table);
padding: 6px 13px;
}
}
diff --git a/app/components/rev-dep-row.module.css b/app/components/rev-dep-row.module.css
index 3f078e8f048..aa81780488c 100644
--- a/app/components/rev-dep-row.module.css
+++ b/app/components/rev-dep-row.module.css
@@ -2,18 +2,17 @@
--hover-bg-color: hsl(217, 37%, 98%);
--crate-color: var(--grey700);
--placeholder-opacity: 0.35;
- --shadow: 0 1px 3px hsla(51, 90%, 42%, .35);
-
+
position: relative;
font-size: 18px;
padding: 15px 25px;
- background-color: white;
+ background-color: var(--item-background);
border-radius: 5px;
box-shadow: var(--shadow);
transition: all 300ms;
&:hover, &.focused {
- background-color: var(--hover-bg-color);
+ background-color: var(--item-hover);
transition: all 0ms;
}
diff --git a/app/components/settings/api-tokens.module.css b/app/components/settings/api-tokens.module.css
index bf90fa265ab..945f3e06b25 100644
--- a/app/components/settings/api-tokens.module.css
+++ b/app/components/settings/api-tokens.module.css
@@ -21,7 +21,7 @@
.row {
width: 100%;
- border: 1px solid #d5d3cb;
+ border: 1px solid var(--gray-border);
border-bottom-width: 0;
padding: 10px 20px;
display: flex;
diff --git a/app/components/version-list/row.module.css b/app/components/version-list/row.module.css
index 5fb3a546f5b..f83fd3aa6fd 100644
--- a/app/components/version-list/row.module.css
+++ b/app/components/version-list/row.module.css
@@ -2,20 +2,19 @@
--bg-color: var(--grey200);
--hover-bg-color: hsl(217, 37%, 98%);
--fg-color: var(--grey700);
- --shadow: 0 1px 3px hsla(51, 90%, 42%, .35);
display: flex;
align-items: center;
position: relative;
font-size: 18px;
padding: 15px 25px;
- background-color: white;
+ background-color: var(--item-background);
border-radius: 5px;
- box-shadow: var(--shadow);
+ box-shadow: var(--shadow2);
transition: all 300ms;
&:hover, &.focused {
- background-color: var(--hover-bg-color);
+ background-color: var(--item-hover);
transition: all 0ms;
}
diff --git a/app/index.html b/app/index.html
index 81dac870e59..434d9a79891 100644
--- a/app/index.html
+++ b/app/index.html
@@ -1,5 +1,5 @@
-
+
diff --git a/app/styles/application.module.css b/app/styles/application.module.css
index 7749c1fa02f..c9922314df9 100644
--- a/app/styles/application.module.css
+++ b/app/styles/application.module.css
@@ -1,29 +1,25 @@
+@import url(./theme.module.css);
+
:root {
--violet800: hsl(252, 44%, 24%);
--grey900: hsl(200, 15%, 19%);
--grey700: hsl(200, 11%, 43%);
--grey600: hsl(200, 13%, 60%);
--grey200: hsl(200, 17%, 96%);
- --green800: hsl(115, 31%, 31%);
--yellow500: hsl(44, 100%, 60%);
- --header-bg-color: var(--green800);
- --footer-bg-color: var(--green800);
-
--font-sans: "Fira Sans", sans-serif;
--font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
-
- --main-color: #383838;
--main-color-light: #858585;
- --main-bg: #f9f7ec;
- --main-bg-dark: #edebdd;
- --gray-border: #d5d3cb;
- --link-color: rgb(0, 172, 91);
- --link-hover-color: #007940;
- --separator-color: #284725;
+ --download-list-link: #525252;
+ --owned-crate-checked: #cfc487;
+ --rendered-html-table: #dfe2e5;
--placeholder-bg: hsl(212, 7%, 57%);
--placeholder-bg2: hsl(213, 16%, 75%);
+ --separator-color: #284725;
+ --link-hover-color: #007940;
+ --link-color: rgb(0, 172, 91);
}
:global(.new-design) {
diff --git a/app/styles/crate/version.module.css b/app/styles/crate/version.module.css
index 89c83b3279a..5ccf5291f8e 100644
--- a/app/styles/crate/version.module.css
+++ b/app/styles/crate/version.module.css
@@ -6,13 +6,11 @@
}
.docs {
- --shadow: 0 2px 3px hsla(51, 50%, 44%, .35);
-
margin-bottom: 25px;
padding: 25px;
- background-color: white;
+ background-color: var(--item-background);
border-radius: 5px;
- box-shadow: var(--shadow);
+ box-shadow: var(--shadow2);
@media only screen and (max-width: 550px) {
margin-left: -15px;
@@ -104,6 +102,9 @@
font-size: 160%;
font-weight: bold;
margin-bottom: 4px;
+ circle {
+ fill: #fff;
+ }
}
.num__align {
diff --git a/app/styles/crates.module.css b/app/styles/crates.module.css
index 01044bfff87..f53d7666def 100644
--- a/app/styles/crates.module.css
+++ b/app/styles/crates.module.css
@@ -4,7 +4,7 @@
justify-content: space-between;
padding: 20px;
border-radius: 5px;
- background-color: white;
+ background-color: var(--item-background);
margin-bottom: 40px;
a {
diff --git a/app/styles/dashboard.module.css b/app/styles/dashboard.module.css
index 52e8fcfb3c6..1b671d043f1 100644
--- a/app/styles/dashboard.module.css
+++ b/app/styles/dashboard.module.css
@@ -81,7 +81,7 @@
}
.feed {
- background: white;
+ background: var(--item-background);
padding: 0 20px 20px;
}
diff --git a/app/styles/me/pending-invites.module.css b/app/styles/me/pending-invites.module.css
index 19813a7a861..94e6a743ff8 100644
--- a/app/styles/me/pending-invites.module.css
+++ b/app/styles/me/pending-invites.module.css
@@ -5,7 +5,7 @@
.row {
margin: 0;
padding: 20px;
- border-bottom: 2px solid #d5d3cb;
+ border-bottom: 2px solid var(--gray-border);
&:last-of-type {
border: none;
diff --git a/app/styles/shared/typography.module.css b/app/styles/shared/typography.module.css
index 60abd926a84..8e96fbab87d 100644
--- a/app/styles/shared/typography.module.css
+++ b/app/styles/shared/typography.module.css
@@ -13,6 +13,6 @@
font-weight: normal;
&:hover {
- color: #6b6b6b;
+ color: var(--color4);
}
}
diff --git a/app/styles/theme.module.css b/app/styles/theme.module.css
new file mode 100644
index 00000000000..48dcb7964ab
--- /dev/null
+++ b/app/styles/theme.module.css
@@ -0,0 +1,65 @@
+@media (prefers-color-scheme: light) {
+ [data-color-mode=auto][data-light-theme*=light] {
+ --shadow: 0 1px 3px hsla(51, 90%, 42%, .35);
+ --shadow2: 0 2px 3px hsla(51, 50%, 44%, .35);
+ --header-bg-color: hsl(115, 31%, 31%);
+ --footer-bg-color: hsl(115, 31%, 31%);
+ --main-color: #383838;
+ --main-bg: #f9f7ec;
+ --main-bg-dark: #edebdd;
+ --gray-border: #d5d3cb;
+ --menu-hover: #5e5e5e;
+ --item: #525252;
+ --item-background: #fff;
+ --item-hover: #fdfcf2;
+ --code: #fff;
+ }
+}
+@media (prefers-color-scheme: dark) {
+ [data-color-mode=auto][data-dark-theme*=dark] {
+ --shadow: 0 0 6px rgba(0,0,0,.4);
+ --shadow2: 0 2px 3px rgba(0,0,0,.3);
+ --header-bg-color: #24292e;
+ --footer-bg-color: #24292e;
+ --main-color: #c9d1d9;
+ --main-bg: #0d1117;
+ --main-bg-dark: #161c22;
+ --gray-border: #444444;
+ --menu-hover: #2a3138;
+ --item: #f9f7ec;
+ --item-background: #161c22;
+ --item-hover: #2a3138;
+ --code: #f0f6fc26;
+ }
+}
+[data-color-mode=light] {
+ --shadow: 0 1px 3px hsla(51, 90%, 42%, .35);
+ --shadow2: 0 2px 3px hsla(51, 50%, 44%, .35);
+ --header-bg-color: hsl(115, 31%, 31%);
+ --footer-bg-color: hsl(115, 31%, 31%);
+ --main-color: #383838;
+ --main-bg: #f9f7ec;
+ --main-bg-dark: #edebdd;
+ --gray-border: #d5d3cb;
+ --menu-hover: #5e5e5e;
+ --item: #525252;
+ --item-background: #fff;
+ --item-hover: #fdfcf2;
+ --code: #fff;
+}
+
+[data-color-mode=dark] {
+ --shadow: 0 0 6px rgba(0,0,0,.4);
+ --shadow2: 0 2px 3px rgba(0,0,0,.3);
+ --header-bg-color: #24292e;
+ --footer-bg-color: #24292e;
+ --main-color: #c9d1d9;
+ --main-bg: #0d1117;
+ --main-bg-dark: #161c22;
+ --gray-border: #444444;
+ --menu-hover: #2a3138;
+ --item: #f9f7ec;
+ --item-background: #161c22;
+ --item-hover: #2a3138;
+ --code: #f0f6fc26;
+}
diff --git a/app/utils/theme.js b/app/utils/theme.js
new file mode 100644
index 00000000000..11f12a0beea
--- /dev/null
+++ b/app/utils/theme.js
@@ -0,0 +1,34 @@
+import { getItem, setItem } from './local-storage';
+
+const THEME_KEY = 'tmeme';
+
+export const theme = {
+ loadSettingTheme() {
+ switch (getItem(THEME_KEY)) {
+ case 'light': {
+ this.useLightTheme();
+ break;
+ }
+ case 'dark': {
+ this.useDarkTheme();
+ break;
+ }
+ default: {
+ this.useSystemTheme();
+ break;
+ }
+ }
+ },
+ useSystemTheme() {
+ document.querySelector('html').dataset.colorMode = 'auto';
+ setItem(THEME_KEY, 'auto');
+ },
+ useLightTheme() {
+ document.querySelector('html').dataset.colorMode = 'light';
+ setItem(THEME_KEY, 'light');
+ },
+ useDarkTheme() {
+ document.querySelector('html').dataset.colorMode = 'dark';
+ setItem(THEME_KEY, 'dark');
+ },
+};
diff --git a/public/assets/download.svg b/public/assets/download.svg
index a754591d5ea..9feaac2fc24 100644
--- a/public/assets/download.svg
+++ b/public/assets/download.svg
@@ -1,6 +1,6 @@
diff --git a/tests/index.html b/tests/index.html
index 7366af85f72..5196b4e3e9c 100644
--- a/tests/index.html
+++ b/tests/index.html
@@ -1,5 +1,5 @@
-
+