diff --git a/about.json b/about.json
index 415ba01..16a0c2e 100644
--- a/about.json
+++ b/about.json
@@ -8,7 +8,9 @@
"minimum_discourse_version": null,
"maximum_discourse_version": null,
"assets": {},
- "modifiers": {},
+ "modifiers": {
+ "svg_icons": ["fire"]
+ },
"components": [
"https://github.com/discourse/discourse-sidebar-new-topic-button.git",
"https://github.com/discourse/discourse-search-banner.git",
diff --git a/javascripts/discourse/components/card/topic-author-avatar-column.gjs b/javascripts/discourse/components/card/topic-author-avatar-column.gjs
new file mode 100644
index 0000000..260e158
--- /dev/null
+++ b/javascripts/discourse/components/card/topic-author-avatar-column.gjs
@@ -0,0 +1,9 @@
+import avatar from "discourse/helpers/avatar";
+
+const TopicAuthorAvatarColumn =
+
+ {{avatar @topic.creator imageSize="large"}}
+
+;
+
+export default TopicAuthorAvatarColumn;
diff --git a/javascripts/discourse/components/card/topic-author-column.gjs b/javascripts/discourse/components/card/topic-author-column.gjs
new file mode 100644
index 0000000..a239d73
--- /dev/null
+++ b/javascripts/discourse/components/card/topic-author-column.gjs
@@ -0,0 +1,5 @@
+const TopicAuthorColumn =
+ @{{@topic.creator.username}}
+;
+
+export default TopicAuthorColumn;
diff --git a/javascripts/discourse/components/card/topic-category-column.gjs b/javascripts/discourse/components/card/topic-category-column.gjs
new file mode 100644
index 0000000..ca8ae87
--- /dev/null
+++ b/javascripts/discourse/components/card/topic-category-column.gjs
@@ -0,0 +1,7 @@
+import { categoryLinkHTML } from "discourse/helpers/category-link";
+
+const TopicCategoryColumn =
+ {{categoryLinkHTML @topic.category}}
+;
+
+export default TopicCategoryColumn;
diff --git a/javascripts/discourse/components/card/topic-likes-column.gjs b/javascripts/discourse/components/card/topic-likes-column.gjs
new file mode 100644
index 0000000..f1d6aff
--- /dev/null
+++ b/javascripts/discourse/components/card/topic-likes-column.gjs
@@ -0,0 +1,9 @@
+import icon from "discourse/helpers/d-icon";
+
+const TopicLikesColumn =
+ {{#if @topic.like_count}}
+ {{icon "heart"}}{{@topic.like_count}}
+ {{/if}}
+;
+
+export default TopicLikesColumn;
diff --git a/javascripts/discourse/components/card/topic-replies-column.gjs b/javascripts/discourse/components/card/topic-replies-column.gjs
new file mode 100644
index 0000000..53e2ac8
--- /dev/null
+++ b/javascripts/discourse/components/card/topic-replies-column.gjs
@@ -0,0 +1,9 @@
+import icon from "discourse/helpers/d-icon";
+
+const TopicRepliesColumn =
+ {{#if @topic.posts_count}}
+ {{icon "reply"}}{{@topic.posts_count}}
+ {{/if}}
+;
+
+export default TopicRepliesColumn;
diff --git a/javascripts/discourse/components/card/topic-status-column.gjs b/javascripts/discourse/components/card/topic-status-column.gjs
new file mode 100644
index 0000000..9e207ad
--- /dev/null
+++ b/javascripts/discourse/components/card/topic-status-column.gjs
@@ -0,0 +1,115 @@
+import Component from "@glimmer/component";
+import { on } from "@ember/modifier";
+import { action } from "@ember/object";
+import { service } from "@ember/service";
+import { and } from "truth-helpers";
+import icon from "discourse/helpers/d-icon";
+import i18n from "discourse-i18n";
+
+export default class TopicStatusColumn extends Component {
+ @service currentUser;
+ @service siteSettings;
+
+ get canAct() {
+ return this.currentUser && !this.args.disableActions;
+ }
+
+ get statusClass() {
+ let classes = ["topic-status-card"];
+ if (this.args.topic.bookmarked) {
+ classes.push("--bookmark");
+ } else if (this.args.topic.closed && this.args.topic.archived) {
+ classes.push("--locked --archived");
+ } else if (this.args.topic.closed) {
+ classes.push("--locked");
+ } else if (this.args.topic.archived) {
+ classes.push("--archived");
+ } else if (this.args.topic.is_warning) {
+ classes.push("--warning");
+ } else if (
+ this.args.showPrivateMessageIcon &&
+ this.args.topic.isPrivateMessage
+ ) {
+ classes.push("--private-message");
+ } else if (this.args.topic.pinned) {
+ classes.push("--pinned");
+ } else if (this.args.topic.unpinned) {
+ classes.push("--unpinned");
+ }
+ return classes.join(" ");
+ }
+
+ get heatMap() {
+ return this.args.topic.views > this.siteSettings.topic_views_heat_medium;
+ }
+
+ @action
+ togglePinned(event) {
+ event.preventDefault();
+ this.args.topic.togglePinnedForUser();
+ }
+
+
+ {{#if @topic.bookmarked}}
+ {{icon "bookmark"}}{{i18n
+ (themePrefix "topic_bookmarked")
+ }}
+ {{/if}}
+ {{#if (and @topic.closed @topic.archived)~}}
+ {{i18n
+ (themePrefix "topic_closed_and_archived")
+ }}
+ {{else if @topic.closed}}
+ {{i18n
+ (themePrefix "topic_closed")
+ }}
+ {{else if @topic.archived}}
+ {{i18n
+ (themePrefix "topic_archived")
+ }}
+ {{/if}}
+ {{#if @topic.is_warning}}
+ {{i18n
+ (themePrefix "topic_warning")
+ }}
+ {{else if (and @showPrivateMessageIcon @topic.isPrivateMessage)}}
+ {{i18n
+ (themePrefix "topic_personal_message")
+ }}
+ {{/if}}
+ {{#if @topic.pinned}}
+ {{#if this.canAct}}
+
+ {{else}}
+ {{icon "thumbtack"}}{{i18n
+ (themePrefix "topic_pinned")
+ }}
+ {{/if}}
+ {{else if @topic.unpinned}}
+ {{#if this.canAct}}
+
+ {{else}}
+ {{icon
+ "thumbtack"
+ class="unpinned"
+ }}{{i18n (themePrefix "topic_unpinned")}}
+ {{/if}}
+ {{/if}}
+
+ {{#if this.heatMap}}
+ {{icon "fire"}}{{i18n
+ (themePrefix "topic_hot")
+ }}
+ {{/if}}
+
+}
diff --git a/javascripts/discourse/initializers/topic-list-columns.gjs b/javascripts/discourse/initializers/topic-list-columns.gjs
new file mode 100644
index 0000000..0fa2e50
--- /dev/null
+++ b/javascripts/discourse/initializers/topic-list-columns.gjs
@@ -0,0 +1,81 @@
+import { withPluginApi } from "discourse/lib/plugin-api";
+import TopicAuthorAvatarColumn from "../components/card/topic-author-avatar-column";
+import TopicAuthorColumn from "../components/card/topic-author-column";
+import TopicCategoryColumn from "../components/card/topic-category-column";
+import TopicLikesColumn from "../components/card/topic-likes-column";
+import TopicRepliesColumn from "../components/card/topic-replies-column";
+import TopicStatusColumn from "../components/card/topic-status-column";
+
+const TopicAuthor =
+ |
+
+ |
+;
+
+const TopicAuthorAvatar =
+
+
+ |
+;
+
+const TopicCategoryStatus =
+
+
+
+ |
+;
+
+const TopicLikesReplies =
+
+
+
+ |
+;
+
+export default {
+ name: "topic-list-customizations",
+
+ initialize() {
+ withPluginApi("1.39.0", (api) => {
+ api.registerValueTransformer(
+ "topic-list-columns",
+ ({ value: columns }) => {
+ columns.add("topic-author", {
+ item: TopicAuthor,
+ after: "activity",
+ });
+ columns.add("topic-category-status", {
+ item: TopicCategoryStatus,
+ after: "topic-author",
+ });
+ columns.add("topic-author-avatar", {
+ item: TopicAuthorAvatar,
+ after: "topic-category-status",
+ });
+ columns.add("topic-likes-replies", {
+ item: TopicLikesReplies,
+ after: "topic-author-avatar",
+ });
+ columns.delete("posters");
+ columns.delete("views");
+ columns.delete("replies");
+ return columns;
+ }
+ );
+
+ api.registerValueTransformer(
+ "topic-list-item-class",
+ ({ value: classes, context }) => {
+ if (context.topic.pinned || context.topic.pinned_globally) {
+ classes.push("--pinned");
+ }
+ return classes;
+ }
+ );
+
+ api.registerValueTransformer("topic-list-item-mobile-layout", () => {
+ return false;
+ });
+ });
+ },
+};
diff --git a/locales/en.yml b/locales/en.yml
index e38fc70..0b63ef7 100644
--- a/locales/en.yml
+++ b/locales/en.yml
@@ -1,5 +1,12 @@
en:
theme_metadata:
- description: ""
- settings:
- example_setting: A description of a setting.
+ description: "A simple, beautiful theme for the future of Discourse that improves the out of the box experience for sites."
+ topic_bookmarked: "Bookmarked"
+ topic_closed_archived: "Closed and archived"
+ topic_closed: "Closed"
+ topic_archived: "Archived"
+ topic_warning: "Warning"
+ topic_personal_message: "Personal message"
+ topic_pinned: "Pinned"
+ topic_unpinned: "Unpinned"
+ topic_hot: "Hot"
diff --git a/package.json b/package.json
index 5ac81bd..9eb29d3 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,7 @@
"prettier": "2.8.8"
},
"engines": {
- "node": ">= 22",
+ "node": ">= 18",
"npm": "please-use-pnpm",
"yarn": "please-use-pnpm",
"pnpm": "9.x"
diff --git a/scss/topic-cards.scss b/scss/topic-cards.scss
index 0940c62..f410b09 100644
--- a/scss/topic-cards.scss
+++ b/scss/topic-cards.scss
@@ -1,3 +1,331 @@
+// 390x844 – mobile/portrait – (Figma iPhone 13 & 14)
+// 744x1133 – tablet/portrait – (Figma iPad mini 8.3)
+// 1280x832 – desktop small – (Figma MacBook Air)
+
+:root {
+ --hot-color: oklch(63.79% 0.1823 34.77);
+}
+
+$extra-small: 435px;
+$small: 576px;
+$medium: 980px;
+$extra-large: 1280px;
+
+.topic-list .topic-list-item-separator {
+ display: none;
+}
+
+.topic-list > .topic-list-body > .topic-list-item.last-visit {
+ border-bottom: 1px solid var(--primary-300);
+}
+
+.topic-list-body {
+ border: none;
+ display: flex;
+ flex-direction: column;
+ gap: 1em;
+ @media screen and (max-width: $extra-small) {
+ gap: 0.5em;
+ }
+}
+
+body.user-messages-page .topic-list-item {
+ .topic-category-status-data {
+ display: none;
+ }
+ grid-template-areas:
+ "avatar author status status . . activity"
+ ". topic-title topic-title topic-title likes-replies likes-replies likes-replies";
+ &.excerpt-expanded {
+ grid-template-columns: 44px repeat(6, 1fr) auto;
+ grid-template-rows: 22px auto auto 30px;
+ grid-template-areas:
+ "avatar author status status . . . activity"
+ "avatar topic-title topic-title topic-title topic-title . . ."
+ ". excerpt excerpt excerpt excerpt excerpt . ."
+ ". excerpt excerpt excerpt excerpt excerpt likes-replies likes-replies";
+ @media screen and (max-width: $extra-large) {
+ grid-template-areas:
+ "avatar author status status . . . activity"
+ "avatar topic-title topic-title topic-title topic-title . . ."
+ ". excerpt excerpt excerpt excerpt excerpt . likes-replies"
+ ". excerpt excerpt excerpt excerpt excerpt . likes-replies";
+ }
+ }
+ @media screen and (max-width: $small) {
+ grid-template-columns: 25px auto repeat(6, 1fr);
+ grid-template-rows: auto auto;
+ grid-template-areas:
+ "topic-title topic-title topic-title topic-title topic-title topic-title topic-title activity"
+ "avatar author . . . . . likes-replies";
+ .topic-excerpt {
+ display: none;
+ }
+ }
+}
+
+.topic-list-item {
+ -webkit-font-smoothing: antialiased;
+ text-overflow: ellipsis;
+ padding: 0.75em 1rem;
+ border: 1px solid var(--primary-300);
+ display: grid;
+ grid-template-columns: 44px min-content min-content auto min-content min-content min-content;
+
+ grid-template-rows: 22px minmax(22px, auto);
+ grid-template-areas:
+ "avatar author status status . . activity"
+ ". topic-title topic-title topic-title likes-replies likes-replies category";
+ &.excerpt-expanded {
+ grid-template-columns: 44px repeat(6, 1fr) auto;
+ grid-template-rows: 22px auto auto 30px;
+ grid-template-areas:
+ "avatar author status status . . . activity"
+ "avatar topic-title topic-title topic-title topic-title . . ."
+ ". excerpt excerpt excerpt excerpt excerpt . ."
+ ". excerpt excerpt excerpt excerpt excerpt likes-replies category";
+ @media screen and (max-width: $extra-large) {
+ grid-template-areas:
+ "avatar author status status . . . activity"
+ "avatar topic-title topic-title topic-title topic-title . . ."
+ ". excerpt excerpt excerpt excerpt excerpt . likes-replies"
+ ". excerpt excerpt excerpt excerpt excerpt . category";
+ }
+ @media screen and (max-width: $small) {
+ grid-template-columns: 25px auto repeat(6, 1fr);
+ grid-template-rows: auto auto auto;
+ grid-template-areas:
+ "category-status category-status category-status . . . . activity"
+ "topic-title topic-title topic-title topic-title topic-title topic-title topic-title topic-title"
+ "avatar author . . . . . likes-replies";
+ .topic-excerpt {
+ display: none;
+ }
+ }
+ }
+ grid-column-gap: 12px;
+ grid-row-gap: 8px;
+ border-radius: var(--d-border-radius);
+ @media screen and (max-width: $medium) {
+ grid-template-columns: 44px min-content min-content auto min-content min-content min-content;
+ grid-template-rows: 22px minmax(22px, auto);
+ grid-template-areas:
+ "avatar author status status . . activity"
+ ". topic-title topic-title topic-title . . likes-replies"
+ ". topic-title topic-title topic-title . . category";
+ }
+ @media screen and (max-width: $small) {
+ grid-template-columns: 25px auto repeat(6, 1fr);
+ grid-template-rows: auto auto auto;
+ grid-template-areas:
+ "category-status category-status category-status . . . . activity"
+ "topic-title topic-title topic-title topic-title topic-title topic-title topic-title topic-title"
+ "avatar author . . . . . likes-replies";
+ }
+ @media screen and (max-width: $extra-small) {
+ border: none;
+ border-bottom: 1px solid var(--primary-200);
+ }
+
+ // display contents
+ td.main-link,
+ td.posters,
+ td.posts,
+ td.views,
+ td.activity {
+ display: contents;
+ }
+
+ td.num.posts a {
+ padding: 0;
+ }
+
+ // avatar & author
+ .topic-author-avatar-data {
+ grid-area: avatar;
+ margin: 0;
+ }
+ .topic-author-avatar img.avatar {
+ width: 44px;
+ height: 44px;
+ border-radius: var(--d-border-radius);
+ @media screen and (max-width: $small) {
+ width: 25px;
+ height: 25px;
+ }
+ }
+ td.topic-author-data {
+ grid-area: author;
+ display: flex;
+ gap: 0.5em;
+ align-items: center;
+ }
+ .topic-author-data .topic-author {
+ color: var(--primary-500);
+ }
+
+ // status
+ .topic-status-card {
+ display: flex;
+ flex-direction: row;
+ gap: 4px;
+ align-items: center;
+ padding: 0 6px;
+ font-size: var(--font-down-2);
+ font-weight: 600;
+ border-radius: var(--d-border-radius);
+ border: 1px solid var(--status-color);
+ color: var(--status-color);
+ height: min-content;
+ grid-area: status;
+ width: min-content;
+ @media screen and (max-width: $small) {
+ height: calc(100% - 2px);
+ }
+ svg {
+ font-size: var(--font-down-1);
+ color: var(--status-color);
+ }
+ }
+
+ .topic-status-card.--bookmark {
+ display: none;
+ }
+ .topic-status-card.--pinned,
+ .topic-status-card.--unpinned {
+ --status-color: var(--primary-500);
+ cursor: pointer;
+ background-color: transparent;
+ line-height: unset;
+ }
+ .topic-status-card.--hot {
+ --status-color: var(--hot-color);
+ }
+
+ // title
+ td.main-link .link-top-line {
+ font-size: var(--font-0);
+ grid-area: topic-title;
+ font-weight: 500;
+ }
+
+ .link-top-line .event-date {
+ font-size: var(--font-down-3);
+ }
+
+ td.main-link a.topic-status {
+ display: none;
+ }
+
+ td.main-link .link-top-line a.raw-topic-link {
+ padding: 0;
+ }
+
+ .topic-post-badges .badge-notification.unread-posts {
+ background-color: var(--tertiary);
+ color: var(--tertiary);
+ overflow: hidden;
+ height: 8px;
+ width: 8px;
+ padding: 0;
+ top: -2px;
+ min-width: unset;
+ }
+
+ // excerpt
+ .topic-excerpt {
+ grid-area: excerpt;
+ margin: 0;
+ font-size: var(--font-down-2);
+ }
+
+ // timestamp
+ td.activity .post-activity {
+ grid-area: activity;
+ font-size: var(--font-down-1);
+ color: var(--primary-500);
+ margin-left: auto;
+ padding: 0;
+ }
+
+ // metadata
+ // metadata - category
+ td.main-link .link-bottom-line {
+ display: none;
+ }
+
+ td.topic-category-status-data {
+ display: contents;
+ @media screen and (max-width: $small) {
+ grid-area: category-status;
+ display: flex;
+ gap: 0.5em;
+ align-items: center;
+ }
+ }
+ td.topic-category-status-data .badge-category__wrapper {
+ grid-area: category;
+ }
+
+ td.topic-category-status-data .badge-category__wrapper {
+ overflow: unset;
+ border-radius: var(--d-border-radius);
+ padding: 6px;
+ align-self: flex-end;
+ background-color: light-dark(
+ oklch(from var(--category-badge-color) 97% calc(c * 0.3) h),
+ oklch(from var(--category-badge-color) 45% calc(c * 0.5) h)
+ );
+
+ @media screen and (max-width: $small) {
+ padding: 2px 6px;
+ }
+
+ .badge-category__name {
+ color: light-dark(
+ oklch(from var(--category-badge-color) 20% calc(c * 1) h),
+ oklch(from var(--category-badge-color) 100% calc(c * 0.9) h)
+ );
+ }
+ }
+ td.main-link .discourse-tags {
+ display: none;
+ }
+
+ // metadata - likes and replies
+ td.posts .badge-posts {
+ grid-area: replies;
+ align-self: center;
+ font-weight: normal;
+ }
+
+ td.topic-likes-replies-data {
+ grid-area: likes-replies;
+ display: flex;
+ flex-direction: row;
+ gap: 0.5em;
+ justify-content: flex-end;
+ height: min-content;
+ align-self: flex-end;
+ padding-bottom: 4px;
+ }
+ .topic-likes-replies-data .topic-likes,
+ .topic-likes-replies-data .topic-replies {
+ display: flex;
+ flex-direction: row;
+ gap: 0.5em;
+ align-items: center;
+ color: var(--primary-500);
+ svg {
+ color: var(--primary-600);
+ }
+ }
+}
+
+.topic-list-header {
+ display: none;
+}
+
.topic-list-item {
background: var(--d-content-background);
box-shadow: 0px 0px 26px 1px
diff --git a/settings.yml b/settings.yml
deleted file mode 100644
index e3cd1c4..0000000
--- a/settings.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-example_setting:
- default: true