summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElias Haugsbakk <[email protected]>2026-07-22 00:48:08 +0200
committerElias Haugsbakk <[email protected]>2026-07-22 01:49:58 +0200
commit8d86458282efae6c3fc26ab8fb4bd61d1a4e01e3 (patch)
tree1efd9709886179b8469a5031d75dba9c35e37588
parent8a4935f7ba29cc39781c0d3169e81d6af3bd5761 (diff)
refine articles and article search styling
-rw-r--r--clams-server/src/main/java/no/eliashaugsbakk/clams/server/config/AppRoutes.java6
-rw-r--r--clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/ArticleController.java2
-rw-r--r--clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/ArticlesController.java36
-rw-r--r--clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/MediaController.java2
-rw-r--r--clams-server/src/main/resources/public/css/articles.css56
-rw-r--r--clams-server/src/main/resources/public/css/common.css49
-rw-r--r--clams-server/src/main/resources/public/css/projects.css8
-rw-r--r--clams-server/src/main/resources/templates/articles-search.html5
-rw-r--r--clams-server/src/main/resources/templates/articles.html11
-rw-r--r--clams-server/src/main/resources/templates/projects.html18
10 files changed, 116 insertions, 77 deletions
diff --git a/clams-server/src/main/java/no/eliashaugsbakk/clams/server/config/AppRoutes.java b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/config/AppRoutes.java
index 3df4d6d..9deef6b 100644
--- a/clams-server/src/main/java/no/eliashaugsbakk/clams/server/config/AppRoutes.java
+++ b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/config/AppRoutes.java
@@ -26,7 +26,7 @@ public class AppRoutes implements EndpointGroup {
Map.of("page_title", "Projects - Elias Haugsbakk", "page_css", "projects")));
path("articles", () -> {
- get(appContext.getArticlesController()::handleArticlesRequest);
+ get(appContext.getArticlesController()::handleGetArticles);
get("{slug}", appContext.getArticlesController()::handleGetArticle);
});
@@ -50,13 +50,13 @@ public class AppRoutes implements EndpointGroup {
}
});
- post("articles", appContext.getArticleController()::handleArticleArticle);
+ post("articles", appContext.getArticleController()::handlePostArticle);
put("articles/{slug}", appContext.getArticleController()::handlePutArticle);
delete("articles/{slug}", appContext.getArticleController()::handleDeleteArticle);
get("media", appContext.getMediaController()::handleGetMediaIndex);
get("media/{uuid}", appContext.getMediaController()::handleGetMedia);
- post("media", appContext.getMediaController()::handleArticleMedia);
+ post("media", appContext.getMediaController()::handlePostMedia);
delete("media/{uuid}", appContext.getMediaController()::handleDeleteMedia);
});
}
diff --git a/clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/ArticleController.java b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/ArticleController.java
index a57414f..8759b1a 100644
--- a/clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/ArticleController.java
+++ b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/ArticleController.java
@@ -15,7 +15,7 @@ public class ArticleController {
this.slugService = slugService;
}
- public void handleArticleArticle(Context ctx) {
+ public void handlePostArticle(Context ctx) {
ArticleDTO newArticle = ctx.bodyAsClass(ArticleDTO.class);
articlesRepo.addArticle(new Article(newArticle, slugService.toSlug(newArticle.title())));
}
diff --git a/clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/ArticlesController.java b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/ArticlesController.java
index f013419..80a1cb5 100644
--- a/clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/ArticlesController.java
+++ b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/ArticlesController.java
@@ -24,7 +24,7 @@ public class ArticlesController {
private final ArticlesRepo articlesRepo;
private final ArticlesSearchService articlesSearchService;
- public record SearchResultItem(String title, String slug, String summary, String formattedDate) {}
+ public record ArticleItem(String title, String slug, String summary, String formattedDate, int year) {}
public ArticlesController(SqliteManager sqliteManager) {
this.articlesRepo = new ArticlesRepoSqlite(sqliteManager);
@@ -58,7 +58,7 @@ public class ArticlesController {
));
}
- public void handleArticlesRequest(Context ctx) {
+ public void handleGetArticles(Context ctx) {
String searchTerm = ctx.queryParam("search");
if (searchTerm != null) {
handleSearch(ctx, searchTerm);
@@ -71,23 +71,28 @@ public class ArticlesController {
List<ArticleMetaData> allArticles = articlesRepo.listArticlesMetaData().stream()
.filter(ArticleMetaData::isPublished)
.toList();
- Map<Integer, List<ArticleMetaData>> articlesByYear = groupArticlesByYear(allArticles);
- var featuredSlugs = List.of(
- ""
- );
+ Map<Integer, List<ArticleMetaData>> articlesByYear = groupArticlesByYear(allArticles);
- List<Article> featuredArticles = new ArrayList<>();
- for (String slug : featuredSlugs) {
- articlesRepo.getArticle(slug).ifPresent(featuredArticles::add);
- }
+ Map<Integer, List<ArticleItem>> articleItemsByYear = articlesByYear.entrySet().stream()
+ .collect(Collectors.toMap(
+ Map.Entry::getKey,
+ entry -> entry.getValue().stream()
+ .map(article -> new ArticleItem(
+ article.title(),
+ article.slug(),
+ article.summary() != null ? article.summary() : "",
+ article.timePublished().atZone(OSLO_ZONE).format(DATE_FORMATTER),
+ article.timePublished().atZone(OSLO_ZONE).getYear()
+ ))
+ .toList()
+ ));
ctx.render("templates/articles.html",
Map.of(
"page_title", "My articles",
"page_css", "articles",
- "articles_by_year", articlesByYear,
- "featured_articles", featuredArticles,
+ "articles_by_year", articleItemsByYear,
"search_value", ""
));
}
@@ -96,13 +101,14 @@ public class ArticlesController {
String query = searchTerm.trim();
List<ArticleMetaData> results = query.isEmpty() ? List.of() : articlesSearchService.searchArticles(query);
- List<SearchResultItem> resultItems = results.stream()
+ List<ArticleItem> resultItems = results.stream()
.filter(ArticleMetaData::isPublished)
- .map(article -> new SearchResultItem(
+ .map(article -> new ArticleItem(
article.title(),
article.slug(),
article.summary() != null ? article.summary() : "",
- article.timePublished().atZone(OSLO_ZONE).format(DATE_FORMATTER)
+ article.timePublished().atZone(OSLO_ZONE).format(DATE_FORMATTER),
+ article.timePublished().atZone(OSLO_ZONE).getYear()
))
.toList();
diff --git a/clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/MediaController.java b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/MediaController.java
index d4e40b9..8855b7b 100644
--- a/clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/MediaController.java
+++ b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/MediaController.java
@@ -28,7 +28,7 @@ public class MediaController {
this.appConfig = appConfig;
}
- public void handleArticleMedia(Context ctx) {
+ public void handlePostMedia(Context ctx) {
UploadedFile file = ctx.uploadedFile("image");
if (file == null) {
diff --git a/clams-server/src/main/resources/public/css/articles.css b/clams-server/src/main/resources/public/css/articles.css
index a3050b0..3ff7648 100644
--- a/clams-server/src/main/resources/public/css/articles.css
+++ b/clams-server/src/main/resources/public/css/articles.css
@@ -25,19 +25,7 @@
}
.search-form button {
- font-family: var(--font-serif), serif;
- padding: 0.4rem 0.7rem;
- background: #e8e8e4;
- color: var(--text-color);
- border: 1px solid var(--border-color);
- border-radius: 4px;
- cursor: pointer;
- font-weight: bold;
- font-size: 0.8rem;
-}
-
-.search-form button:hover {
- background: #ddd;
+ margin-left: 0.2rem;
}
.year-accordion {
@@ -50,6 +38,7 @@
font-weight: bold;
list-style: none;
margin-bottom: 0.5rem;
+ transition: color 0.2s ease;
}
.year-toggle::-webkit-details-marker {
@@ -57,7 +46,7 @@
}
.year-toggle:hover {
- opacity: 0.7;
+ color: var(--accent-color)
}
.article-list {
@@ -66,15 +55,28 @@
border-left: 2px solid var(--border-color);
}
-.article-list li {
+.article-list-item,
+.search-result-item {
margin-bottom: 0.8rem;
+ font-size: 1rem;
+ line-height: 1.6;
}
-.article-list a {
+.article-list a,
+.search-result-item a {
font-size: 1rem;
font-weight: normal;
}
+.article-date {
+ display: inline-block;
+ font-size: 0.9rem;
+ color: var(--text-color);
+ opacity: 0.65;
+ margin-right: 0.9rem;
+ min-width: 6.8rem;
+}
+
.featured-desc {
font-weight: normal;
color: var(--text-color);
@@ -82,29 +84,15 @@
}
.search-results-container {
- max-width: 40ch;
- margin: 1.8rem auto 0 auto;
+ max-width: 100%;
+ margin: 1.8rem 0 0 0;
}
.search-results-list {
list-style-type: none;
- padding: 0;
margin: 0;
-}
-
-.search-result-item {
- margin-bottom: 1.25rem;
- font-size: 1rem;
- line-height: 1.6;
-}
-
-.search-result-date {
- display: inline-block;
- font-size: 0.9rem;
- color: var(--text-color);
- opacity: 0.65;
- margin-right: 0.9rem;
- min-width: 6.8rem;
+ padding: 0 0 0 2.2rem !important;
+ border-left: 2px solid var(--border-color);
}
.search-result-item a {
diff --git a/clams-server/src/main/resources/public/css/common.css b/clams-server/src/main/resources/public/css/common.css
index 89748f0..dc81927 100644
--- a/clams-server/src/main/resources/public/css/common.css
+++ b/clams-server/src/main/resources/public/css/common.css
@@ -5,6 +5,8 @@
--text-color: #363737;
--border-color: #ccc;
--accent-color: #8fae8f;
+ --surface-color: #e8e8e4;
+ --surface-hover-color: #ddd;
--font-serif: "Spectral", Georgia, serif;
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
--font-size-body: 1.2rem;
@@ -93,6 +95,32 @@ a:hover {
color: var(--accent-color);
}
+.pill-button {
+ font-family: var(--font-serif), serif;
+ padding: 0.4rem 0.7rem;
+ background: var(--surface-color);
+ color: var(--text-color);
+ border: 1px solid var(--border-color);
+ border-radius: 4px;
+ cursor: pointer;
+ font-weight: bold;
+ font-size: 0.8rem;
+ text-decoration: none;
+ display: inline-block;
+ transition: background-color 0.2s ease, color 0.2s ease;
+}
+
+.pill-button:hover,
+.pill-button:focus-visible {
+ background: var(--surface-hover-color);
+ color: var(--text-color);
+}
+
+.pill-button:focus-visible {
+ outline: 1px solid var(--accent-color);
+ outline-offset: 2px;
+}
+
footer {
padding: 1.875rem 1.25rem;
border-top: 1px solid var(--border-color);
@@ -100,6 +128,11 @@ footer {
font-size: var(--font-size-small);
}
+footer a {
+ font-weight: normal;
+ font-size: 1rem;
+}
+
footer p {
margin: 0;
display: flex;
@@ -172,14 +205,26 @@ footer p {
font-size: var(--font-size-caption);
text-decoration: none;
font-weight: bold;
+ color: var(--text-color);
opacity: 0.7;
- transition: color 0.2s ease;
+ transition: color 0.2s ease, opacity 0.2s ease;
+ display: inline-flex;
+ align-items: center;
+ gap: 0.2rem;
}
-.back-link-top:hover {
+.back-link-top:hover,
+.back-link-top:focus-visible {
+ color: var(--accent-color);
opacity: 1;
}
+.back-link-top:focus-visible {
+ outline: 1px solid var(--accent-color);
+ outline-offset: 2px;
+ border-radius: 2px;
+}
+
@media (max-width: 768px) {
.back-link-top {
position: static;
diff --git a/clams-server/src/main/resources/public/css/projects.css b/clams-server/src/main/resources/public/css/projects.css
index f56955b..d953411 100644
--- a/clams-server/src/main/resources/public/css/projects.css
+++ b/clams-server/src/main/resources/public/css/projects.css
@@ -44,15 +44,7 @@
.project-actions a {
font-size: 0.85rem;
- text-decoration: none;
- border: 1px solid var(--border-color);
padding: 0.2rem 0.6rem;
- border-radius: 4px;
- background: #e8e8e4;
-}
-
-.project-actions a:hover {
- background: #ddd;
}
@media (max-width: 768px) {
diff --git a/clams-server/src/main/resources/templates/articles-search.html b/clams-server/src/main/resources/templates/articles-search.html
index dd435e5..54eb609 100644
--- a/clams-server/src/main/resources/templates/articles-search.html
+++ b/clams-server/src/main/resources/templates/articles-search.html
@@ -9,7 +9,7 @@
<form action="/articles" method="GET" class="search-form">
<input type="text" name="search" value="{{ search_term }}" placeholder="Search articles..." aria-label="Search articles">
- <button type="submit">Search</button>
+ <button type="submit" class="pill-button">Search</button>
</form>
</header>
@@ -27,9 +27,10 @@
<ul class="search-results-list">
{% for article in results %}
<li class="search-result-item">
- <span class="search-result-date">{{ article.formattedDate }}</span>
+ <span class="article-date">{{ article.formattedDate }}</span>
<a href="/articles/{{ article.slug }}">{{ article.title }}</a>
{% if article.summary is not empty %}
+ <br>
<span class="search-result-summary">-- {{ article.summary }}</span>
{% endif %}
</li>
diff --git a/clams-server/src/main/resources/templates/articles.html b/clams-server/src/main/resources/templates/articles.html
index 905ed52..5feddae 100644
--- a/clams-server/src/main/resources/templates/articles.html
+++ b/clams-server/src/main/resources/templates/articles.html
@@ -10,7 +10,7 @@
<form action="/articles" method="GET" class="search-form">
<input type="text" name="search" value="{{ search_value }}" placeholder="Search articles..."
aria-label="Search articles">
- <button type="submit">Search</button>
+ <button type="submit" class="pill-button">Search</button>
</form>
</header>
@@ -21,7 +21,14 @@
<summary class="year-toggle">{{ group.key }}</summary>
<ul class="article-list">
{% for article in group.value %}
- <li><a href="/articles/{{ article.slug }}">{{ article.title }}</a></li>
+ <li class="article-list-item">
+ <span class="article-date">{{ article.formattedDate }}</span>
+ <a href="/articles/{{ article.slug }}">{{ article.title }}</a>
+ {% if article.summary is not empty %}
+ <br>
+ <span class="search-result-summary">-- {{ article.summary }}</span>
+ {% endif %}
+ </li>
{% endfor %}
</ul>
</details>
diff --git a/clams-server/src/main/resources/templates/projects.html b/clams-server/src/main/resources/templates/projects.html
index 7a1f440..8d476cd 100644
--- a/clams-server/src/main/resources/templates/projects.html
+++ b/clams-server/src/main/resources/templates/projects.html
@@ -15,9 +15,9 @@
<p>Short project description / introduction.</p>
</div>
<div class="project-actions">
- <a href="#">Read More</a>
- <a href="#">Self-Hosted Git</a>
- <a href="#">GitHub</a>
+ <a href="#" class="pill-button">Read More</a>
+ <a href="#" class="pill-button">Self-Hosted Git</a>
+ <a href="#" class="pill-button">GitHub</a>
</div>
</li>
@@ -27,9 +27,9 @@
<p>Short project description / introduction.</p>
</div>
<div class="project-actions">
- <a href="#">Read More</a>
- <a href="#">Self-Hosted Git</a>
- <a href="#">GitHub</a>
+ <a href="#" class="pill-button">Read More</a>
+ <a href="#" class="pill-button">Self-Hosted Git</a>
+ <a href="#" class="pill-button">GitHub</a>
</div>
</li>
@@ -39,9 +39,9 @@
<p>A short one-line description.</p>
</div>
<div class="project-actions">
- <a href="#">Read More</a>
- <a href="#">Self-Hosted Git</a>
- <a href="#">GitHub</a>
+ <a href="#" class="pill-button">Read More</a>
+ <a href="#" class="pill-button">Self-Hosted Git</a>
+ <a href="#" class="pill-button">GitHub</a>
</div>
</li>
</ul>