diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index ec8dce9c4..b3da2e276 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -24,7 +24,7 @@ module ApplicationHelper
tag.li(id: id, class: klass) do
link_to(url, id: "#{id}-link", **options) do
- concat tag.i(class: icon)
+ concat svg_icon(icon)
concat " "
concat tag.span(text)
end
@@ -189,6 +189,21 @@ module ApplicationHelper
end
end
+ def simple_avatar(user, **options)
+ return "" if user.nil?
+ post_id = user.avatar_id
+ deferred_post_ids.add(post_id) if post_id
+
+ klass = options.delete(:class)
+ named = options.delete(:named)
+ tag.a href: user_path(user), class: "simple-avatar #{klass}", data: { id: post_id, name: user.name } do
+ tag.span(class: "simple-avatar-button") do
+ concat tag.span(user.pretty_name, class: "simple-avatar-name") if named
+ concat tag.span(class: "simple-avatar-image", data: { name: user.name[0].capitalize })
+ end
+ end
+ end
+
def user_banner(user)
return "" if user.nil?
post_id = user.banner_id
diff --git a/app/helpers/icon_helper.rb b/app/helpers/icon_helper.rb
new file mode 100644
index 000000000..f5f919320
--- /dev/null
+++ b/app/helpers/icon_helper.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+module IconHelper
+ # https://lucide.dev/
+ PATHS = {
+ # Navigation
+ hamburger: %(),
+ brush: %(),
+ images: %(),
+ library: %(),
+ group: %(),
+ tags: %(),
+ megaphone: %(),
+ message_square: %(),
+ lectern: %(),
+
+ swatch: %(),
+ settings: %(),
+ log_in: %(),
+
+ # Pagination
+ chevron_left: %(),
+ chevron_right: %(),
+ ellipsis: %(),
+
+ # Posts
+ fullscreen: %(),
+ }.freeze
+
+ def svg_icon(name, *args)
+ options = args.extract_options!
+ width = options[:width] || 24
+ height = options[:height] || 24
+
+ tag.svg(
+ "xmlns": "http://www.w3.org/2000/svg",
+ "width": width,
+ "height": height,
+ "viewbox": "0 0 24 24",
+ "fill": "none",
+ "stroke": "currentColor",
+ "stroke-width": 2,
+ "stroke-linecap": "round",
+ "stroke-linejoin": "round",
+ ) do
+ raw(PATHS[name]) # rubocop:disable Rails/OutputSafety
+ end
+ end
+end
diff --git a/app/helpers/pagination_helper.rb b/app/helpers/pagination_helper.rb
index 1759c2cd6..2e65d042f 100644
--- a/app/helpers/pagination_helper.rb
+++ b/app/helpers/pagination_helper.rb
@@ -2,17 +2,23 @@
module PaginationHelper
def sequential_paginator(records)
- with_paginator_wrapper do
+ tag.div(class: "paginator") do
return "" if records.try(:none?)
html = "".html_safe
- unless records.is_first_page?
- html << tag.li(link_to("< Previous", nav_params_for("a#{records[0].id}"), rel: "prev", id: "paginator-prev", data: { shortcut: "a left" }))
+
+ # Previous
+ html << link_to(records.is_first_page? ? "#" : nav_params_for("a#{records[0].id}"), class: "prev", id: "paginator-prev", rel: "prev", data: { shortcut: "a left", disabled: records.is_first_page? }) do
+ concat svg_icon(:chevron_left)
+ concat tag.span("Prev")
end
- unless records.is_last_page?
- html << tag.li(link_to("Next >", nav_params_for("b#{records[-1].id}"), rel: "next", id: "paginator-next", data: { shortcut: "d right" }))
+ # Next
+ html << link_to(records.is_last_page? ? "#" : nav_params_for("b#{records[-1].id}"), class: "next", id: "paginator-next", rel: "next", data: { shortcut: "d right", disabled: records.is_last_page? }) do
+ concat tag.span("Next")
+ concat svg_icon(:chevron_right)
end
+
html
end
end
@@ -22,64 +28,73 @@ module PaginationHelper
return sequential_paginator(records)
end
- with_paginator_wrapper do
+ tag.div(class: "paginator", data: { total: [records.total_pages, records.max_numbered_pages].min, current: records.current_page }) do
html = "".html_safe
- icon_left = tag.i(class: "fa-solid fa-chevron-left")
- if records.current_page >= 2
- html << tag.li(class: "arrow") { link_to(icon_left, nav_params_for(records.current_page - 1), rel: "prev", id: "paginator-prev", data: { shortcut: "a left" }) }
- else
- html << tag.li(class: "arrow") { tag.span(icon_left) }
+
+ # Previous
+ has_prev = records.current_page < 2
+ html << link_to(has_prev ? "#" : nav_params_for(records.current_page - 1), class: "prev", id: "paginator-prev", rel: "prev", data: { shortcut: "a left", disabled: has_prev }) do
+ concat svg_icon(:chevron_left)
+ concat tag.span("Prev")
end
- paginator_pages(records).each do |page|
- html << numbered_paginator_item(page, records)
+ # Break
+ html << tag.div(class: "break")
+
+ # Numbered
+ paginator_pages(records).each do |page, klass|
+ html << numbered_paginator_item(page, klass, records)
end
- icon_right = tag.i(class: "fa-solid fa-chevron-right")
- if records.current_page < records.total_pages
- html << tag.li(class: "arrow") { link_to(icon_right, nav_params_for(records.current_page + 1), rel: "next", id: "paginator-next", data: { shortcut: "d right" }) }
- else
- html << tag.li(class: "arrow") { tag.span(icon_right) }
+ # Next
+ has_next = records.current_page >= records.total_pages
+ html << link_to(has_next ? "#" : nav_params_for(records.current_page + 1), class: "next", id: "paginator-next", rel: "next", data: { shortcut: "d right", disabled: has_next }) do
+ concat tag.span("Next")
+ concat svg_icon(:chevron_right)
end
+
html
end
end
private
- def with_paginator_wrapper(&)
- tag.div(class: "paginator") do
- tag.menu(&)
- end
- end
-
def paginator_pages(records)
- window = 4
+ small_window = 2
+ large_window = 4
last_page = [records.total_pages, records.max_numbered_pages].min
- left = [2, records.current_page - window].max
- right = [records.current_page + window, last_page - 1].min
+ left_sm = [2, records.current_page - small_window].max
+ left_lg = [2, records.current_page - large_window].max
+ right_sm = [records.current_page + small_window, last_page - 1].min
+ right_lg = [records.current_page + large_window, last_page - 1].min
+ small_range = left_sm..right_sm
- [
- 1,
- ("..." unless left == 2),
- (left..right).to_a,
- ("..." unless right == last_page - 1),
- (last_page unless last_page <= 1),
- ].flatten.compact
+ result = [
+ [1, "first"],
+ ]
+ result.push([0, "spacer"]) unless left_lg == 2
+ (left_lg..right_lg).each do |page|
+ result.push([page, small_range.member?(page) ? "sm" : "lg"])
+ end
+ result.push([0, "spacer"]) unless right_lg == last_page - 1
+ result.push([last_page, "last"]) unless last_page <= 1
+
+ result
end
- def numbered_paginator_item(page, records)
+ def numbered_paginator_item(page, klass, records)
return "" if page.to_i > records.max_numbered_pages
html = "".html_safe
- if page == "..."
- html << tag.li(class: "more") { tag.i(class: "fa-solid fa-ellipsis") }
+ if page == 0
+ html << link_to(svg_icon(:ellipsis), nav_params_for(0), class: "spacer")
elsif page == records.current_page
- html << tag.li(class: "current-page") { tag.span(page) }
+ html << tag.span(page, class: "page current")
else
- html << tag.li(class: "numbered-page") { link_to(page, nav_params_for(page)) }
+ html << link_to(page, nav_params_for(page), class: "page #{klass}")
end
+
html
end
diff --git a/app/javascript/src/javascripts/home.js b/app/javascript/src/javascripts/home.js
new file mode 100644
index 000000000..612c451a0
--- /dev/null
+++ b/app/javascript/src/javascripts/home.js
@@ -0,0 +1,43 @@
+import Page from "./utility/page";
+
+const Home = {};
+
+Home.init = function () {
+
+ const $form = $("#home-search-form");
+ const $tags = $("#tags");
+
+ let isEmpty = !$tags.val();
+ let wasEmpty = isEmpty;
+ if (isEmpty) $form.addClass("empty");
+
+ $tags.on("input", () => {
+ wasEmpty = isEmpty;
+ isEmpty = !$tags.val();
+
+ if (isEmpty && !wasEmpty) $form.addClass("empty");
+ else if (!isEmpty && wasEmpty) $form.removeClass("empty");
+ });
+
+ $(".home-buttons a").on("click", (event) => {
+ if (isEmpty) return; // Act like regular links
+
+ event.preventDefault();
+ const extraTags = $(event.currentTarget).attr("tags");
+ if (extraTags) {
+ $tags.val((index, value) => {
+ return value + " " + extraTags;
+ });
+ }
+
+ $form.trigger("submit");
+ return false;
+ });
+};
+
+$(() => {
+ if (!Page.matches("static", "home")) return;
+ Home.init();
+});
+
+export default Home;
diff --git a/app/javascript/src/javascripts/navigation.js b/app/javascript/src/javascripts/navigation.js
index 3d5fd84ad..beae52ac3 100644
--- a/app/javascript/src/javascripts/navigation.js
+++ b/app/javascript/src/javascripts/navigation.js
@@ -2,7 +2,7 @@ const Navigation = {};
Navigation.init = function () {
const wrapper = $("html");
- $("#nav-toggle, .nav-offset-left, .nav-offset-bottom").on("click", (event) => {
+ $("#nav-toggle, .nav-offset-left, .nav-offset-bott").on("click", (event) => {
event.preventDefault();
wrapper.toggleClass("nav-toggled");
diff --git a/app/javascript/src/javascripts/news_updates.js b/app/javascript/src/javascripts/news_updates.js
index aae44cbd1..aabe7912c 100644
--- a/app/javascript/src/javascripts/news_updates.js
+++ b/app/javascript/src/javascripts/news_updates.js
@@ -10,7 +10,6 @@ NewsUpdate.initialize = function () {
let newsOpen = false;
$("#news-header, #news-show").on("click", (event) => {
event.preventDefault();
- console.log("click");
newsOpen = !newsOpen;
$("#news").toggleClass("open", newsOpen);
diff --git a/app/javascript/src/javascripts/paginator.js b/app/javascript/src/javascripts/paginator.js
new file mode 100644
index 000000000..434750a2c
--- /dev/null
+++ b/app/javascript/src/javascripts/paginator.js
@@ -0,0 +1,19 @@
+const Paginator = {};
+
+Paginator.init_fasttravel = function (button) {
+ button.on("click", (event) => {
+ event.preventDefault();
+
+ const value = prompt("Navigate to page");
+ if (!value) return;
+
+ window.location.replace(button.attr("href").replace("page=0", "page=" + value));
+ });
+};
+
+$(() => {
+ for (const one of $(".paginator a.spacer").get())
+ Paginator.init_fasttravel($(one));
+});
+
+export default Paginator;
diff --git a/app/javascript/src/javascripts/post_mode_menu.js b/app/javascript/src/javascripts/post_mode_menu.js
index a94151fdf..31415a3a8 100644
--- a/app/javascript/src/javascripts/post_mode_menu.js
+++ b/app/javascript/src/javascripts/post_mode_menu.js
@@ -35,12 +35,10 @@ PostModeMenu.change_tag_script = function (e) {
e.preventDefault();
const newScriptID = Number(e.key);
- console.log(newScriptID, LStorage.Posts.TagScript.ID);
if (!newScriptID || newScriptID == LStorage.Posts.TagScript.ID)
return;
LStorage.Posts.TagScript.ID = newScriptID;
- console.log("settings", LStorage.Posts.TagScript.ID, LStorage.Posts.TagScript.Content);
$("#tag-script-field").val(LStorage.Posts.TagScript.Content);
PostModeMenu.show_notice(newScriptID);
};
diff --git a/app/javascript/src/javascripts/post_search.js b/app/javascript/src/javascripts/post_search.js
index 78cb1a5ea..a3e3a20cc 100644
--- a/app/javascript/src/javascripts/post_search.js
+++ b/app/javascript/src/javascripts/post_search.js
@@ -1,4 +1,5 @@
import LStorage from "./utility/storage";
+import Page from "./utility/page";
const PostSearch = {};
@@ -10,6 +11,8 @@ PostSearch.init = function () {
$(".wiki-excerpt").each((index, element) => {
PostSearch.initialize_wiki_preview($(element));
});
+
+ PostSearch.initialize_controls();
};
PostSearch.initialize_input = function ($form) {
@@ -39,21 +42,35 @@ PostSearch.initialize_input = function ($form) {
PostSearch.initialize_wiki_preview = function ($preview) {
let visible = LStorage.Posts.WikiExcerpt;
- if (visible)
- $preview.removeClass("hidden");
+ if (visible) $preview.addClass("open");
+ window.setTimeout(() => { // Disable the rollout on first load
+ $preview.removeClass("loading");
+ }, 250);
- $($preview.find("a.wiki-excerpt-toggle")).on("click", (event) => {
+ $($preview.find("h3.wiki-excerpt-toggle")).on("click", (event) => {
event.preventDefault();
visible = !visible;
- $preview.toggleClass("hidden", !visible);
+ $preview.toggleClass("open", visible);
LStorage.Posts.WikiExcerpt = visible;
return false;
});
};
+PostSearch.initialize_controls = function () {
+ let fullscreen = LStorage.Posts.Fullscreen;
+ $("#search-fullscreen").on("click", () => {
+ fullscreen = !fullscreen;
+ $("body").attr("data-st-fullscreen", fullscreen);
+ LStorage.Posts.Fullscreen = fullscreen;
+ });
+};
+
$(() => {
+ if (!Page.matches("posts", "index") && !Page.matches("favorites"))
+ return;
+
PostSearch.init();
});
diff --git a/app/javascript/src/javascripts/shortcuts.js b/app/javascript/src/javascripts/shortcuts.js
index 4e3aa6e56..869c2f136 100644
--- a/app/javascript/src/javascripts/shortcuts.js
+++ b/app/javascript/src/javascripts/shortcuts.js
@@ -28,6 +28,7 @@ Shortcuts.initialize_data_shortcuts = function () {
Shortcuts.keydown(keys, namespace, event => {
const e = $(`[data-shortcut="${keys}"]`).get(0);
+ if ($e.data("disabled")) return;
if ($e.is("input, textarea")) {
$e.trigger("focus").selectEnd();
} else {
diff --git a/app/javascript/src/javascripts/themes.js b/app/javascript/src/javascripts/themes.js
index 83d7f9da6..8ee283cfc 100644
--- a/app/javascript/src/javascripts/themes.js
+++ b/app/javascript/src/javascripts/themes.js
@@ -3,7 +3,7 @@ import LStorage from "./utility/storage";
const Theme = {};
-Theme.Values = ["Main", "Extra", "StickyHeader", "Palette", "Navbar", "Gestures"];
+Theme.Values = ["Main", "Extra", "StickyHeader", "ForumNotif", "Palette", "Navbar", "Gestures"];
for (const one of Theme.Values) {
Object.defineProperty(Theme, one, {
diff --git a/app/javascript/src/javascripts/thumbnails.js b/app/javascript/src/javascripts/thumbnails.js
index c2877b281..2ab79d685 100644
--- a/app/javascript/src/javascripts/thumbnails.js
+++ b/app/javascript/src/javascripts/thumbnails.js
@@ -8,6 +8,23 @@ Thumbnails.initialize = function () {
const posts = $(".post-thumb.placeholder, .thumb-placeholder-link");
const replacedPosts = [];
+ // Avatar special case
+ for (const post of $(".simple-avatar")) {
+ const $post = $(post);
+
+ const postID = $post.data("id");
+ if (!postID) continue;
+
+ const postData = postsData[postID];
+ if (!postData || !postData["preview_url"]) continue;
+
+ $("
")
+ .attr("src", postData["preview_url"])
+ .appendTo($post.find("span.simple-avatar-image"));
+ continue;
+ }
+
+ // Reset of the deferred posts
for (const post of posts) {
const $post = $(post);
diff --git a/app/javascript/src/javascripts/utility/storage.js b/app/javascript/src/javascripts/utility/storage.js
index 50e2e92d3..84c476ef0 100644
--- a/app/javascript/src/javascripts/utility/storage.js
+++ b/app/javascript/src/javascripts/utility/storage.js
@@ -65,6 +65,9 @@ LStorage.Theme = {
/** @returns {boolean} True if the sticky header is enabled */
StickyHeader: ["theme-sheader", false],
+
+ /** @returns {boolean} True if the forum notification dot is enabled */
+ ForumNotif: ["theme-forumnotif", false],
};
StorageUtils.bootstrapMany(LStorage.Theme);
@@ -85,6 +88,9 @@ LStorage.Posts = {
/** @returns {boolean} True if the wiki excerpt should be visible */
WikiExcerpt: ["e6.posts.wiki", true],
+
+ /** @returns {boolean} True if the search should be displayed in fullscreen */
+ Fullscreen: ["e6.posts.fusk", false],
};
StorageUtils.bootstrapMany(LStorage.Posts);
diff --git a/app/javascript/src/styles/base.scss b/app/javascript/src/styles/base.scss
index 05920d6db..6ec5cfc2f 100644
--- a/app/javascript/src/styles/base.scss
+++ b/app/javascript/src/styles/base.scss
@@ -9,6 +9,9 @@
@import "base/links";
@import "base/fontawesome";
+@import "common/standard_variables";
+@import "common/standard_elements";
+
@import "common/footer";
@import "common/helper_classes";
@import "common/helper_palette";
diff --git a/app/javascript/src/styles/common/_footer.scss b/app/javascript/src/styles/common/_footer.scss
index 79047aa3a..31c2c6935 100644
--- a/app/javascript/src/styles/common/_footer.scss
+++ b/app/javascript/src/styles/common/_footer.scss
@@ -1,73 +1,84 @@
-footer#page-footer {
- display: grid;
- grid-template-columns: 1fr 2fr;
- gap: 0.5rem 0;
-
- padding: 0.5rem 0 1rem;
- margin: 1rem 0 0;
-
+footer.footer-wrapper {
background: var(--color-foreground);
+ margin-top: 1rem;
+
+ .footer-grid {
+ display: grid;
+ grid-template-columns: min-content min-content;
+ grid-template-areas:
+ "logo . "
+ "left right";
+ justify-items: center;
+ gap: 0.5rem 0;
+
+ width: min-content;
+ margin: 0 auto;
+ padding: 1rem;
+ }
+
+ .footer-logo {
+ grid-area: logo;
+ width: 100%;
+ text-align: right;
+
+ img {
+ width: 5rem;
+ height: auto;
+
+ // Aligning the logo with the line below
+ margin-right: -3.25rem;
+
+ background: themed("color-background") themed("image-background");
+ border-radius: 50%;
+ padding: 0.5rem;
+ }
+ }
.footer-left, .footer-right {
display: flex;
flex-flow: column;
- gap: 0.25em;
- padding: 0 0.5rem;
-
- font-size: 1.25em;
-
- a, span {
- line-height: 1.25em;
- }
+ box-sizing: border-box;
+ width: 100%;
+ gap: 0.25rem;
+
+ font-size: 1rem;
+
+ a, span { white-space: nowrap; }
+ span { line-height: 1.25em; }
}
+
.footer-left {
- align-items: end;
- border-right: 1px solid var(--color-section);
+ grid-area: left;
+ padding-right: 1rem;
+
+ text-align: right;
+ border-right: 1px solid var(--color-section-lighten-5);
}
-
- .footer-logo {
- grid-column: 1 / -1;
- grid-row: 1;
- text-align: center;
-
- img {
- width: 5rem;
- height: 5rem;
- }
+ .footer-right {
+ grid-area: right;
+ padding-left: 1rem;
}
}
-// Desktop
-footer#page-footer {
- @include window-larger-than(800px) {
- grid-template-columns: 1fr min-content 1fr;
-
- .footer-left, .footer-right {
- font-size: unset;
- flex-flow: row;
- align-items: center;
- border: 0;
- gap: 0;
-
- a:not(:last-child)::after {
- content: "";
- width: 4px;
- height: 4px;
- background: white;
- display: inline-block;
- border-radius: 2px;
- margin: 0.125rem 0.5rem;
- }
-
- span.footer-running { display: none; }
+// Desktop-ish
+footer.footer-wrapper {
+ @include window-larger-than(450px) {
+ .footer-grid {
+ grid-template-areas: "logo left right";
+ padding: 1rem 0;
}
- .footer-left { justify-content: right; }
-
.footer-logo {
- grid-column: unset;
- grid-row: unset;
+ display: flex;
+ align-items: center;
+ padding-right: 2rem;
+ box-sizing: border-box;
+
+ img {
+ width: 6rem;
+ margin-right: unset;
+ }
}
}
-}
\ No newline at end of file
+}
diff --git a/app/javascript/src/styles/common/_standard_elements.scss b/app/javascript/src/styles/common/_standard_elements.scss
new file mode 100644
index 000000000..843738416
--- /dev/null
+++ b/app/javascript/src/styles/common/_standard_elements.scss
@@ -0,0 +1,59 @@
+// Standard button
+// Could be applied to either a button or a link
+// Semi-expected to have an icon
+.st-button {
+ $button-font-size: st-value(100);
+ $button-background: themed("color-section-lighten-5");
+ $button-background-hover: themed("color-section-lighten-10");
+ $button-text-color: themed("color-text");
+
+ display: flex;
+ gap: st-value(100) / 4;
+ border-radius: radius(025);
+
+ // Button final size
+ // Font 1rem
+ // Padding 2 * 0.5rem
+ font-size: st-value(100);
+ line-height: st-value(100);
+ padding: st-value(100) / 2;
+ height: st-value(100) * 2;
+
+ // TODO What if button is on a light background
+ background: $button-background;
+ color: $button-text-color;
+ &:hover { background: $button-background-hover; }
+
+ & > svg {
+ // Icon should be slightly larger than text,
+ // with padding to fill the entire button height
+ height: st-value(100) * 1.5; // 1.5rem
+ width: st-value(100) * 1.5; // 1.5rem
+ margin: -#{st-value(100) / 2} 0; // 0.5rem
+ padding: st-value(100) / 4; // 0.25rem
+
+ border-radius: radius(025);
+ }
+ & > span {
+ text-align: left;
+
+ // Do not overflow text
+ overflow: hidden;
+ flex: 1;
+ }
+
+ // Full width button
+ &.w100 { width: 100%; }
+
+ &.stealth {
+ background: none;
+ padding: (st-value(100) / 2) 0;
+
+ svg { background: $button-background; }
+ span { color: themed("color-link"); }
+ &:hover {
+ svg { background: $button-background-hover; }
+ span { color: themed("color-link-hover"); }
+ }
+ }
+}
diff --git a/app/javascript/src/styles/common/_standard_variables.scss b/app/javascript/src/styles/common/_standard_variables.scss
new file mode 100644
index 000000000..8504ec0b5
--- /dev/null
+++ b/app/javascript/src/styles/common/_standard_variables.scss
@@ -0,0 +1,21 @@
+@use "sass:map";
+
+// Standard variables for typography and UI elements
+
+$st-values: (
+ 000: 0rem,
+ 025: 0.25rem,
+ 050: 0.50rem,
+ 075: 0.75rem,
+ 100: 1rem,
+);
+
+@function st-value($name) {
+ @return map-get($map: $st-values, $key: $name);
+}
+
+@function padding($value) { @return st-value(($value)); }
+@mixin st-padding($value) { padding: padding(($value)); }
+
+@function radius($value) { @return st-value($value); }
+@mixin st-radius($value) { border-radius: radius($value); }
diff --git a/app/javascript/src/styles/common/navigation.scss b/app/javascript/src/styles/common/navigation.scss
index 3a732f95d..6e383dcd1 100644
--- a/app/javascript/src/styles/common/navigation.scss
+++ b/app/javascript/src/styles/common/navigation.scss
@@ -6,6 +6,7 @@ nav.navigation {
width: 100%; // otherwise narrow when fixed
z-index: 20; // otherwise post labels layered above
+ position: relative;
/* Top bar, always visible */
@@ -13,16 +14,18 @@ nav.navigation {
grid-area: logo;
background-color: themed("color-background");
+ height: 3.75rem;
a.nav-logo-link {
display: flex;
+ box-sizing: border-box;
// Height: 3.75rem
- // - padding 0.25 * 2 = 0.5
- // - image 3.25
- height: 3.25rem;
- width: 3.25rem;
- margin: 0.25rem;
+ // - padding 0.125 * 2 = 0.25
+ // - image 3.5
+ height: 3.5rem;
+ width: 4rem;
+ margin: 0.125rem;
background-image: url("main-logo.svg");
background-repeat: no-repeat;
@@ -37,160 +40,208 @@ nav.navigation {
display: flex;
flex-flow: row-reverse;
align-items: center;
- gap: 0.5em;
font-size: 1.15rem;
- padding-right: 0.5em;
background-color: themed("color-background");
+ padding-right: 0.25rem;
+ height: 3.75rem;
// Height: 3.75rem
- // - wrapper padding 0.875 * 2 = 1.75
- // - link padding 0.25 * 2 = 0.5
- // - font size 1.5
- padding: 0.875rem;
-
+ // - link padding 0.625 * 2 = 1.25
+ // - internal size 2.5
& > a {
display: flex;
- gap: 0.25em;
+ padding: 0.625rem 0.5rem;
- padding: 0.25rem 0.5rem;
- background: themed("color-foreground");
- border-radius: 6px;
+ & > span {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ box-sizing: border-box;
+ gap: 0.5rem;
+
+ height: 2.5rem;
+ min-width: 2.5rem;
+ line-height: 1.5rem;
+ padding: 0 0.5rem;
- & > i {
- font-size: 1.5rem;
+ background: themed("color-foreground");
color: themed("color-link-active");
+ border-radius: 0.25rem;
+ white-space: nowrap;
+ }
+
+ &:hover > span, &:active > span { background: themed("color-section"); }
+ &:focus { outline: none; }
+ }
+
+ a.simple-avatar {
+ .simple-avatar-button {
+ padding: 0;
+ gap: 0;
+
+ .simple-avatar-name {
+ padding: 0.5rem;
+
+ @include window-smaller-than(32rem) {
+ display: none;
+ }
+ }
+
+ .simple-avatar-image {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ position: relative;
+
+ width: 2.5rem;
+ height: 2.5rem;
+
+ box-sizing: border-box;
+ border-radius: 0.25rem;
+ background: themed("color-section");
+
+ img {
+ width: 2.5rem;
+ height: 2.5rem;
+
+ border-radius: 0.25rem;
+ object-fit: cover;
+
+ z-index: 1; // above the letter
+ }
+
+ &::before {
+ content: attr(data-name);
+ position: absolute;
+ font-size: 1.5rem;
+ }
+ }
+
+ @include window-smaller-than(32rem) {
+ &.sign-in .simple-avatar-image {
+ background: themed("color-foreground");
+ }
+ }
}
}
}
- /* Prevent toggled menus from being too wide */
- .nav-offset-left {
- grid-area: offleft;
- display: none; // flex
- background: #00000050;
- }
-
- .nav-offset-bottom {
- grid-area: offbott;
- display: none; // flex
- background: #00000050;
+ /* Offset elements on the left and bottom */
+ // Needed to track clicks outside the menu area
+ @each $name in ("left", "bott") {
+ .nav-offset-#{$name} {
+ grid-area: off#{$name};
+ display: none; // flex
+ background: #00000050;
+ }
}
/* Toggled menus, hidden by default */
- .nav-primary {
- grid-area: primary;
- display: none; // flex
- flex-flow: column;
+ // Naming areas
+ @each $name in (primary, secondary, tools, help) {
+ .nav-#{$name} {
+ grid-area: $name;
+ display: none;
- background-color: themed("color-section");
- font-size: 1.5em;
+ li {
+ padding: 0;
- li {
- padding: 0;
- a {
- display: block;
- border-bottom: 1px solid themed("color-foreground");
- padding: 0.5em;
+ & > a {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
- // "Comments" is usually the longest and might wrap
- white-space: nowrap;
+ white-space: nowrap;
- i {
- width: 1.5rem;
- color: themed("color-link-active");
- text-align: center;
- }
- }
-
- &.current a { background-color: themed("color-foreground"); }
- &.forum-updated {
- position: relative;
-
- &::after {
- content: "";
- width: 6px;
- height: 6px;
- border-radius: 3px;
-
- background: var(--palette-text-red);
-
- position: absolute;
- right: 0.2em;
- top: 1em;
+ svg {
+ margin: -0.25rem 0;
+ color: themed("color-link-active");
+ }
}
}
}
}
- .nav-secondary {
- grid-area: secondary;
- display: none; // flex
+ // Common top
+ .nav-primary, .nav-secondary {
flex-flow: column;
- background-color: themed("color-foreground");
- font-size: 1.35em;
- height: 440px;
-
// Prevent the tools / help buttons from being pushed
// way too low on pages with a lot of secondary links
- overflow: scroll;
+ height: 422px;
+ overflow-y: scroll;
+
+ li a {
+ justify-content: start;
+ }
+ }
+
+ .nav-primary {
+ background-color: themed("color-section");
+ font-size: 1.25rem;
+
+ li > a {
+ border-bottom: 1px solid themed("color-foreground");
+ line-height: 1.25rem;
+ padding: 1rem 0.5rem;
+ }
+ li.current a { background-color: themed("color-foreground"); }
+ }
+
+ .nav-secondary {
+ background-color: themed("color-foreground");
+ font-size: 1.2rem;
li {
- padding: 0;
- a {
- display: block;
+ & > a {
border-bottom: 1px solid themed("color-section");
- padding: 0.5em;
+ line-height: 1.2rem;
+ padding: 0.7rem;
+
+ white-space: wrap; // forum menus are long
}
&.divider {
border-bottom: 1px solid themed("color-section");
- height: 0.25em;
+ height: 0.25rem;
}
form input[type="text"] {
width: 100%;
box-sizing: border-box;
- // Reduced font size to make the search
- // box less claustrophobic
- font-size: 1em;
- padding: 0.25em 0.5em;
+ font-size: 1.25rem;
+ padding: 0.5rem 0.5rem;
}
}
}
- .nav-tools {
- grid-area: tools;
-
- display: none; // grid
- grid-template-columns: 1fr 1fr;
+ // Common bottom
+ .nav-tools, .nav-help {
grid-template-rows: min-content;
-
- padding: 1rem;
+ font-size: 1.1rem;
gap: 1rem;
background-color: themed("color-section");
+
+ li > a {
+ justify-content: center;
+ border-radius: 0.25rem;
+
+ padding: 0.7rem 1rem;
+ line-height: 1.1rem;
+ }
+ }
+
+ .nav-tools {
+ grid-template-columns: 1fr 1fr;
border-top: 1px solid themed("color-foreground");
+ padding: 1rem 1rem 0.5rem;
li {
- padding: 0;
-
+ a { background: themed("color-section-lighten-5"); }
&.nav-tools-login { grid-column: 1 / -1; }
-
- & > a {
- display: block;
-
- background: themed("color-section-lighten-5");
- border-radius: 6px;
-
- font-size: 125%;
- padding: 0.5rem 1rem;
- text-align: center;
-
- i { color: themed("color-link-active"); }
- }
}
&.anonymous li.nav-tools-themes {
@@ -199,42 +250,19 @@ nav.navigation {
}
.nav-help {
- grid-area: help;
-
- display: none; // grid
grid-template-columns: 1fr 1fr 1fr;
- grid-template-rows: min-content;
-
- padding: 1rem;
- gap: 1rem;
-
- background: themed("color-section");
+ padding: 0.5rem 1rem 1rem;
li {
- padding: 0;
-
+ a { background: themed("color-section-darken-5"); }
&.nav-help-discord,
&.nav-help-subscribestar {
grid-column: 1 / -1;
}
- & > a {
- display: flex;
-
- background: themed("color-section-darken-5");
- border-radius: 6px;
-
- font-size: 125%;
- padding: 0.5rem 1rem;
-
- justify-content: center;
- align-items: center;
- gap: 0.25em;
-
- img {
- height: 1.25em;
- margin: -0.5em 0;
- }
+ & > a img {
+ height: 1.5rem;
+ margin: -0.2rem 0;
}
// Hack to put the wiki/help links before discord/sstar on mobile
@@ -255,6 +283,29 @@ body[data-th-sheader="true"] nav.navigation {
}
+// Forum notification
+body[data-th-forumnotif="true"] nav.navigation .nav-primary li.forum-updated {
+ position: relative;
+
+ &::after {
+ content: "";
+ width: 6px;
+ height: 6px;
+ border-radius: 3px;
+
+ background: palette("text-red");
+
+ position: absolute;
+ right: 0.2rem;
+ top: 1.25rem;
+
+ @include window-larger-than(800px) {
+ top: 0.2rem;
+ }
+ }
+}
+
+
// Mobile toggle
html.nav-toggled {
@@ -279,14 +330,14 @@ html.nav-toggled {
// Allow scrolling when the menu is too long
overflow-y: scroll;
- .nav-primary, .nav-secondary, .nav-offset-left, .nav-offset-bottom {
+ .nav-primary, .nav-secondary, .nav-offset-left, .nav-offset-bott {
display: flex;
}
.nav-tools, .nav-help {
display: grid;
}
.nav-primary, .nav-tools, .nav-help {
- box-shadow: -1px 0 5px -1px var(--color-background);
+ box-shadow: -3px 3px 5px -1px themed("color-background");
}
}
}
@@ -294,74 +345,68 @@ html.nav-toggled {
// Desktop
nav.navigation, html.nav-toggled nav.navigation {
- @include window-larger-than(800px) {
+ @include window-larger-than(50rem) {
grid-template-areas:
- "logo primary help tools "
- "logo secondary secondary secondary"
- ;
- grid-template-columns: min-content min-content minmax(0, 1fr) min-content;
- grid-template-rows: 1.75em 2em;
+ "logo primary help help controls"
+ "logo secondary secondary tools controls";
+ grid-template-columns: min-content min-content minmax(0, 1fr) min-content 3.25rem;
+ grid-template-rows: 1.5rem 1.75rem;
- padding: 0 1em 0.5em;
+ padding: 0 1rem 0.5rem;
box-sizing: border-box;
height: unset;
- background: var(--color-background);
+ background: themed("color-background");
overflow-y: hidden; // overrides mobile hack allowing the menu scrolling
- .nav-logo {
- a.nav-logo-link { margin: 0.25rem 0.5rem 0 0; }
+ .nav-logo a.nav-logo-link {
+ height: 3.25rem;
+ width: 3.25rem;
+ margin: 0.25rem 0.5rem 0 0;
}
- .nav-offset-left, .nav-offset-bottom, .nav-controls { display: none; }
+ .mobile { display: none; }
- .nav-primary {
+ // All link ribbons
+ .desktop {
display: flex;
flex-flow: row;
- background: unset;
- font-size: 1.05em;
- padding: 0 0.25em;
+ font-size: 0.875rem;
+ box-shadow: unset;
- li {
+ li a {
display: flex;
+ align-items: center;
+ height: 100%;
- a {
- align-content: center;
+ padding: 0 0.625rem;
+ white-space: nowrap;
+ }
+ }
- border-bottom: 0;
- padding: 0 0.75em;
- i { display: none; }
- }
+ .nav-primary {
+ background: unset;
+ height: unset;
+ padding-left: 0.25rem;
- &.forum-updated::after {
- top: 0.2em;
- }
+ li a {
+ border-bottom: 0;
+ svg { display: none; }
}
}
.nav-secondary {
- display: flex;
- flex-flow: row;
height: unset;
- padding: 0 0.25em;
- font-size: 1.05em;
- border-radius: 6px;
+ padding: 0 0.25rem;
+ border-radius: 0.25rem 0 0 0.25rem;
- // Silly fix for too many links
- overflow: hidden;
+ overflow: hidden; // Silly fix for too many links
+ z-index: 1; // above the avatar
li {
- display: flex;
-
- a {
- align-content: center;
-
- border-bottom: 0;
- padding: 0 0.75em;
- white-space: nowrap;
- }
+ a { border-bottom: 0; }
&.divider {
display: flex;
@@ -371,50 +416,46 @@ nav.navigation, html.nav-toggled nav.navigation {
&::after { content: "|"; }
}
- form {
- display: flex;
- align-items: center;
-
- input[type="text"] { min-width: 10em; }
+ form input[type="text"] {
+ width: 12rem;
+ padding: 0.25rem 0.5rem;
+ font-size: 1rem;
+ border-radius: 0.25rem;
}
}
}
.nav-tools, .nav-help {
- display: flex;
-
padding: 0;
background: unset;
border: none;
gap: 0;
- li {
- display: flex;
+ li a {
+ gap: 0.25rem;
- a {
- align-content: center;
-
- background: unset;
- font-size: 1.05em;
- padding: 0 0.75em;
- text-align: unset;
- white-space: nowrap;
- border-radius: 0;
- }
+ background: unset;
+ text-align: unset;
+ border-radius: 0;
}
}
.nav-tools {
-
- // Otherwise help gets layered above it
- // When the viewport is narrow (but not mobile)
+ background: themed("color-foreground");
z-index: 1;
- background: var(--color-background);
+ border-radius: 0 0.25rem 0.25rem 0;
+ margin-right: 0.25rem;
li {
a {
- i { color: themed("color-link"); }
- &:hover i { color: themed("color-link-hover"); }
+ padding: 0 0.5rem;
+
+ svg {
+ color: themed("color-link");
+ height: 1.25rem;
+ width: 1.25rem;
+ }
+ &:hover svg { color: themed("color-link-hover"); }
}
&.nav-tools-themes, &.nav-tools-settings {
@@ -422,26 +463,123 @@ nav.navigation, html.nav-toggled nav.navigation {
}
}
}
- .nav-help {
- li a img { display: none; }
+ .nav-help {
+
+ // At small resolutions, overflow can
+ // cause scrollbars to appear
+ overflow: hidden;
+
+ li a img { display: none; }
li.current a {
background-color: themed("color-foreground");
}
}
- .nav-primary, .nav-tools, .nav-help { box-shadow: unset; }
+ .nav-controls {
+ position: absolute;
+ right: 0;
+ top: 0;
+
+ height: 3.25rem;
+ box-sizing: border-box;
+ padding: 0.25rem 0 0;
+
+ #nav-toggle { display: none; }
+
+ a.simple-avatar {
+ padding: 0;
+ height: 100%;
+
+ .simple-avatar-button {
+ background: none;
+ color: inherit;
+ align-items: start;
+ font-size: 0.875rem;
+ line-height: 0.875rem;
+
+ .simple-avatar-name {
+ padding: 0 0.5rem;
+ }
+
+ .simple-avatar-image {
+ height: 3rem;
+ width: 3rem;
+ background: themed("color-foreground");
+
+ img {
+ width: 2.75rem;
+ height: 2.75rem;
+ }
+ }
+ }
+ }
+ }
+
+ // Icon collapse
+ // Stage 1: discord and subscribestar buttons
+ .collapse-1 { display: none; }
+ @include window-larger-than(77rem) {
+ .collapse-1 { display: flex; }
+ }
+
+ // Stage 2: account label
+ .collapse-2 .simple-avatar-name { display: none; }
+ @include window-larger-than(65rem) {
+ .collapse-2 .simple-avatar-name { display: unset; }
+ }
}
}
// Tweak for the secondary menu on desktop
body.c-static.a-home {
- @include window-larger-than(800px) {
- nav.navigation, menu.nav-logo, menu.nav-secondary {
+ @include window-larger-than(50rem) {
+
+ nav.navigation {
+
+ // Center and align the navbar
+ grid-template-areas: "logo primary help controls";
+ grid-template-columns: repeat(4, min-content);
+ justify-content: center;
+
+ // Remove padding to prevent a scrollbar
+ // at low desktop resolutions
+ padding: 0 0 0.5rem;
+
+ #nav-subscribestar, #nav-discord, .nav-secondary, .nav-tools { display: none; }
+ }
+
+ // Match the background colors
+ nav.navigation, menu.nav-logo, menu.nav-secondary, menu.nav-controls {
background: unset;
}
menu.nav-tools {
background: var(--bg-color);
}
+ menu.nav-controls {
+ position: static;
+ height: unset;
+ padding: 0;
+ .simple-avatar-button {
+ height: unset;
+ align-items: center;
+ .simple-avatar-image { display: none; }
+ }
+ }
+ }
+
+ @include window-smaller-than(50rem) {
+ // Only show the primary navbar on mobile
+ // since the secondary is empty anyways
+ nav.navigation {
+ grid-template-areas:
+ "logo logo controls"
+ "offleft primary primary "
+ "offleft tools tools "
+ "offleft help help "
+ "offleft offbott offbott ";
+
+ .nav-secondary { display: none; }
+ }
}
}
diff --git a/app/javascript/src/styles/common/news.scss b/app/javascript/src/styles/common/news.scss
index d57876385..180900cd9 100644
--- a/app/javascript/src/styles/common/news.scss
+++ b/app/javascript/src/styles/common/news.scss
@@ -17,6 +17,8 @@ div#page div#news {
&.open {
max-height: none;
+
+ #news-body { pointer-events: unset; }
}
#news-header {
@@ -26,26 +28,27 @@ div#page div#news {
cursor: pointer;
width: 100%;
text-align: center;
- padding: 0.5rem 0.5rem 0;
+ padding: 0.5rem;
}
#news-body {
display: block;
box-sizing: border-box;
- margin-top: 5px;
max-width: 800px;
padding: 0 0.5rem 0.5rem;
+
+ pointer-events: none;
}
#news-dismiss {
position: absolute;
- right: 0.5rem;
- top: 0.25rem;
+ right: 0;
+ top: 0;
+ padding: 0.25rem 1rem;
cursor: pointer;
- font-size: 1.15rem;
+ font-size: 1.25rem;
font-weight: bold;
- color: white;
}
}
diff --git a/app/javascript/src/styles/common/paginator.scss b/app/javascript/src/styles/common/paginator.scss
index c006084b6..6ffd9a446 100644
--- a/app/javascript/src/styles/common/paginator.scss
+++ b/app/javascript/src/styles/common/paginator.scss
@@ -1,35 +1,101 @@
+.paginator {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: space-evenly;
+ background-color: themed("color-foreground");
+ border-radius: 0.25rem;
-div.paginator {
- display: block;
- padding: 2em 0 1em 0;
- text-align: center;
- clear: both;
+ width: max-content;
+ margin: 0 auto;
- menu {
+ & > a, & > span {
display: flex;
+ box-sizing: border-box;
justify-content: center;
+ align-items: center;
+ min-width: 2.15rem;
+
+ font-size: 1rem;
+ line-height: 1rem;
+ padding: 0.75rem 0.3rem; // otherwise large page numbers wrap
+
+ border-radius: 0.25rem;
+
+ &:hover {
+ background: themed("color-section");
+ }
}
- li {
- a {
- margin: 0 0.25em;
- padding: 0.25em 0.75em;
+ & > a[data-disabled="true"] {
+ color: var(--color-text);
+ pointer-events: none;
+ }
+
+ // Ordering
+ // Oh boy
+ .page {
+ order: 20;
+ &.lg { display: none; }
+ &.current { cursor: default; }
+ }
+ .prev {
+ order: 1;
+ margin-right: auto;
+ }
+ .spacer {
+ order: 20;
+ padding: 0;
+
+ &:last-child { display: none; }
+ svg {
+ height: 1rem;
+ transform: rotate(90deg);
+ }
+ }
+ .next {
+ order: 9;
+ margin-left: auto;
+ }
+ .break {
+ order: 10;
+ width: 100%;
+ }
+
+
+ // Tablet
+ @include window-larger-than(35rem) {
+ justify-content: center;
+ gap: 0.125rem;
+
+ a, span {
+ order: 0 !important;
+ min-width: 2.25rem;
+ font-size: 0.9rem;
}
- a:hover {
- background: $paginator-hover-background;
- color: $paginator-hover-color;
+ .break { display: none; }
+ .spacer {
+ padding: inherit;
+ svg { transform: unset; }
}
- &.more {
- color: $paginator-more-color;
+ .prev { margin-right: 1rem; }
+ .next { margin-left: 1rem; }
+ }
+
+ @include window-larger-than(50rem) {
+ a, span {
+ padding: 0.75rem 0.5rem;
+ font-size: 0.95rem;
}
- span {
- margin: 0 0.25em;
- padding: 0.25em 0.75em;
- font-weight: bold;
+ .prev, .next {
+ span { display: none; }
}
}
+
+ @include window-larger-than(65rem) {
+ a.page.lg { display: flex; }
+ }
}
diff --git a/app/javascript/src/styles/common/z_responsive.scss b/app/javascript/src/styles/common/z_responsive.scss
index df89c65fe..316b3be09 100644
--- a/app/javascript/src/styles/common/z_responsive.scss
+++ b/app/javascript/src/styles/common/z_responsive.scss
@@ -59,14 +59,6 @@
}
}
- .guest-warning {
- .guest-warning-dialog {
- top: 5vh;
- height: 90vh;
- width: 80vw;
- }
- }
-
div#page {
> div /* div#c-$controller */
diff --git a/app/javascript/src/styles/specific/guest_warning.scss b/app/javascript/src/styles/specific/guest_warning.scss
index 36701b901..be988bd31 100644
--- a/app/javascript/src/styles/specific/guest_warning.scss
+++ b/app/javascript/src/styles/specific/guest_warning.scss
@@ -1,33 +1,55 @@
.guest-warning {
position: fixed;
- left: 0;
top: 0;
- height: 100vh;
- width: 100vw;
- z-index: 500;
- background-color: themed("color-background");
+ left: 0;
+ right: 0;
+ bottom: 0;
+ z-index: 1000;
+
+ display: flex;
+ justify-content: center;
+
+ background: themed("color-background");
.guest-warning-dialog {
- z-index: 500;
- position: relative;
- top: 20vh;
- margin-left: auto;
- margin-right: auto;
- height: 40vh;
- width: 40vw;
- overflow-x: scroll;
- background-color: themed("color-section");
+ margin: 20vh 0.5em 0;
+
+ height: min-content;
+ max-width: 360px;
.dialog-header {
- padding: $padding-050 $base-padding;
- background-color: themed("color-section-lighten-5");
- border-bottom: 1px solid themed("color-background");
+ padding: 0 0.5em;
+ margin-bottom: 0.5em;
}
+
.dialog-content {
- padding: $padding-050 $base-padding;
+ background: var(--color-foreground);
+ padding: 2em 1em 1em;
+ border-radius: 3px 3px 0 0;
+
+ p:last-child { margin-bottom: 0; }
}
+
.dialog-footer {
- padding: $padding-050 $base-padding;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5em;
+
+ background: var(--color-foreground);
+ padding: 1em 1em 2em;
+ border-radius: 0 0 3px 3px;
+
+ button {
+ background: var(--color-section-lighten-5);
+ color: var(--color-link);
+ padding: 0.5em 1em;
+ border-radius: 3px;
+
+ &:hover {
+ background: var(--color-section-lighten-10);
+ color: var(--color-link-hover);
+ }
+ }
}
}
}
diff --git a/app/javascript/src/styles/specific/home.scss b/app/javascript/src/styles/specific/home.scss
index 0bd86c9f1..6bcb8752d 100644
--- a/app/javascript/src/styles/specific/home.scss
+++ b/app/javascript/src/styles/specific/home.scss
@@ -3,6 +3,12 @@ body.c-static.a-home {
background-color: var(--bg-color);
background-image: var(--bg-image);
+ background-position-y: 3.75rem;
+
+ @include window-larger-than(800px) {
+ background-position-y: unset;
+ }
+
#page {
background: none;
margin: 8rem auto 0;
@@ -20,7 +26,7 @@ body.c-static.a-home {
padding: 1rem;
margin: 0.5rem;
- border-radius: 0.5rem;
+ border-radius: 0.25rem;
text-align: center;
}
@@ -73,6 +79,10 @@ body.c-static.a-home {
}
}
+ #home-search-form:not(.empty) .home-buttons a span::before {
+ content: "Search ";
+ }
+
// Footer
.home-footer-top {
@@ -86,7 +96,7 @@ body.c-static.a-home {
justify-content: space-between;
align-items: center;
- border-radius: 0.5rem 0.5rem 0 0;
+ border-radius: 0.25rem 0.25rem 0 0;
@include window-larger-than(480px) {
margin: 0.5rem 0.5rem 0;
@@ -109,7 +119,7 @@ body.c-static.a-home {
margin: 0;
padding: 0.5rem;
- border-radius: 0 0 0.5rem 0.5rem;
+ border-radius: 0 0 0.25rem 0.25rem;
@include window-larger-than(480px) {
margin: 0 0.5rem;
diff --git a/app/javascript/src/styles/specific/post_index.scss b/app/javascript/src/styles/specific/post_index.scss
index 497115070..67b6bfcfa 100644
--- a/app/javascript/src/styles/specific/post_index.scss
+++ b/app/javascript/src/styles/specific/post_index.scss
@@ -1,3 +1,22 @@
+body.c-posts.a-index, body.c-favorites.a-index {
+ #page {
+ // Override the theme to instead
+ // project it upon the content area
+ background: none;
+ padding: 0;
+ }
+
+ // Exhibit A
+ // Makes the content area take up the
+ // full height of the page. Yes, really.
+ #page, #c-posts, #c-favorites, #a-index {
+ // I hate both this and myself
+ display: flex;
+ flex-flow: column;
+ flex: 1;
+ }
+}
+
.post-index {
display: grid;
@@ -7,24 +26,46 @@
"sidebar";
grid-template-columns: 1fr;
grid-template-rows: min-content 1fr min-content;
- gap: 1em;
+
+ flex: 1; // See Exhibit A
// 1. Searchbox
.search {
grid-area: search;
+ padding: 0.5rem 0.25rem;
+ background-color: #152f56;
+ background-color: themed("color-foreground");
+ box-shadow: inset 0px -0.25rem 0.25rem -0.25rem themed("color-background");
+
h1 {
font-size: $h3-size;
}
+
+ .search-controls {
+ display: none;
+ flex-flow: column;
+ }
}
// 2. Content
.content {
+ display: flex; // See Exhibit A
+ flex-flow: column;
+
grid-area: content;
+ // Imported from #page
+ padding: 0.5rem 0.25rem themed("content-padding-bottom");
+ background-color: #152f56;
+ background-color: themed("color-foreground");
+ background-image: themed("image-foreground");
+ background-position: themed("image-foreground-position");
+ background-repeat: themed("image-foreground-repeat");
+
// Quick tag edit
#edit-dialog textarea {
- margin-bottom: 0.25em;
+ margin-bottom: 0.25rem;
}
// Actual content area:
@@ -32,86 +73,13 @@
.post-index-gallery {
display: flex;
flex-flow: column;
- gap: 1em;
+ gap: 1rem;
- .wiki-excerpt {
- display: flex;
- flex-flow: column;
- position: relative;
- padding: 1em 1em 0;
- gap: 0.5em;
+ flex: 1; // See Exhibit A
- background: var(--color-section);
- max-width: 60em;
-
- .wiki-excerpt-toggle {
- position: absolute;
- top: 0;
- right: 0;
- padding: 1em;
- outline: none;
-
- transition: transform 0.25s;
-
- &::after {
- @include font-awesome-icon;
- content: unicode("f0d8");
- }
- }
-
- .styled-dtext {
- background: linear-gradient(to top, var(--color-section), var(--color-text));
- -webkit-background-clip: text;
- background-clip: text;
- color: transparent;
- max-height: 10em;
- overflow: hidden;
-
- transition: max-height 0.25s;
-
- // Disable links
- pointer-events: none;
- cursor: unset;
-
- a {
- color: unset;
- text-decoration: underline;
- &::after { content: none; }
- }
- }
-
- .wiki-excerpt-readmore {
- display: flex;
- justify-content: center;
- align-items: center;
-
- position: absolute;
- bottom: 0;
- left: 0;
- right: 0;
- height: 3em;
-
- // Makes the button appear in the middle of the animation
- transition: visibility 0s 0.125s;
-
- span {
- padding: 0.5em 1em;
- background: var(--color-section);
- border-radius: 6px;
- }
- }
-
- &.hidden{
- .wiki-excerpt-toggle { transform: rotate(-90deg); }
- .styled-dtext {
- max-height: 0;
- }
- .wiki-excerpt-readmore { visibility: hidden; }
- }
- }
-
- .paginator {
- padding: 1em 0;
+ .posts-container {
+ flex: 1; // See Exhibit A
+ grid-auto-rows: min-content;
}
}
}
@@ -124,6 +92,11 @@
flex-flow: column;
gap: 1em;
+ padding: 0.5rem 0.25rem;
+ background-color: #152f56;
+ background-color: themed("color-foreground");
+ box-shadow: inset 0px 0.25rem 0.25rem -0.25rem themed("color-background");
+
// Mode selection
#mode-box-mode, #mode-box #set-id {
width: 100%;
@@ -139,11 +112,170 @@
// Desktop
.post-index {
- @include window-larger-than(800px) {
+ @include window-larger-than(50rem) {
grid-template-areas:
"search content"
"sidebar content";
- grid-template-columns: 15em 1fr;
+ grid-template-columns: 14rem 1fr;
grid-template-rows: min-content 1fr;
+
+ .search {
+ box-shadow: inset -0.25rem 0px 0.25rem -0.25rem themed("color-background");
+ margin-top: 0.25rem;
+ border-top-left-radius: 0.25rem;
+ padding: 0.5rem;
+
+ .search-controls {
+ display: flex;
+ margin-top: 0.5rem;
+ }
+ }
+
+ .sidebar {
+ box-shadow: inset -0.25rem 0px 0.25rem -0.25rem themed("color-background");
+ margin-bottom: 0.25rem;
+ border-bottom-left-radius: 0.25rem;
+ padding: 0.5rem
+ }
+
+ .content {
+ border-radius: 0.25rem;
+ }
+ }
+}
+
+
+// Fullscreen
+body.c-posts.a-index[data-st-fullscreen="true"] {
+ // Desktop-only, for obvious reasons
+ @include window-larger-than(50rem) {
+ .post-index {
+ grid-template-areas:
+ "search "
+ "content";
+ grid-template-columns: 1fr;
+
+ .search {
+ display: flex;
+
+ border-radius: 0.25rem 0.25rem 0 0;
+ box-shadow: inset 0px -0.25rem 0.25rem -0.25rem themed("color-background");
+
+ .post-search {
+ flex: 1;
+ }
+
+ .search-controls {
+ display: flex;
+ justify-content: right;
+ align-self: end;
+ margin: 0 0 0 0.5rem;
+
+ .st-button.w100 {
+ width: unset;
+ span { display: none; }
+ }
+ }
+ }
+ .sidebar { display: none; }
+ .content {
+ border-radius: 0 0 0.25rem 0.25rem;
+ }
+ }
+ }
+}
+
+
+// FEATURES
+// Wiki Excerpt
+.wiki-excerpt {
+ display: flex;
+ flex-flow: column;
+ position: relative;
+
+ background: themed("color-section");
+ border-radius: 0.25rem;
+
+ // header
+ h3 {
+ cursor: pointer;
+ padding: 0.5rem 1rem 0.5rem 1.5rem;
+
+ &::after {
+ @include font-awesome-icon;
+ content: unicode("f0da");
+
+ transition: transform 0.25s;
+
+ position: absolute;
+ top: 0;
+ left: 0;
+ padding: 0.5rem;
+ }
+ }
+
+ // body
+ .styled-dtext {
+ background: linear-gradient(to top, themed("color-section"), themed("color-text"));
+ -webkit-background-clip: text;
+ background-clip: text;
+ color: transparent;
+
+ max-height: 0rem;
+ max-width: 50rem;
+ overflow: hidden;
+ padding: 0 0.5rem;
+
+ transition: max-height 0.25s;
+
+ // Disable links
+ pointer-events: none;
+ cursor: unset;
+
+ a {
+ color: unset;
+ text-decoration: underline;
+ &::after { content: none; }
+ }
+
+ p:last-child { margin-bottom: 0; }
+ }
+
+ // wiki link
+ .wiki-excerpt-readmore {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+
+ height: 3rem;
+ max-width: 50rem;
+ box-sizing: border-box;
+
+ // Makes the button appear in the middle of the animation
+ transition: visibility 0s 0.125s;
+ visibility: hidden;
+
+ span {
+ padding: 0.5rem 1rem;
+ background: themed("color-section");
+ border-radius: 0.25rem;
+ }
+ }
+
+ &.open{
+ .wiki-excerpt-toggle::after { transform: rotate(90deg); }
+ .styled-dtext {
+ max-height: 10rem;
+ }
+ .wiki-excerpt-readmore { visibility: visible; }
+ }
+
+ &.loading {
+ h3::after, .styled-dtext { transition: none; }
}
}
diff --git a/app/javascript/src/styles/specific/post_mode_menu.scss b/app/javascript/src/styles/specific/post_mode_menu.scss
index 6e02eff31..467f43954 100644
--- a/app/javascript/src/styles/specific/post_mode_menu.scss
+++ b/app/javascript/src/styles/specific/post_mode_menu.scss
@@ -20,7 +20,7 @@ $modes: (
);
@each $mode, $color in $modes {
- #page[data-mode-menu="#{$mode}"] {
+ #page[data-mode-menu="#{$mode}"] .content {
background-color: $color;
}
}
diff --git a/app/javascript/src/styles/specific/posts.scss b/app/javascript/src/styles/specific/posts.scss
index 1a73bec76..04d2a4c54 100644
--- a/app/javascript/src/styles/specific/posts.scss
+++ b/app/javascript/src/styles/specific/posts.scss
@@ -2,6 +2,7 @@
#has-parent-relationship-preview, #has-children-relationship-preview {
+ display: flex;
flex-direction: row;
flex-wrap: wrap;
diff --git a/app/javascript/src/styles/specific/site_map.scss b/app/javascript/src/styles/specific/site_map.scss
index 562e4f6fb..7dfccf9f8 100644
--- a/app/javascript/src/styles/specific/site_map.scss
+++ b/app/javascript/src/styles/specific/site_map.scss
@@ -1,20 +1,12 @@
-
-
div#c-static {
div#a-site-map {
- width: 80em;
+ display: flex;
+ flex-flow: row wrap;
+ max-width: 80em;
section {
width: 20em;
- float: left;
-
- h1 {
- font-size: $h3-size;
- }
-
- ul {
- margin-bottom: 1.5em;
- }
+ ul { margin-bottom: 1.5em; }
}
}
}
diff --git a/app/javascript/src/styles/specific/users.scss b/app/javascript/src/styles/specific/users.scss
index a3bbd9205..0108e4c8c 100644
--- a/app/javascript/src/styles/specific/users.scss
+++ b/app/javascript/src/styles/specific/users.scss
@@ -249,8 +249,9 @@ div#c-users {
div.input {
input[type="text"], input[type="email"], input[type="password"], select {
- width: 100%;
- max-width: unset;
+ // z_responsive is the absolute worst
+ width: 100% !important;
+ max-width: unset !important;
box-sizing: border-box;
}
}
diff --git a/app/logical/git_helper.rb b/app/logical/git_helper.rb
index 4de133bd1..fe4db750e 100644
--- a/app/logical/git_helper.rb
+++ b/app/logical/git_helper.rb
@@ -3,18 +3,27 @@
module GitHelper
def self.init
if Rails.root.join("REVISION").exist?
- @hash = Rails.root.join("REVISION").read.strip
+ @hash = @tag = Rails.root.join("REVISION").read.strip
elsif system("type git > /dev/null && git rev-parse --show-toplevel > /dev/null")
@hash = `git rev-parse HEAD`.strip
+ @tag = `git describe --abbrev=0`
else
- @hash = ""
+ @hash = @tag = ""
end
end
+ def self.tag
+ @tag
+ end
+
def self.hash
@hash
end
+ def self.version
+ @tag.presence || short_hash
+ end
+
def self.short_hash
@hash[0..8]
end
@@ -22,4 +31,13 @@ module GitHelper
def self.commit_url(commit_hash)
"#{Danbooru.config.source_code_url}/commit/#{commit_hash}"
end
+
+ def self.release_url(tag_name)
+ "#{Danbooru.config.source_code_url}/releases/tag/#{tag_name}"
+ end
+
+ def self.version_url
+ return release_url(@tag) if @tag.present?
+ commit_url(@hash)
+ end
end
diff --git a/app/models/user_feedback.rb b/app/models/user_feedback.rb
index 811994906..4c21ccd74 100644
--- a/app/models/user_feedback.rb
+++ b/app/models/user_feedback.rb
@@ -38,9 +38,6 @@ class UserFeedback < ApplicationRecord
def log_destroy
ModAction.log(:user_feedback_destroy, { user_id: user_id, reason: body, type: category, record_id: id })
- deletion_user = "\"#{CurrentUser.name}\":/users/#{CurrentUser.id}"
- creator_user = "\"#{creator.name}\":/users/#{creator.id}"
- StaffNote.create(body: "#{deletion_user} deleted #{category} feedback, created #{created_at.to_date} by #{creator_user}: #{body}", user_id: user_id, creator: User.system)
end
end
diff --git a/app/views/comments/partials/show/_comment.html.erb b/app/views/comments/partials/show/_comment.html.erb
index 5c1db74cd..86aff0df3 100644
--- a/app/views/comments/partials/show/_comment.html.erb
+++ b/app/views/comments/partials/show/_comment.html.erb
@@ -1,4 +1,4 @@
-<% if comment.should_see?(CurrentUser.user) || (current_page?(:controller => "comments", :action => "show") && CurrentUser.id == comment.creator_id) %>
+<% if comment.should_see?(CurrentUser.user) || (params[:controller] == "comments" && params[:action] == "show" && CurrentUser.id == comment.creator_id) %>