Skip to content

Commit b96672b

Browse files
author
y-yamasaki
committed
Merge branch 'develop'
2 parents 15dd12b + 1cb319a commit b96672b

22 files changed

Lines changed: 311 additions & 67 deletions

assets/css/blog.css

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
}
7474

7575
.blog-card::before {
76+
cursor: pointer;
7677
content: "";
7778
position: absolute;
7879
z-index: -1;
@@ -109,6 +110,7 @@
109110

110111
/* サムネイル枠 */
111112
.blog-card .card__thumb {
113+
cursor: pointer;
112114
flex-shrink: 0;
113115
width: 14rem;
114116
height: 7.5rem;
@@ -168,7 +170,9 @@
168170
margin-top: auto; /* タグをカード下部に配置 */
169171
}
170172

171-
.blog-card .tag {
173+
/* タグ(共通) */
174+
.tag {
175+
cursor: pointer;
172176
display: inline-flex;
173177
align-items: center;
174178
padding: 0.25rem 0.75rem;
@@ -179,8 +183,9 @@
179183
color: var(--color-text);
180184
white-space: nowrap;
181185
transition: var(--transition-smooth);
186+
text-decoration: none; /* リンクの下線を消す */
182187
}
183-
.blog-card .tag:hover {
188+
.tag:hover {
184189
background: var(--color-accent);
185190
color: #fff;
186191
box-shadow: var(--shadow-glow);
@@ -224,6 +229,30 @@
224229
line-height: 1.6;
225230
}
226231

232+
/* 記事詳細ページのタグ群(一覧の .tag と見た目を揃える) */
233+
/* 記事詳細ページのタグ群(.tag を共通化したので上書きは最小限) */
234+
.post-detail__tags {
235+
margin-top: 1rem;
236+
display: flex;
237+
flex-wrap: wrap;
238+
gap: 0.4rem;
239+
align-items: center;
240+
}
241+
242+
/* キーボードフォーカスの見た目を明確にする(アクセシビリティ) */
243+
.post-detail__tags a.tag:focus {
244+
outline: none;
245+
box-shadow:
246+
0 0 0 3px rgba(0, 0, 0, 0.06),
247+
0 0 0 4px var(--color-accent-soft);
248+
border-color: var(--color-accent);
249+
}
250+
251+
/* リンク(<a class="tag">)は既存の .tag スタイルを使うので上書きは最小限にする */
252+
.post-detail__tags a.tag {
253+
text-decoration: none; /* リンク下線を消す(見た目をバッジにする) */
254+
}
255+
227256
/* ==========================================================================
228257
4. Markdown 本文 (.markdown-body)
229258
========================================================================== */

assets/css/portfolio.css

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,9 @@
168168
margin-top: auto; /* タグをカード下部に配置 */
169169
}
170170

171-
.portfolio-card .tag {
171+
/* 共通タグスタイルにする(一覧と作品詳細の両方で使う) */
172+
.tag {
173+
cursor: pointer;
172174
display: inline-flex;
173175
align-items: center;
174176
padding: 0.25rem 0.75rem;
@@ -179,8 +181,9 @@
179181
color: var(--color-text);
180182
white-space: nowrap;
181183
transition: var(--transition-smooth);
184+
text-decoration: none;
182185
}
183-
.portfolio-card .tag:hover {
186+
.tag:hover {
184187
background: var(--color-accent);
185188
color: #fff;
186189
box-shadow: var(--shadow-glow);
@@ -233,7 +236,14 @@
233236
border: 1px solid var(--color-border);
234237
box-shadow: var(--shadow-lg);
235238
}
236-
/* ... (work-detail以下のスタイルはblog.cssと共通なので省略) ... */
239+
240+
.work-detail__tags a.tag:focus {
241+
outline: none;
242+
box-shadow:
243+
0 0 0 3px rgba(0, 0, 0, 0.06),
244+
0 0 0 4px var(--color-accent-soft);
245+
border-color: var(--color-accent);
246+
}
237247

238248
/* ==========================================================================
239249
4. Markdown 本文 (.markdown-body) - blog.css と基本的に統一

assets/data/portfolioList.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"title": "クマの逆転プロ野球",
55
"date": "",
66
"category": "",
7-
"role": "Full Stack Enginner",
7+
"role": "Full Stack Engineer",
88
"description": "カジュアル",
99
"tech": "Unity / C#",
1010
"tags": [
@@ -21,7 +21,7 @@
2121
"title": "霊迷の湯",
2222
"date": "",
2323
"category": "",
24-
"role": "Full Stack Enginner",
24+
"role": "Full Stack Engineer",
2525
"description": "8番ライクホラー",
2626
"tech": "Unity / C#",
2727
"tags": [

assets/js/blog.js

Lines changed: 90 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,36 @@ document.addEventListener("DOMContentLoaded", () => {
1414
/** @type {Array<any>} */
1515
let allPosts = [];
1616

17+
// -----------------------------
18+
// URL パラメータ操作ヘルパ
19+
// -----------------------------
20+
function readInitialParams() {
21+
const params = new URLSearchParams(location.search);
22+
// 優先順: tag -> q
23+
const tag = params.get("tag") || "";
24+
const q = params.get("q") || "";
25+
const category = params.get("category") || "";
26+
27+
const initial = tag || q;
28+
if (searchInput && initial) {
29+
searchInput.value = initial;
30+
}
31+
if (categorySelect && category) {
32+
categorySelect.value = category;
33+
}
34+
}
35+
36+
// 現在の UI 状態を URL に反映(ページの再読み込みはしない)
37+
function updateUrlParams(keyword, category) {
38+
const params = new URLSearchParams();
39+
if (keyword) params.set("q", keyword);
40+
if (category) params.set("category", category);
41+
const newUrl =
42+
location.pathname +
43+
(params.toString() ? "?" + params.toString() : "");
44+
history.replaceState(null, "", newUrl);
45+
}
46+
1747
// -----------------------------
1848
// JSON 読み込み
1949
// -----------------------------
@@ -22,6 +52,8 @@ document.addEventListener("DOMContentLoaded", () => {
2252
const res = await fetch("assets/data/blogList.json");
2353
if (!res.ok) throw new Error(`HTTP ${res.status}`);
2454
allPosts = await res.json();
55+
// 初期パラメータを読み取ってフィルタをプリセット
56+
readInitialParams();
2557
render();
2658
} catch (err) {
2759
console.error("Failed to load blog list:", err);
@@ -52,7 +84,7 @@ document.addEventListener("DOMContentLoaded", () => {
5284
)
5385
return false;
5486

55-
// キーワードフィルタ
87+
// キーワードフィルタ (タイトル/説明/カテゴリ/タグ)
5688
if (keyword) {
5789
const tags = Array.isArray(post.tags)
5890
? post.tags
@@ -76,6 +108,9 @@ document.addEventListener("DOMContentLoaded", () => {
76108
return true;
77109
});
78110

111+
// フィルタ状態を URL に反映
112+
updateUrlParams(keyword, categoryFilter);
113+
79114
listEl.innerHTML = "";
80115

81116
if (!filtered.length) {
@@ -132,24 +167,68 @@ document.addEventListener("DOMContentLoaded", () => {
132167
}
133168

134169
// タグ
135-
if (post.tags && post.tags.length) {
170+
const tagsArr = Array.isArray(post.tags)
171+
? post.tags
172+
: String(post.tags || "")
173+
.split(",")
174+
.map((t) => t.trim())
175+
.filter(Boolean);
176+
177+
if (tagsArr.length) {
136178
const tagRow = document.createElement("div");
137179
tagRow.className = "card__tags";
138-
post.tags.forEach((t) => {
180+
tagsArr.forEach((t) => {
139181
const tag = document.createElement("span");
140182
tag.className = "tag";
141183
tag.textContent = t;
184+
185+
// タグはクリックでそのタグでフィルタ
186+
tag.addEventListener("click", (e) => {
187+
e.stopPropagation(); // カードクリックを阻止
188+
if (searchInput) searchInput.value = t;
189+
if (categorySelect) categorySelect.value = "";
190+
render();
191+
// URL に tag(または q)として反映(履歴は積まない)
192+
const params = new URLSearchParams();
193+
params.set("q", t);
194+
const newUrl =
195+
location.pathname + "?" + params.toString();
196+
history.replaceState(null, "", newUrl);
197+
});
198+
199+
// キーボード対応
200+
tag.tabIndex = 0;
201+
tag.addEventListener("keydown", (ev) => {
202+
if (ev.key === "Enter" || ev.key === " ") {
203+
ev.preventDefault();
204+
tag.click();
205+
}
206+
});
207+
142208
tagRow.appendChild(tag);
143209
});
144210
body.appendChild(tagRow);
145211
}
146212

147213
card.appendChild(body);
148214

149-
// カード全体クリックで記事ページへ
215+
// カード全体クリックで記事ページへ遷移
150216
card.addEventListener("click", () => {
151217
if (post.contentPath) {
152-
window.location.href = post.contentPath;
218+
// 現在のフィルタ状態をクエリに付与して遷移(戻ってきたときに復元しやすくする)
219+
const params = new URLSearchParams();
220+
const q = (searchInput?.value || "").trim();
221+
const category = (
222+
categorySelect?.value || ""
223+
).trim();
224+
if (q) params.set("q", q);
225+
if (category) params.set("category", category);
226+
const target =
227+
post.contentPath +
228+
(params.toString()
229+
? "?" + params.toString()
230+
: "");
231+
window.location.href = target;
153232
}
154233
});
155234

@@ -158,14 +237,18 @@ document.addEventListener("DOMContentLoaded", () => {
158237
}
159238

160239
// -----------------------------
161-
// イベント
240+
// イベント登録
162241
// -----------------------------
163242
if (searchInput) {
164-
searchInput.addEventListener("input", render);
243+
searchInput.addEventListener("input", () => {
244+
// タイプ中は即時フィルタ(必要に応じてデバウンスを追加してください)
245+
render();
246+
});
165247
}
166248
if (categorySelect) {
167249
categorySelect.addEventListener("change", render);
168250
}
169251

252+
// 初回ロード
170253
loadPosts();
171254
});

0 commit comments

Comments
 (0)