diff --git a/api/channels.js b/api/channels.js
index 4751d5a..de9bc2c 100644
--- a/api/channels.js
+++ b/api/channels.js
@@ -24,7 +24,7 @@ module.exports = [
video.watched = watchedVideos.includes(video.videoId)
})
}
- return render(200, "pug/channel.pug", {url, data, subscribed, instanceOrigin})
+ return render(200, "pug/channel.pug", {settings, url, data, subscribed, instanceOrigin})
}
}
]
diff --git a/api/filters.js b/api/filters.js
index 189af7b..0563a60 100644
--- a/api/filters.js
+++ b/api/filters.js
@@ -9,8 +9,7 @@ const {Matcher, PatternCompileError} = require("../utils/matcher")
const filterMaxLength = 160
const regexpEnabledText = constants.server_setup.allow_regexp_filters ? "" : "not"
-function getCategories(req) {
- const user = getUser(req)
+function getCategories(user) {
const filters = user.getFilters()
// Sort filters into categories for display. Titles are already sorted.
@@ -39,7 +38,9 @@ function getCategories(req) {
module.exports = [
{
route: "/filters", methods: ["GET"], code: async ({req, url}) => {
- const categories = getCategories(req)
+ const user = getUser(req)
+ const categories = getCategories(user)
+ const settings = user.getSettingsOrDefaults()
let referrer = url.searchParams.get("referrer") || null
let type = null
@@ -54,7 +55,7 @@ module.exports = [
label = url.searchParams.get("label")
}
- return render(200, "pug/filters.pug", {categories, type, contents, label, referrer, filterMaxLength, regexpEnabledText})
+ return render(200, "pug/filters.pug", {settings, categories, type, contents, label, referrer, filterMaxLength, regexpEnabledText})
}
},
{
@@ -100,8 +101,10 @@ module.exports = [
return true
}, state => {
const {type, contents, label, compileError} = state
- const categories = getCategories(req)
- return render(400, "pug/filters.pug", {categories, type, contents, label, compileError, filterMaxLength, regexpEnabledText})
+ const user = getUser(req)
+ const categories = getCategories(user)
+ const settings = user.getSettingsOrDefaults()
+ return render(400, "pug/filters.pug", {settings, categories, type, contents, label, compileError, filterMaxLength, regexpEnabledText})
})
.last(state => {
const {type, contents, label} = state
diff --git a/api/pages.js b/api/pages.js
index 88e8635..761052f 100644
--- a/api/pages.js
+++ b/api/pages.js
@@ -1,16 +1,35 @@
const {render} = require("pinski/plugins")
+const {getUser} = require("../utils/getuser")
module.exports = [
{
route: "/", methods: ["GET"], code: async ({req}) => {
const userAgent = req.headers["user-agent"] || ""
const mobile = userAgent.toLowerCase().includes("mobile")
- return render(200, "pug/home.pug", {mobile})
+ const user = getUser(req)
+ const settings = user.getSettingsOrDefaults()
+ return render(200, "pug/home.pug", {settings, mobile})
}
},
{
- route: "/js-licenses", methods: ["GET"], code: async () => {
- return render(200, "pug/js-licenses.pug")
+ route: "/(?:js-)?licenses", methods: ["GET"], code: async ({req}) => {
+ const user = getUser(req)
+ const settings = user.getSettingsOrDefaults()
+ return render(200, "pug/licenses.pug", {settings})
+ }
+ },
+ {
+ route: "/cant-think", methods: ["GET"], code: async ({req}) => {
+ const user = getUser(req)
+ const settings = user.getSettingsOrDefaults()
+ return render(200, "pug/cant-think.pug", {settings})
+ }
+ },
+ {
+ route: "/privacy", methods: ["GET"], code: async ({req}) => {
+ const user = getUser(req)
+ const settings = user.getSettingsOrDefaults()
+ return render(200, "pug/privacy.pug", {settings})
}
}
]
diff --git a/api/search.js b/api/search.js
index 223b3b6..9cc9277 100644
--- a/api/search.js
+++ b/api/search.js
@@ -26,7 +26,7 @@ module.exports = [
const filters = user.getFilters()
results = converters.applyVideoFilters(results, filters).videos
- return render(200, "pug/search.pug", {url, query, results, instanceOrigin})
+ return render(200, "pug/search.pug", {settings, url, query, results, instanceOrigin})
}
}
]
diff --git a/api/video.js b/api/video.js
index 1161fd5..89084fa 100644
--- a/api/video.js
+++ b/api/video.js
@@ -111,7 +111,7 @@ module.exports = [
// Check if playback is allowed
const videoTakedownInfo = db.prepare("SELECT id, org, url FROM TakedownVideos WHERE id = ?").get(id)
if (videoTakedownInfo) {
- return render(451, "pug/takedown-video.pug", videoTakedownInfo)
+ return render(451, "pug/takedown-video.pug", Object.assign({settings}, videoTakedownInfo))
}
// Media fragment
@@ -129,7 +129,7 @@ module.exports = [
// Work out how to fetch the video
if (req.method === "GET") {
if (settings.local) { // skip to the local fetching page, which will then POST video data in a moment
- return render(200, "pug/local-video.pug", {id})
+ return render(200, "pug/local-video.pug", {settings, id})
}
var instanceOrigin = settings.instance
var outURL = `${instanceOrigin}/api/v1/videos/${id}`
@@ -153,7 +153,7 @@ module.exports = [
// automatically add the entry to the videos list, so it won't be fetched again
const args = {id, ...channelTakedownInfo}
db.prepare("INSERT INTO TakedownVideos (id, org, url) VALUES (@id, @org, @url)").run(args)
- return render(451, "pug/takedown-video.pug", channelTakedownInfo)
+ return render(451, "pug/takedown-video.pug", Object.assign({settings}, channelTakedownInfo))
}
// process stream list ordering
@@ -225,7 +225,7 @@ module.exports = [
// Create appropriate formatted message
const message = render(0, `pug/errors/${errorType}.pug`, locals).content
- return render(500, "pug/video.pug", {video: {videoId: id}, error: true, message})
+ return render(500, "pug/video.pug", {video: {videoId: id}, error: true, message, settings})
}
}
}
diff --git a/html/static/images/arrow-down-wide.svg b/html/static/images/arrow-down-wide-dark.svg
similarity index 100%
rename from html/static/images/arrow-down-wide.svg
rename to html/static/images/arrow-down-wide-dark.svg
diff --git a/html/static/images/arrow-down-wide-light.svg b/html/static/images/arrow-down-wide-light.svg
new file mode 100644
index 0000000..3be01f2
--- /dev/null
+++ b/html/static/images/arrow-down-wide-light.svg
@@ -0,0 +1 @@
+
diff --git a/html/static/images/settings.svg b/html/static/images/settings.svg
index a28bb15..c381540 100644
--- a/html/static/images/settings.svg
+++ b/html/static/images/settings.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/html/static/images/subscriptions.svg b/html/static/images/subscriptions.svg
index 420d620..0399a04 100644
--- a/html/static/images/subscriptions.svg
+++ b/html/static/images/subscriptions.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/pug/includes/layout.pug b/pug/includes/layout.pug
index c837122..2cc0d91 100644
--- a/pug/includes/layout.pug
+++ b/pug/includes/layout.pug
@@ -3,7 +3,8 @@ html
head
meta(charset="utf-8")
meta(name="viewport" content="width=device-width, initial-scale=1")
- link(rel="stylesheet" type="text/css" href=getStaticURL("sass", "/main.sass"))
+ - const theme = settings && ["dark", "light", "edgeless-light"][settings.theme] || "dark"
+ link(rel="stylesheet" type="text/css" href=getStaticURL("sass", `/${theme}.sass`))
script(type="module" src=getStaticURL("html", "/static/js/focus.js"))
block head
@@ -15,13 +16,9 @@ html
.links
a(href="/").link.home CloudTube
a(href="/subscriptions" title="Subscriptions").link.icon-link
- svg(width=30 height=25)
- image(href=getStaticURL("html", "/static/images/subscriptions.svg") alt="Subscriptions.").icon
- title Subscriptions
+ != icons.get("subscriptions")
a(href="/settings" title="Settings").link.icon-link
- svg(width=25 height=25)
- image(href=getStaticURL("html", "/static/images/settings.svg") alt="Settings.").icon
- title Settings
+ != icons.get("settings")
form(method="get" action="/search").search-form
input(type="text" placeholder="Search" aria-label="Search a video" name="q" autocomplete="off" value=query).search
diff --git a/pug/settings.pug b/pug/settings.pug
index 80fda31..16f5741 100644
--- a/pug/settings.pug
+++ b/pug/settings.pug
@@ -36,6 +36,16 @@ block content
form(method="post" action="/settings")
+fieldset("Settings")
+ +select({
+ id: "theme",
+ label: "Theme",
+ options: [
+ {value: "0", text: "Standard dark"},
+ {value: "1", text: "Standard light"},
+ {value: "2", text: "Edgeless light"}
+ ]
+ })
+
+input({
id: "instance",
label: "Instance",
diff --git a/pug/subscriptions.pug b/pug/subscriptions.pug
index 4e8fbef..2846b01 100644
--- a/pug/subscriptions.pug
+++ b/pug/subscriptions.pug
@@ -31,8 +31,8 @@ block content
if settings.save_history
input(type="checkbox" id="watched-videos-display")
- .watched-videos-display-container
- label(for="watched-videos-display").watched-videos-display-label Hide watched videos
+ .checkbox-hider__container
+ label(for="watched-videos-display").checkbox-hider__label Hide watched videos
each video in videos
+video_list_item("subscriptions-video", video, instanceOrigin, {showMarkWatched: settings.save_history && !video.watched})
diff --git a/pug/video.pug b/pug/video.pug
index 1000b92..28b8416 100644
--- a/pug/video.pug
+++ b/pug/video.pug
@@ -21,6 +21,7 @@ block content
noscript
meta(http-equiv="refresh" content=`${video.lengthSeconds+5};url=/watch?v=${first.videoId}&continuous=1&session-watched=${sessionWatchedNext}`)
.video-page(class={
+ "video-page--recommended-side": settings.recommended_mode === 0,
"video-page--recommended-below": settings.recommended_mode === 1,
"video-page--recommended-hidden": settings.recommended_mode === 2
})
diff --git a/sass/dark.sass b/sass/dark.sass
new file mode 100644
index 0000000..fa4b58c
--- /dev/null
+++ b/sass/dark.sass
@@ -0,0 +1,9 @@
+@use "themes/dark" as *
+@use "includes/main" with ($_theme: $theme)
+
+@use "theme-modules/edgeless" with ($_theme: $theme)
+
+// navigation shadow
+.main-nav
+ position: relative // needed for box shadow to overlap related videos section
+ box-shadow: 0px 0px 20px 5px rgba(0, 0, 0, 0.1)
diff --git a/sass/edgeless-light.sass b/sass/edgeless-light.sass
new file mode 100644
index 0000000..48ed88d
--- /dev/null
+++ b/sass/edgeless-light.sass
@@ -0,0 +1,4 @@
+@use "themes/edgeless-light" as *
+@use "includes/main" with ($_theme: $theme)
+
+@use "theme-modules/edgeless" with ($_theme: $theme)
diff --git a/sass/includes/base.sass b/sass/includes/_base.sass
similarity index 55%
rename from sass/includes/base.sass
rename to sass/includes/_base.sass
index fa23dd3..06a7521 100644
--- a/sass/includes/base.sass
+++ b/sass/includes/_base.sass
@@ -1,8 +1,10 @@
-@use "colors.sass" as c
+$_theme: () !default
+
+@use "sass:map"
body
- background-color: c.$bg-dark
- color: c.$fg-main
+ background-color: map.get($_theme, "bg-2")
+ color: map.get($_theme, "fg-main")
font-family: "Bariol", sans-serif
font-size: 18px
margin: 0
@@ -13,13 +15,13 @@ body
flex-direction: column
a
- color: c.$link
+ color: map.get($_theme, "link")
pre, code
font-size: 0.88em
code
- background: c.$bg-darker
+ background: map.get($_theme, "bg-1")
padding: 3px 5px
border-radius: 4px
@@ -32,7 +34,7 @@ button
cursor: pointer
::placeholder
- color: #c4c4c4
+ color: map.get($_theme, "placeholder")
opacity: 1
// focus section
@@ -48,19 +50,20 @@ button
select:-moz-focusring
color: transparent
- text-shadow: 0 0 0 c.$fg-bright
+ text-shadow: 0 0 0 map.get($_theme, "fg-bright")
body.show-focus
a, select, button, input, video, summary
&:focus
- outline: 2px dotted #ddd
+ outline: 2px dotted map.get($_theme, "fg-main")
video
background-color: black
details
- background-color: c.$bg-accent-x
+ background-color: map.get($_theme, "bg-3")
padding: 12px
+ border: 1px solid map.get($_theme, "edge-grey")
border-radius: 8px
summary
@@ -68,20 +71,22 @@ details
line-height: 1
margin-bottom: 0
user-select: none
- color: c.$fg-main
+ color: map.get($_theme, "fg-main")
&[open] summary
- margin-bottom: 16px
+ padding-bottom: 12px
+ border-bottom: 1px solid map.get($_theme, "edge-grey")
+ margin-bottom: 8px
table
- background-color: c.$bg-darker
+ background-color: map.get($_theme, "bg-1")
table, td, th
- border: 1px solid c.$edge-grey
+ border: 1px solid map.get($_theme, "edge-grey")
border-collapse: collapse
td, th
padding: 4px 8px
thead, tr:nth-child(even)
- background-color: c.$bg-darkest
+ background-color: map.get($_theme, "bg-0")
diff --git a/sass/includes/buttons.sass b/sass/includes/_buttons.sass
similarity index 66%
rename from sass/includes/buttons.sass
rename to sass/includes/_buttons.sass
index 6a40741..162c573 100644
--- a/sass/includes/buttons.sass
+++ b/sass/includes/_buttons.sass
@@ -1,10 +1,12 @@
+$_theme: () !default
+
@use "sass:selector"
-@use "colors.sass" as c
+@use "sass:map"
@mixin button-base
-webkit-appearance: none
-moz-appearance: none
- color: c.$fg-bright
+ color: map.get($_theme, "fg-bright")
border: none
border-radius: 4px
padding: 8px
@@ -14,7 +16,7 @@
@at-root #{selector.unify(&, "select")}
padding: 8px 27px 8px 8px
- background: url(/static/images/arrow-down-wide.svg) right 53% no-repeat c.$bg-accent-x
+ background: map.get($_theme, "image-dropdown") right 53% no-repeat map.get($_theme, "bg-4")
@at-root #{selector.unify(&, "a")}
padding: 7px 8px
@@ -31,12 +33,12 @@
@mixin button-bg
@include button-base
- background-color: c.$bg-accent-x
+ background-color: map.get($_theme, "bg-4")
@mixin border-button
@include button-bg
- border: 1px solid c.$edge-grey
+ border: 1px solid map.get($_theme, "edge-grey")
@mixin button-size
margin: 4px
@@ -44,10 +46,10 @@
@mixin button-hover
&:hover
- background-color: c.$bg-accent
+ background-color: map.get($_theme, "bg-3")
&:active
- background-color: c.$bg-dark
+ background-color: map.get($_theme, "bg-2")
.base-border-look
@include border-button
@@ -62,13 +64,13 @@
@include button-size
-webkit-appearance: none
-moz-appearance: none
- color: c.$fg-bright
+ color: map.get($_theme, "fg-bright")
text-decoration: none
line-height: 1.25
margin: 0
padding: 8px 20px
- background: c.$bg-accent
- border: solid c.$bg-darker
+ background: map.get($_theme, "bg-3")
+ border: solid map.get($_theme, "edge-grey")
border-width: 1px 0px 0px
text-align: left
@@ -76,7 +78,7 @@
border-width: 1px 0px 1px
&:hover
- background: c.$bg-accent-x
+ background: map.get($_theme, "bg-4")
&:active
- background: c.$bg-darker
+ background: map.get($_theme, "bg-1")
diff --git a/sass/includes/cant-think-page.sass b/sass/includes/_cant-think-page.sass
similarity index 88%
rename from sass/includes/cant-think-page.sass
rename to sass/includes/_cant-think-page.sass
index ae9a594..36399ef 100644
--- a/sass/includes/cant-think-page.sass
+++ b/sass/includes/_cant-think-page.sass
@@ -1,5 +1,7 @@
+$_theme: () !default
+
@use "sass:list"
-@use "colors.sass" as c
+@use "sass:map"
.cant-think-page
.main-nav
@@ -13,7 +15,7 @@
box-sizing: border-box
.page-narration
- background-color: c.$bg-accent
+ background-color: map.get($_theme, "bg-3")
border: 1px solid #aaa
color: #fff
border-radius: 0
@@ -28,7 +30,7 @@
.leave
margin: 26px 32px !important
- color: #aaa
+ color: map.get($_theme, "fg-dim")
$sizes: 14px 16px 21px 30px 72px
@each $size in $sizes
@@ -37,7 +39,7 @@
&.leave__final
font-weight: bold
- color: #f2f2f2
+ color: map.get($_theme, "fg-bright")
text-align: center
.leave__actions
diff --git a/sass/includes/channel-page.sass b/sass/includes/_channel-page.sass
similarity index 77%
rename from sass/includes/channel-page.sass
rename to sass/includes/_channel-page.sass
index 2b4cfd8..7f91f61 100644
--- a/sass/includes/channel-page.sass
+++ b/sass/includes/_channel-page.sass
@@ -1,6 +1,8 @@
-@use "colors.sass" as c
-@use "video-list-item.sass" as *
-@use "_dimensions.sass" as dimensions
+$_theme: () !default
+
+@use "sass:map"
+@use "_dimensions" as dimensions
+@use "video-list-item" as *
.channel-page
padding: 40px 20px 20px
@@ -17,7 +19,7 @@
align-self: flex-start
.channel-data
- background-color: c.$bg-darker
+ background-color: map.get($_theme, "bg-1")
padding: 24px
margin: 12px 0px 24px
border-radius: 8px
@@ -44,11 +46,11 @@
.name
font-size: 30px
font-weight: normal
- color: c.$fg-bright
+ color: map.get($_theme, "fg-bright")
margin: 0
.subscribers
- color: c.$fg-main
+ color: map.get($_theme, "fg-main")
font-size: 18px
.subscribe-form
@@ -61,7 +63,8 @@
line-height: 1
border-radius: 8px
font-size: 22px
- background-color: c.$power-deep
+ background-color: map.get($_theme, "power-deep")
+ color: map.get($_theme, "power-fg")
border: none
.description
diff --git a/sass/includes/filters-page.sass b/sass/includes/_filters-page.sass
similarity index 64%
rename from sass/includes/filters-page.sass
rename to sass/includes/_filters-page.sass
index 4525f2b..85e42b3 100644
--- a/sass/includes/filters-page.sass
+++ b/sass/includes/_filters-page.sass
@@ -1,10 +1,12 @@
-@use "colors.sass" as c
+$_theme: () !default
+
+@use "sass:map"
@mixin filter-notice
margin-top: 24px
padding: 12px
border-radius: 8px
- background-color: c.$bg-darker
+ background-color: map.get($_theme, "bg-1")
white-space: pre-line
.filters-page
@@ -20,23 +22,23 @@
.filter-confirmation-notice
@include filter-notice
- color: c.$fg-warning
+ color: map.get($_theme, "fg-warning")
.filter-compile-error
@include filter-notice
&__header
- color: c.$fg-warning
+ color: map.get($_theme, "fg-warning")
&__trace
- background-color: c.$bg-darkest
+ background-color: map.get($_theme, "bg-0")
padding: 6px
.save-filter
margin-top: 12px
.border-look
- background-color: c.$bg-darker
+ background-color: map.get($_theme, "bg-1")
font-size: 22px
padding: 7px 16px 8px
font-size: 24px
@@ -48,17 +50,17 @@
.filter
display: flex
padding: 5px 0
- border-top: 1px solid c.$edge-grey
+ border-top: 1px solid map.get($_theme, "edge-grey")
&:last-child
- border-bottom: 1px solid c.$edge-grey
+ border-bottom: 1px solid map.get($_theme, "edge-grey")
&__details
flex: 1
&__type
font-size: 15px
- color: c.$fg-dim
+ color: map.get($_theme, "fg-dim")
&__remove
flex-shrink: 0
diff --git a/sass/includes/footer.sass b/sass/includes/_footer.sass
similarity index 76%
rename from sass/includes/footer.sass
rename to sass/includes/_footer.sass
index b624fa5..e0012c1 100644
--- a/sass/includes/footer.sass
+++ b/sass/includes/_footer.sass
@@ -1,4 +1,6 @@
-@use "./colors.sass" as c
+$_theme: () !default
+
+@use "sass:map"
.footer__container
flex: 1
@@ -10,9 +12,10 @@
display: flex
flex-direction: column
align-items: center
- background-color: c.$bg-darkest
+ background-color: map.get($_theme, "bg-dim")
margin: 40px 0 0
padding: 10px 10px 30px
+ border-top: 1px solid map.get($_theme, "edge-grey")
.footer__cols
display: flex
diff --git a/sass/includes/forms.sass b/sass/includes/_forms.sass
similarity index 75%
rename from sass/includes/forms.sass
rename to sass/includes/_forms.sass
index 7c8f0a7..e946d19 100644
--- a/sass/includes/forms.sass
+++ b/sass/includes/_forms.sass
@@ -1,7 +1,9 @@
-@use "colors.sass" as c
+$_theme: () !default
+
+@use "sass:map"
@mixin disabled
- background-color: c.$bg-dark
+ background-color: map.get($_theme, "bg-2")
color: #808080
fieldset
@@ -20,7 +22,7 @@ fieldset
font-size: 28px
font-weight: bold
padding: 0
- border-bottom: 1px solid #333
+ border-bottom: 1px solid map.get($_theme, "edge-grey") // TODO: originally contrasted more
line-height: 1.56
@media screen and (max-width: 400px)
@@ -36,7 +38,7 @@ fieldset
position: relative
padding-bottom: 5px
margin-bottom: 5px
- border-bottom: 1px solid #999
+ border-bottom: 1px solid map.get($_theme, "edge-grey")
@media screen and (max-width: 400px)
flex-direction: column
@@ -52,7 +54,7 @@ fieldset
&__label
grid-area: label
padding: 8px 8px 8px 0px
- color: #fff
+ color: map.get($_theme, "fg-main")
&__input
grid-area: input
@@ -63,7 +65,7 @@ fieldset
white-space: pre-line
margin: 12px 0px 18px
font-size: 16px
- color: #ccc
+ color: map.get($_theme, "fg-dim")
line-height: 1.2
//
@@ -79,7 +81,7 @@ fieldset
width: 16px
height: 16px
padding: 0px
- border: 1px solid #666
+ border: 1px solid map.get($_theme, "edge-grey")
border-radius: 3px
margin-left: 8px
position: relative
@@ -110,18 +112,19 @@ fieldset
height: 42px
margin: 0
- .#{$base}-container
- position: relative
- display: grid // why does the default not work???
- top: -42px
- background: c.$bg-accent-x
- line-height: 1
- border-radius: 8px
- margin-bottom: -18px
+.checkbox-hider__container
+ position: relative
+ display: grid // why does the default not work???
+ top: -42px
+ background: map.get($_theme, "bg-3")
+ line-height: 1
+ border: 1px solid map.get($_theme, "edge-grey")
+ border-radius: 8px
+ margin-bottom: -18px
- .#{$base}-label
- padding: 12px 0px 12px 32px
- cursor: pointer
+ .checkbox-hider__label
+ padding: 12px 0px 12px 32px
+ cursor: pointer
@mixin single-button-form
display: inline-block
diff --git a/sass/includes/home-page.sass b/sass/includes/_home-page.sass
similarity index 80%
rename from sass/includes/home-page.sass
rename to sass/includes/_home-page.sass
index 77c65b2..0b8b42b 100644
--- a/sass/includes/home-page.sass
+++ b/sass/includes/_home-page.sass
@@ -1,4 +1,6 @@
-@use "colors.sass" as c
+$_theme: () !default
+
+@use "sass:map"
.home-page
padding: 40px
@@ -18,8 +20,8 @@
padding: 16px
border-radius: 4px
font-size: 20px
- background-color: c.$bg-darker
- color: c.$fg-main
+ background-color: map.get($_theme, "bg-1")
+ color: map.get($_theme, "fg-main")
p
margin: 0 32px
diff --git a/sass/includes/licenses-page.sass b/sass/includes/_licenses-page.sass
similarity index 71%
rename from sass/includes/licenses-page.sass
rename to sass/includes/_licenses-page.sass
index 1794067..0b66909 100644
--- a/sass/includes/licenses-page.sass
+++ b/sass/includes/_licenses-page.sass
@@ -1,3 +1,5 @@
+$_theme: () !default
+
.js-licenses-page
max-width: 800px
margin: 0 auto
diff --git a/sass/includes/_main.sass b/sass/includes/_main.sass
new file mode 100644
index 0000000..c2a076f
--- /dev/null
+++ b/sass/includes/_main.sass
@@ -0,0 +1,32 @@
+$_theme: () !default
+
+@use "sass:selector"
+
+// preload second-level includes with the theme (there will be conflicts due to reconfiguration they are loaded individually)
+// this isn't _exactly_ what @forward is supposed to be used for, but it's the best option here
+@forward "video-list-item" show _ with ($_theme: $_theme)
+@forward "forms" show _ with ($_theme: $_theme)
+@forward "buttons" show _ with ($_theme: $_theme)
+
+@use "base" with ($_theme: $_theme)
+@use "video-page" with ($_theme: $_theme)
+@use "search-page" with ($_theme: $_theme)
+@use "home-page" with ($_theme: $_theme)
+@use "channel-page" with ($_theme: $_theme)
+@use "subscriptions-page" with ($_theme: $_theme)
+@use "settings-page" with ($_theme: $_theme)
+@use "cant-think-page" with ($_theme: $_theme)
+@use "privacy-page" with ($_theme: $_theme)
+@use "licenses-page" with ($_theme: $_theme)
+@use "filters-page" with ($_theme: $_theme)
+@use "takedown-page" with ($_theme: $_theme)
+@use "nav" with ($_theme: $_theme)
+@use "footer" with ($_theme: $_theme)
+
+@font-face
+ font-family: "Bariol"
+ src: url(/static/fonts/bariol.woff?statichash=1)
+
+.button-container
+ display: flex
+ flex-wrap: wrap
diff --git a/sass/includes/nav.sass b/sass/includes/_nav.sass
similarity index 52%
rename from sass/includes/nav.sass
rename to sass/includes/_nav.sass
index f5e66dd..f1eab5f 100644
--- a/sass/includes/nav.sass
+++ b/sass/includes/_nav.sass
@@ -1,12 +1,14 @@
-@use "colors.sass" as c
-@use "buttons.sass" as *
-@use "_dimensions.sass" as dimensions
+$_theme: () !default
+
+@use "sass:map"
+@use "buttons" as *
+@use "_dimensions" as dimensions
.main-nav
- background-color: c.$bg-accent
+ background-color: map.get($_theme, "bg-nav")
display: flex
padding: 8px
- box-shadow: 0px 0px 20px 5px rgba(0, 0, 0, 0.1)
+ border-bottom: 1px solid map.get($_theme, "edge-grey")
+dimensions.thin
display: block
@@ -30,10 +32,16 @@
font-weight: bold
&, &:visited
- color: #fff
+ color: map.get($_theme, "fg-bright")
&:focus, &:hover
- background-color: c.$bg-accent-x
+ background-color: map.get($_theme, "bg-4")
+
+ &.icon-link
+ color: map.get($_theme, "fg-dim")
+
+ &:hover, &:focus
+ color: map.get($_theme, "fg-bright")
.search-form
display: flex
@@ -44,8 +52,7 @@
@include button-bg
padding: 10px
flex: 1
- margin: 1px
+ border: 1px solid map.get($_theme, "bg-nav")
&:hover, &:focus
- border: 1px solid c.$edge-grey
- margin: 0px
+ border-color: map.get($_theme, "edge-grey")
diff --git a/sass/includes/privacy-page.sass b/sass/includes/_privacy-page.sass
similarity index 69%
rename from sass/includes/privacy-page.sass
rename to sass/includes/_privacy-page.sass
index 51c8ac0..9f38ec3 100644
--- a/sass/includes/privacy-page.sass
+++ b/sass/includes/_privacy-page.sass
@@ -1,3 +1,5 @@
+$_theme: () !default
+
.privacy-page
max-width: 600px
margin: 0 auto
diff --git a/sass/includes/search-page.sass b/sass/includes/_search-page.sass
similarity index 63%
rename from sass/includes/search-page.sass
rename to sass/includes/_search-page.sass
index b12de93..0d42cca 100644
--- a/sass/includes/search-page.sass
+++ b/sass/includes/_search-page.sass
@@ -1,5 +1,7 @@
-@use "video-list-item.sass" as *
-@use "colors.sass" as c
+$_theme: () !default
+
+@use "sass:map"
+@use "video-list-item" as *
.search-page
padding: 40px 20px 20px
diff --git a/sass/includes/settings-page.sass b/sass/includes/_settings-page.sass
similarity index 77%
rename from sass/includes/settings-page.sass
rename to sass/includes/_settings-page.sass
index 279c761..a3eb77f 100644
--- a/sass/includes/settings-page.sass
+++ b/sass/includes/_settings-page.sass
@@ -1,5 +1,7 @@
-@use "forms.sass" as forms
-@use "colors.sass" as c
+$_theme: () !default
+
+@use "sass:map"
+@use "forms" as forms
.settings-page
padding: 40px 20px 20px
@@ -19,8 +21,9 @@
.more-settings
margin-top: 24px
padding: 12px
+ border: 1px solid map.get($_theme, "edge-grey")
border-radius: 8px
- background-color: c.$bg-accent-x
+ background-color: map.get($_theme, "bg-3")
&__list
margin: 0
@@ -34,7 +37,7 @@
margin-top: 24px
.delete-confirm-container
- background: c.$bg-darker
+ background: map.get($_theme, "bg-1")
margin-bottom: -36px
@include forms.checkbox-hider("delete-confirm")
diff --git a/sass/includes/subscriptions-page.sass b/sass/includes/_subscriptions-page.sass
similarity index 83%
rename from sass/includes/subscriptions-page.sass
rename to sass/includes/_subscriptions-page.sass
index 46f4050..bd70f37 100644
--- a/sass/includes/subscriptions-page.sass
+++ b/sass/includes/_subscriptions-page.sass
@@ -1,6 +1,8 @@
-@use "colors.sass" as c
-@use "video-list-item.sass" as *
-@use "forms.sass" as forms
+$_theme: () !default
+
+@use "sass:map"
+@use "forms" as forms
+@use "video-list-item" as *
.subscriptions-page
padding: 40px 20px 20px
@@ -33,7 +35,7 @@
.name
font-size: 22px
- color: c.$fg-main
+ color: map.get($_theme, "fg-main")
@include forms.checkbox-hider("watched-videos-display")
diff --git a/sass/includes/_takedown-page.sass b/sass/includes/_takedown-page.sass
new file mode 100644
index 0000000..2535e5f
--- /dev/null
+++ b/sass/includes/_takedown-page.sass
@@ -0,0 +1,16 @@
+$_theme: () !default
+
+@use "sass:map"
+
+.takedown-page
+ max-width: 700px
+ margin: 0 auto
+
+ .new-section
+ margin-top: 200px
+
+ .important-section
+ padding: 4px 20px
+ border: 1px solid map.get($_theme, "edge-grey")
+ color: map.get($_theme, "fg-bright")
+ background-color: map.get($_theme, "bg-1")
diff --git a/sass/includes/video-list-item.sass b/sass/includes/_video-list-item.sass
similarity index 84%
rename from sass/includes/video-list-item.sass
rename to sass/includes/_video-list-item.sass
index d240360..321a964 100644
--- a/sass/includes/video-list-item.sass
+++ b/sass/includes/_video-list-item.sass
@@ -1,5 +1,7 @@
-@use "colors.sass" as c
-@use "_dimensions.sass" as dimensions
+$_theme: () !default
+
+@use "sass:map"
+@use "_dimensions" as dimensions
// navigator hacks
.thumbnail > .thumbnail__options-container
@@ -30,6 +32,7 @@
&__show-more
display: block
height: $more-size
+ color: #fff
line-height: 16px
font-size: 25px
text-align: center
@@ -52,7 +55,7 @@
&__options-list
pointer-events: auto
display: grid
- background-color: c.$bg-accent
+ background-color: map.get($_theme, "bg-3")
padding: 8px 0px
border-radius: 8px
box-shadow: 0 2px 6px 2px #000
@@ -67,7 +70,7 @@
right: 0
transform: translate(-6px, -1px) rotate(-45deg)
clip-path: polygon(-5% -20%, 120% -20%, 120% 125%)
- background-color: c.$bg-accent
+ background-color: map.get($_theme, "bg-3")
box-shadow: 0px 0px 4px 0px #000
pointer-events: none
@@ -80,7 +83,7 @@
margin-bottom: 12px
@at-root .video-list-item--watched#{&}
- background: c.$bg-darker
+ background: map.get($_theme, "bg-dim")
padding: 4px 4px 0px
margin: -4px -4px 8px
@@ -93,7 +96,7 @@
.thumbnail
position: relative
display: flex
- background: c.$bg-darkest
+ background: map.get($_theme, "bg-0")
&__link
font-size: 0 // remove whitespace around the image
@@ -106,7 +109,7 @@
position: absolute
bottom: 3px
right: 3px
- color: c.$fg-bright
+ color: #fff
font-size: 14px
background: rgba(20, 20, 20, 0.85)
line-height: 1
@@ -119,20 +122,20 @@
line-height: 1.2
.title-link
- color: c.$fg-main
+ color: map.get($_theme, "fg-main")
text-decoration: none
.author-line
margin-top: 4px
font-size: 15px
- color: c.$fg-dim
+ color: map.get($_theme, "fg-dim")
.author
- color: c.$fg-dim
+ color: map.get($_theme, "fg-dim")
text-decoration: none
&:hover, &:active
- color: c.$fg-bright
+ color: map.get($_theme, "fg-bright")
text-decoration: underline
@mixin recommendation-item
@@ -176,15 +179,15 @@
.author-line
font-size: 15px
- color: c.$fg-main
+ color: map.get($_theme, "fg-main")
.author
- color: c.$fg-main
+ color: map.get($_theme, "fg-main")
.description
margin-top: 16px
font-size: 15px
- color: c.$fg-dim
+ color: map.get($_theme, "fg-dim")
+dimensions.thin
.description
@@ -195,7 +198,7 @@
.description b
font-weight: normal
- color: c.$fg-main
+ color: map.get($_theme, "fg-main")
@mixin channel-video
@include large-item
diff --git a/sass/includes/video-page.sass b/sass/includes/_video-page.sass
similarity index 77%
rename from sass/includes/video-page.sass
rename to sass/includes/_video-page.sass
index f134825..6774a83 100644
--- a/sass/includes/video-page.sass
+++ b/sass/includes/_video-page.sass
@@ -1,11 +1,11 @@
-@use "colors.sass" as c
-@use "video-list-item.sass" as *
+$_theme: () !default
+
+@use "sass:map"
+@use "video-list-item" as *
.video-page
display: grid
grid-auto-flow: row
- padding: 20px
- grid-gap: 16px
@media screen and (min-width: 1000px)
grid-template-columns: 1fr 400px
@@ -13,10 +13,25 @@
&--recommended-below, &--recommended-hidden
grid-template-columns: none
- &--recommended-hidden .related-videos
- display: none
+ &--recommended-side
+ .related-videos
+ border-left: 1px solid map.get($_theme, "edge-grey")
+ padding-left: 12px
+ padding-right: 20px
+ background-color: map.get($_theme, "bg-4")
+ padding-top: 12px
+
+ &--recommended-below
+ .related-videos
+ padding: 20px
+
+ &--recommended-hidden
+ .related-videos
+ display: none
.main-video-section
+ padding: 20px
+
.video-container
text-align: center
@@ -27,7 +42,7 @@
max-height: 80vh
.stream-notice
- background: c.$bg-darkest
+ background: map.get($_theme, "bg-0")
padding: 4px
.info
@@ -45,15 +60,15 @@
margin: 0px 0px 4px
font-size: 30px
font-weight: normal
- color: c.$fg-bright
+ color: map.get($_theme, "fg-bright")
word-break: break-word
.author-link
- color: c.$fg-main
+ color: map.get($_theme, "fg-main")
text-decoration: none
&:hover, &:active
- color: c.$fg-bright
+ color: map.get($_theme, "fg-bright")
text-decoration: underline
.info-secondary
@@ -75,7 +90,7 @@
margin: 16px 4px
padding: 12px
border-radius: 4px
- background-color: c.$bg-darkest
+ background-color: map.get($_theme, "bg-0")
&__description
margin-left: 12px
@@ -85,7 +100,7 @@
&__script-warning
font-size: 15px
- color: c.$fg-warning
+ color: map.get($_theme, "fg-warning")
&__buttons
display: flex
@@ -97,12 +112,12 @@
line-height: 1.4
word-break: break-word
margin: 16px 4px 4px 4px
- background-color: c.$bg-accent-area
+ background-color: map.get($_theme, "bg-5")
padding: 12px
border-radius: 4px
- --regular-background: #{c.$bg-accent-area}
- --highlight-background: #{c.$bg-darker}
+ --regular-background: #{map.get($_theme, "bg-5")}
+ --highlight-background: #{map.get($_theme, "bg-1")}
.subscribe-form
display: inline-block
diff --git a/sass/includes/colors.sass b/sass/includes/colors.sass
deleted file mode 100644
index e60e409..0000000
--- a/sass/includes/colors.sass
+++ /dev/null
@@ -1,17 +0,0 @@
-$bg-darkest: #202123
-$bg-darker: #303336
-$bg-dark: #36393f
-$bg-accent: #4f5359
-$bg-accent-x: #3f4247
-$bg-accent-area: #44474b
-
-$fg-bright: #fff
-$fg-main: #ddd
-$fg-dim: #bbb
-$fg-warning: #fdca6d
-
-$edge-grey: #a0a0a0
-
-$link: #8ac2f9
-
-$power-deep: #c62727
diff --git a/sass/includes/takedown-page.sass b/sass/includes/takedown-page.sass
deleted file mode 100644
index 36ede5d..0000000
--- a/sass/includes/takedown-page.sass
+++ /dev/null
@@ -1,14 +0,0 @@
-@use "colors.sass" as c
-
-.takedown-page
- max-width: 700px
- margin: 0 auto
-
- .new-section
- margin-top: 200px
-
- .important-section
- padding: 4px 20px
- border: 1px solid c.$edge-grey
- color: c.$fg-bright
- background-color: c.$bg-darker
diff --git a/sass/light.sass b/sass/light.sass
new file mode 100644
index 0000000..4cece7e
--- /dev/null
+++ b/sass/light.sass
@@ -0,0 +1,2 @@
+@use "themes/light" as *
+@use "includes/main" with ($_theme: $theme)
diff --git a/sass/main.sass b/sass/main.sass
deleted file mode 100644
index 17c15df..0000000
--- a/sass/main.sass
+++ /dev/null
@@ -1,30 +0,0 @@
-@use "includes/colors.sass" as c
-
-@use "includes/base.sass"
-@use "sass:selector"
-@use "includes/video-page.sass"
-@use "includes/search-page.sass"
-@use "includes/home-page.sass"
-@use "includes/channel-page.sass"
-@use "includes/subscriptions-page.sass"
-@use "includes/settings-page.sass"
-@use "includes/cant-think-page.sass"
-@use "includes/privacy-page.sass"
-@use "includes/licenses-page.sass"
-@use "includes/filters-page.sass"
-@use "includes/takedown-page.sass"
-@use "includes/forms.sass"
-@use "includes/nav.sass"
-@use "includes/footer.sass"
-
-@font-face
- font-family: "Bariol"
- src: url(/static/fonts/bariol.woff?statichash=1)
-
-.icon-link:hover, .icon-link:focus
- .icon
- filter: brightness(2)
-
-.button-container
- display: flex
- flex-wrap: wrap
diff --git a/sass/theme-modules/_edgeless.sass b/sass/theme-modules/_edgeless.sass
new file mode 100644
index 0000000..a23c11e
--- /dev/null
+++ b/sass/theme-modules/_edgeless.sass
@@ -0,0 +1,27 @@
+$_theme: () !default
+
+@use "sass:map"
+
+// remove separating edges
+.main-nav, .footer__center, .video-page--recommended-side .related-videos
+ border: none
+
+// no background change to recommended videos sidebar
+.video-page--recommended-side .related-videos
+ background-color: map.get($_theme, "bg-2")
+
+// navigation shadow
+.main-nav
+ position: relative // needed for box shadow to overlap related videos section
+ box-shadow: 0px 0px 20px 5px rgba(0, 0, 0, 0.1)
+
+// thumbnail dropdown menu dividers
+.menu-look
+ border-color: map.get($_theme, "bg-0")
+
+// details areas
+details, .checkbox-hider__container, .more-settings
+ border: none
+details[open] summary
+ border: none
+ margin-bottom: 4px
diff --git a/sass/themes/_dark.scss b/sass/themes/_dark.scss
new file mode 100644
index 0000000..0308b83
--- /dev/null
+++ b/sass/themes/_dark.scss
@@ -0,0 +1,38 @@
+// Defined in scss file instead of sass because indented syntax does not have multiline maps
+// https://github.com/sass/sass/issues/216
+
+@use "sass:map";
+
+// This section is for colour shades
+$theme: (
+ // darker
+ "bg-0": #252628,
+ "bg-1": #303336,
+ // regular
+ "bg-2": #36393f,
+ // lighter
+ "bg-3": #3f4247, // slightly
+ "bg-4": #44474b, // noticably
+ "bg-5": #4f5359, // brightly
+
+ "fg-bright": #fff,
+ "fg-main": #ddd,
+ "fg-dim": #bbb,
+ "fg-warning": #fdca6d,
+
+ "edge-grey": #a0a0a0,
+ "placeholder": #c4c4c4,
+
+ "link": #8ac2f9,
+
+ "power-deep": #c62727,
+ "power-fg": "#fff",
+
+ "image-dropdown": url(/static/images/arrow-down-wide-dark.svg)
+);
+
+// This section is for colour meanings
+$theme: map.merge($theme, (
+ "bg-dim": map.get($theme, "bg-0"),
+ "bg-nav": map.get($theme, "bg-5"),
+));
diff --git a/sass/themes/_edgeless-light.scss b/sass/themes/_edgeless-light.scss
new file mode 100644
index 0000000..be4f453
--- /dev/null
+++ b/sass/themes/_edgeless-light.scss
@@ -0,0 +1,8 @@
+// extend regular light theme to change a couple of shades
+@use "light";
+@use "sass:map";
+
+// this section is for colour meanings
+$theme: map.merge(light.$theme, (
+ "edge-grey": #c0c0c0,
+));
diff --git a/sass/themes/_light.scss b/sass/themes/_light.scss
new file mode 100644
index 0000000..a927ce0
--- /dev/null
+++ b/sass/themes/_light.scss
@@ -0,0 +1,38 @@
+// Defined in scss file instead of sass because indented syntax does not have multiline maps
+// https://github.com/sass/sass/issues/216
+
+@use "sass:map";
+
+// this section is for colour shades
+$theme: (
+ // lighter
+ "bg-0": #fff,
+ "bg-1": #fff,
+ // regular
+ "bg-2": #f2f2f2,
+ // darker
+ "bg-3": #e8e8e8, // slightly
+ "bg-4": #dadada, // noticably
+ "bg-5": #d0d0d0, // brightly
+
+ "fg-bright": #000,
+ "fg-main": #202020,
+ "fg-dim": #454545,
+ "fg-warning": #ce8600,
+
+ "edge-grey": #909090,
+ "placeholder": #636363,
+
+ "link": #0b51d4,
+
+ "power-deep": #c62727,
+ "power-fg": #fff,
+
+ "image-dropdown": url(/static/images/arrow-down-wide-light.svg)
+);
+
+// this section is for colour meanings
+$theme: map.merge($theme, (
+ "bg-dim": map.get($theme, "bg-4"),
+ "bg-nav": map.get($theme, "bg-0")
+));
diff --git a/server.js b/server.js
index e9d7805..3dd550b 100644
--- a/server.js
+++ b/server.js
@@ -1,9 +1,11 @@
const {Pinski} = require("pinski")
const {setInstance} = require("pinski/plugins")
const constants = require("./utils/constants")
+const iconLoader = require("./utils/icon-loader").icons
;(async () => {
await require("./utils/upgradedb")()
+ const icons = await iconLoader
const server = new Pinski({
port: 10412,
@@ -13,19 +15,19 @@ const constants = require("./utils/constants")
setInstance(server)
server.pugDefaultLocals.constants = constants
+ server.pugDefaultLocals.icons = icons
server.muteLogsStartingWith("/vi/")
server.muteLogsStartingWith("/favicon")
server.muteLogsStartingWith("/static")
- server.addSassDir("sass", ["sass/includes"])
- server.addRoute("/static/css/main.css", "sass/main.sass", "sass")
+ server.addSassDir("sass", ["sass/includes", "sass/themes", "sass/theme-modules"])
+ server.addRoute("/static/css/dark.css", "sass/dark.sass", "sass")
+ server.addRoute("/static/css/light.css", "sass/light.sass", "sass")
+ server.addRoute("/static/css/edgeless-light.css", "sass/edgeless-light.sass", "sass")
server.addPugDir("pug", ["pug/includes"])
server.addPugDir("pug/errors")
- server.addRoute("/cant-think", "pug/cant-think.pug", "pug")
- server.addRoute("/privacy", "pug/privacy.pug", "pug")
- server.addRoute("/licenses", "pug/licenses.pug", "pug")
server.addStaticHashTableDir("html/static/js")
server.addStaticHashTableDir("html/static/js/elemjs")
diff --git a/utils/constants.js b/utils/constants.js
index e4ed2ae..585ee9f 100644
--- a/utils/constants.js
+++ b/utils/constants.js
@@ -9,6 +9,10 @@ let constants = {
type: "string",
default: "http://localhost:3000"
},
+ theme: {
+ type: "integer",
+ default: 0
+ },
save_history: {
type: "boolean",
default: false
diff --git a/utils/icon-loader.js b/utils/icon-loader.js
new file mode 100644
index 0000000..8e712ad
--- /dev/null
+++ b/utils/icon-loader.js
@@ -0,0 +1,8 @@
+const fs = require("fs").promises
+
+const names = ["subscriptions", "settings"]
+const icons = names.map(name => fs.readFile(`html/static/images/${name}.svg`, "utf8"))
+
+module.exports.icons = Promise.all(icons).then(resolvedIcons => {
+ return new Map(names.map((name, index) => [name, resolvedIcons[index]]))
+})
diff --git a/utils/upgradedb.js b/utils/upgradedb.js
index e5f65e5..27607c5 100644
--- a/utils/upgradedb.js
+++ b/utils/upgradedb.js
@@ -70,6 +70,11 @@ const deltas = [
.run()
db.prepare("CREATE TABLE TakedownChannels (ucid TEXT NOT NULL, org TEXT, url TEXT, PRIMARY KEY (ucid))")
.run()
+ },
+ // 11: Settings +theme
+ function() {
+ db.prepare("ALTER TABLE Settings ADD COLUMN theme INTEGER DEFAULT 0")
+ .run()
}
]
@@ -82,7 +87,7 @@ async function createBackup(entry) {
/**
* @param {number} entry
- * @param {boolean} log
+ * @param {boolean} [log]
*/
function runDelta(entry, log) {
process.stdout.write(`Upgrading database to version ${entry}... `)