diff --git a/src/content/blog/2022-03-11-firstdiary.md b/src/content/blog/2022-03-11-firstdiary.md
new file mode 100644
index 0000000..353c60e
--- /dev/null
+++ b/src/content/blog/2022-03-11-firstdiary.md
@@ -0,0 +1,20 @@
+---
+title: 일상 기록 첫 번째 이야기
+date: 2022-03-11
+tags: 일상
+excerpt: 일상 기록 첫 번째 이야기
+---
+
+# 일상 기록 첫 번째 이야기
+
+~~깃~~ 블로그를 만들면서 앞으로 써 내려갈 일상 디렉토리의 첫 번째 포스트를 작성해 봅니다.
+
+어려서부터 내 생각을 정리하거나 메모를 할 때에는 간단히 들고 다니는 메모장이나 노트에 많이 끄적여 봤는데,
+
+이처럼 누구나 볼 수 있는 페이지에 나의 일상을 기록하고 내 생각을 정리하는 포스트를 만든다는게 너무 어색하기만 하다 :)
+
+평소에 글을 쓰는걸 별로 좋아하지 않았기 때문에 글을 작성하는데에도 많은 시간이 소요되고 내용도 알차지는 못하겠지만, 점점 나의 글을 쓰면서 어제보다 발전해가는 모습을 볼 수 있지 않을까?!
+
+때문에 일상 포스트를 꼭 해보고 싶었다.
+
+~~깃~~ 블로그 일상 포스트 시작합니다.
\ No newline at end of file
diff --git a/src/content/blog/2022-03-13-transferring-git-storage.md b/src/content/blog/2022-03-13-transferring-git-storage.md
new file mode 100644
index 0000000..1926e09
--- /dev/null
+++ b/src/content/blog/2022-03-13-transferring-git-storage.md
@@ -0,0 +1,20 @@
+---
+title: Transferring git storage
+date: 2022-03-13
+tags: 일상
+excerpt: 깃 저장소 변경하면서 잠깐 쉬는 타임에 끄적여보는 일상 기록
+---
+
+# 일상 기록 - 깃 저장소 변경
+
+기존 학부시절부터 사용했던 깃 저장소([git_storage_gahu](https://github.com/gahu))에서 했었던 프로젝트와 공부 했던 기록들을 정리해서
+새로운 깃 저장소([git_storage_gahusb](https://github.com/gahusb))로 변경하였다.
+
+기존의 깃이 공부하면서 썻던 내용들이라... 정리도 되어 있지 않았고 그래서 뭔가 지저분하게 되어 있는데 어디서부터 손을 대야 할지 감이 잡히지 않았다.
+
+때문에 새로운 [저장소](https://github.com/gahusb)를 만들고, github io를 이용하여 나의 [블로그](https://gahusb.github.io/)도 작성하면서
+기존 프로젝트들의 내용 정리와 블로그 정리 두가지를 같이 하면서 옮기게 되었다.
+
+블로그 작업 준비도 하면서 기존 프로젝트들을 옮기고 정리한다는게 쉬운 일은 아니지만
+지금 목표로 하고 있는 일이 있기 때문에 시작을 할 수 있었던것 같고,
+무엇보다도 새로 산 Macbook M1 Max를 잘 활용해 볼 수 있는 기회가 되지 않을까 생각하면서 작업하고 있다.
diff --git a/src/content/blog/2022-03-15-samgyub.md b/src/content/blog/2022-03-15-samgyub.md
new file mode 100644
index 0000000..e7bd647
--- /dev/null
+++ b/src/content/blog/2022-03-15-samgyub.md
@@ -0,0 +1,18 @@
+---
+title: 오랜만에 삼겹살집!
+date: 2022-03-15
+tags: 일상
+excerpt: 오랜만에 삼겹살집에 가서 먹부림한 하루 기록.
+---
+
+# 일상 기록 - 삼겹살 먹부림
+
+오늘 오랜만에 집 근처에 있는 삼겹살집에 갔다.
+퇴근 후 평소와는 다른 방향으로 걸어가면서 삼겹살이 먹고 싶다던 승토리와 함께 돼지고기 뿌시러 갔다.
+
+해바라기집이라는 이곳은 이 집에 온지 얼마 안 지나서 방문하고 이번이 두 번째 방문이다.
+
+
+돼지 반마리를 시켰는데, 위에 사진 처럼 두꺼운 삼겹살과 항정살, 갈비살, 목살 등이 500g 나오는데 우리 둘이서는 이정도가 딱 정당히 배부르고 좋은 것 같다.
+
+최근에 감기 기운이 있어서 목도 칼칼하니 별로 좋지 않았는데 기름진 고기를 먹으며 힐링했던 시간
diff --git a/src/content/blog/2022-04-02-warehouse43.md b/src/content/blog/2022-04-02-warehouse43.md
new file mode 100644
index 0000000..f6c27b9
--- /dev/null
+++ b/src/content/blog/2022-04-02-warehouse43.md
@@ -0,0 +1,49 @@
+---
+title: 소고기 먹부림
+date: 2022-04-02
+tags: 일상
+excerpt: 여의도 창고 43에서 오랜만에 소고기와 와인을 즐긴 기록.
+---
+
+# 일상 기록 - 소는 누가~~ 키우나?!
+
+오랜만에 승토리와 몸보신(?)을 하기 위해서 여의도에 있는 고깃집을 탐색했다.
+
+여의도에는 소고기 오마카세를 하는 집도 있었지만 오랜만에 소고기를 소금에 찍어서 먹어보고 싶다는 의견에 나도 적극적으로 동의했고, 오마카세가 아닌 구이집을 가기로 했다.
+
+약속시간, 7시 반 이라는 조금 늦은 저녁이 되어서 배고픔을 안고 여의도로 넘어왔고, 가깝지만 괜찮다고 생각되는 집으로 향했다.
+
+## 창고 43 본점
+
+여의도 맨하탄빌딩 2층에 있는 창고 43으로 갔다.
+
+
+
+입구에 가까히 갈수록 소고기 특유의 냄새가 풍겼고, 발걸음을 더 빨리해서 입성!
+
+예약을 하지 않았지만 다행히도 조금 늦은 시간에 가서 그런지 한 타임의 폭풍이 지나가고 난 뒤의 모습이었다.
+덕분에 바로 자리를 안내 받을 수 있었다.
+
+가장 괜찮아보이는 한우 세트를 주문하고 함께 어울릴만한 와인도 하나 먹고 싶었다!
+오늘은 FLEX 하는 날!!
+무난한 평의 칠레 산 와인을 시켜보았다.
+
+
+
+살짝 무게감이 있으면서도 향이 아주 좋아서 우리 둘다 만족할 수 있었다.
+
+그렇게 와인을 한 잔 따라놓고 가볍게 향을 즐기고 있을 때쯤!
+
+드디어!!! 소고기님 입장하셨다.
+
+
+
+사진으로 보기에는 양이 적어 보일 순 있지만
+맞다.😥 뭔가 평소 먹었던 양보다는 확실히 적긴 했지만 마블링이 살아 있었다...
+
+투쁠 안심, 새우살, 투쁠 등심 이렇게 세트로 하는데 600g이었나..?
+그래도 처음으로 소에서 가장 적게 나와서 귀하다는 새우살을 먹어 볼 수 있다는 생각에 들떠 있었고, 그 맛은 너무 훌륭했다.😋
+
+입에 넣자마자 사라지는 마법을 느끼며 정말 괜찮은 소고기를 먹어본 것 같다.
+
+고기와 와인을 먹고 냉면, 깍두기 볶음밥까지 완벽하게 마무리를 지으며 배 터지게 잘 먹고서 나왔다. :)
diff --git a/src/content/blog/2022-04-07-slicknlist.md b/src/content/blog/2022-04-07-slicknlist.md
new file mode 100644
index 0000000..f642047
--- /dev/null
+++ b/src/content/blog/2022-04-07-slicknlist.md
@@ -0,0 +1,263 @@
+---
+title: 블로그 기능 추가
+date: 2022-04-07
+tags: 개발
+excerpt: slick 슬라이더, 목차, 페이지 버튼, 유튜브 임베드 추가 정리.
+---
+
+# 게시글 테스트
+
+* toc
+{:toc .large-only}
+
+---
+
+## slick image
+[Slick](https://kenwheeler.github.io/slick/)
+slick 사이트에 들어가서 `get it now`를 눌러 다운로드 받아주어 slick 폴더를 `/assets/css/slick`에 복사>붙여넣기 해준다.
+
+
+게시글 원하는 위치에 아래와 같이 넣어주면 된다.
+
+```html
+
+
+
+
+
+
+```
+
+.slick() 안의 옵션을 원하는대로 설정하여 사용 할 수 있다.
+
+
+
+
+
+
+
+
+
+그리고 scss를 수정하여 좌우로 화살표를 넣어서 표출 하는것도 가능하도록 변경 할 수 있다.
+이미지는 별도로 구해서 사용하시면 됩니다.
+
+> assests/css/slick/slick-theme.css
+
+```css
+.slick-prev:before
+{
+ content: url(left.png);
+}
+[dir='rtl'] .slick-prev:before
+{
+ content: url(right.png);
+}
+
+.slick-next:before
+{
+ content: url(right.png);
+}
+[dir='rtl'] .slick-next:before
+{
+ content: url(left.png);
+}
+```
+
+---
+## 게시글 목차 만들기
+h1 타이틀 바로 아래에
+
+```
+* toc
+{:toc .large-only}
+```
+
+를 추가하여 헤더를 기준으로 목차 생성
+
+---
+## 페이지 버튼 만들기
+
+> _include/components/page-button.html
+
+```html
+
+```
+
+위 처럼 youtube 영상의 id를 가져와서 표출 할 수 있도록 만들고
+
+```console
+`{% include components/youtubePlayer.html id="{youtube ID}" %}`
+```
+
+게시글 원하는 위치에 youtube id를 넣어주면 된다.
+
+
+
+위와 같이 `watch?v=` 이후에 오는 id를 가져다가 사용한다.
+
+{% include components/youtubePlayer.html id='0TNFb5zgpbg' %}
+
+---
+
+## 게시물 조회수 보이기
+Hits 이용
+> [Hits](https://hits.seeyoufarm.com/)
+
+TARGET URL에 나의 블로그 주소를 입력하고, Options에서 여러 설정들을 조정하여 내가 원하는 스타일의 hits를 만들 수 있다. 미리보기를 통해 표시되는것을 볼 수 있으니 참고해서 만들면 된다.
+
+그렇게 생성된 아래의 HTML Link를 복사해서
+
+> _layouts/post.html
+
+에 넣어서 각 게시물 별로 조회수가 보이는 위치를 조정할 수 있다.
+
+> _includes/body/sidebar-sticky.html
+
+에 넣으면 사이드바는 어떤 게시물을 봐도 같이 보이는것이기 때문에 전체 조회수를 체크할 수 있다.
+
+만약 조회수 가장 오른쪽에 생성되는 링크모양 아이콘이 싫다면
+
+> _sass/my-style.scss
+
+```scss
+a.external::after, a::after {
+ display: none;
+}
+```
+
+를 추가하여 제거 할 수 있다.
+
+---
+
diff --git a/src/content/blog/2022-04-09-oh_codingtest.md b/src/content/blog/2022-04-09-oh_codingtest.md
new file mode 100644
index 0000000..72702e2
--- /dev/null
+++ b/src/content/blog/2022-04-09-oh_codingtest.md
@@ -0,0 +1,24 @@
+---
+title: 코딩테스트
+date: 2022-04-09
+tags: 공부
+excerpt: 오늘의집 코딩테스트를 치르며 느낀 점을 정리한 기록.
+---
+
+# 코딩 테스트 기록
+
+오늘의집 개발자 대규모 채용 코딩테스트
+
+오늘은 오늘의집이라는 유니콘 기업에 프론트엔드 개발자 포지션으로 코딩테스트를 했다.
+
+문제는 3문제 180분이었고, 나는 평소대로 JAVA로 치뤘다.
+
+3문제 모두 문자열과 관련된 알고리즘을 사용해 푸는 문제였고, 1번 문제는 단순히 문자열에서 다음에 나오는 문자를 보고 방향전환 및 직진 거리에 대한 명령을 알려주는 프로그램이라 단순 구현력을 묻는 문제였다.
+
+2번 문제는 반복 문자열을 제거하는 알고리즘 문제로, 이 문제와 유사한 문제를 이전에 풀었었는데.. 이게 참 내 생각처럼 쉽게 해결되지 않아서 이 문제에만 1시간 반 가까이 소비한 것 같다.
+
+3번 문제는 문자열에 특정 변수를 두고 그것을 치환하는 문제였는데, HashSet을 이용해서 반복 유무 검사와 무한 반복인지를 체크하도록 하여 해결하였다.
+
+지금까지 알고리즘을 이렇게 꾸준히 한 경험이 많지 않아서인지 생각보다 구현에는 어려움이 없었고, 평소에 얼마나 하느냐가 크게 다가왔었다. ~~(심지어 전날 새벽까지 놀고 집중해서 할 수 있었다는게 놀라울 정도)~~
+
+목표인 IT 대장급이라고 불리는 기업의 코테 올솔할 수 있는 실력을 갖출때까지 멈추지 않고 계속 할 것이다.
diff --git a/src/content/blog/2022-04-25-dairyhard.md b/src/content/blog/2022-04-25-dairyhard.md
new file mode 100644
index 0000000..2510dfb
--- /dev/null
+++ b/src/content/blog/2022-04-25-dairyhard.md
@@ -0,0 +1,16 @@
+---
+title: 일상기록 - 04월25일
+date: 2022-04-25
+tags: 일상
+excerpt: 바쁜 일정 속에서 다시 루틴을 잡아가려는 마음 기록.
+---
+
+# 일상의 기록 - 04월25일
+
+최근에는 업무도 바빠서 늦게까지 야근하고, 그러지 않은 날에는 미뤄진 약속을 가느라고 목표로 했던 1일 1commit 100일 목표에서 많이 떨어지게 된 것 같다.
+
+물론 채우기 위해서 중간중간 알고리즘 한 문제라도 풀어서 올리고는 있지만 예전만큼 활발하지 못한것 같아서 거의 반성의 기록을 남긴다.
+
+처음 1일 1commit을 목표로 했을 때에는 공부도 흥미롭고, 일도 여유가 있었기 때문에 공부에 투자하는 시간도 많았는데 그러지 못하는게 너무 불편한 상황이 되었다.
+
+이번주만 지나면 그래도 여유를 찾을 수 있을것 같으니 좀 더 공부하고 목표를 채워가는 일상을 기록 할 수 있도록 노력할 것이다.
diff --git a/src/content/blog/2022-06-09-publicdatacontest.md b/src/content/blog/2022-06-09-publicdatacontest.md
new file mode 100644
index 0000000..fb2d225
--- /dev/null
+++ b/src/content/blog/2022-06-09-publicdatacontest.md
@@ -0,0 +1,19 @@
+---
+title: 식의약 공공데이터 활용 공모전 준비
+date: 2022-06-09
+tags: 개발, 아이디어
+excerpt: 공공데이터 공모전 준비 과정과 아이디어 선정 기록.
+---
+
+# 식의약 공공데이터 활용 공모전 준비
+
+이번에 같은 팀의 동료와 동기 3명이서 식의약 공공데이터 활용을 해서 웹/앱 개발을 하는 공모전에 참가하려고 한다.
+그래서 사전 Notion을 이용하여 일정 관리 및 아이디어 모집을 했고, 오늘에서 모여 최종 아이디어 회의를 시작했다.
+
+다양한 아이디어들 중에서 3가지 아이디어가 가장 괜찮을것 같았고, 그 중 내가 제시한 아이디어가 가장 참신성에서 괜찮다고 투표 결과가 나와서 채택하게 되었다.
+아이디어가 정해지고 나서도 활용할 공공데이터의 데이터 내용 및 배경, 기대효과, Mock-up design 등 해야 할 것들이 많이 있어서 정리하는데만 시간이 더 걸리기도 했다.
+
+퇴근 후에 다들 힘든 상태에서 진행하기도 했고, 이번 주.. 너무 바빴어서 잠도 제대로 잔 적이 없긴 하지만 오랜만에 다시 두근거리는 프로젝트를 진행하는것 같아서 기대가 된다.
+(개발하느라 고생할거 생각하면 아찔하긴 하다..)
+
+8월 초까지 개발이 어느정도 완료가 되어야 하니 스케줄 정해진대로 완료해서 좋은 성과를 냈으면 한다.
diff --git a/src/content/blog/2022-07-06-publicdatacontest.md b/src/content/blog/2022-07-06-publicdatacontest.md
new file mode 100644
index 0000000..dded096
--- /dev/null
+++ b/src/content/blog/2022-07-06-publicdatacontest.md
@@ -0,0 +1,17 @@
+---
+title: 식의약 공공데이터 활용 공모전 1차 평가 통과
+date: 2022-07-06
+tags: 개발, 아이디어
+excerpt: 공모전 1차 통과 후 일정과 해야 할 일 정리.
+---
+
+# 식의약 공공데이터 활용 공모전 1차 평가 통과
+
+'그린라이트'라는 장기 기증 활성화를 주제로 하는 캠페인의 이름을 따서 식의약 공공데이터 활용 공모전에 지난 6월에 참가를 했었다.
+아이디어만큼은 자신이 있었으나 마이너한듯한 분야라서 걱정을 하고 있었는데, 오늘 1차 평가 통과 메세지가 왔다!!
+
+67개의 팀이 참가해서 아이디어부문 8개 팀, 개발부문 4개 팀이 선정 되었는데 그 중 개발부문 한 팀으로 선정이 된 것이다!!!
+기쁨도 잠시... 2차 평가의 날이 보름정도 남은 상태라 발표자료와 앱 개발을 해야하는 상황이 온 것이다.
+공공데이터도 사용 신청도 미흡한 상태라 걱정이 되긴 하지만 팀원들과 모여서 합의하고 일정 및 업무 분담을 하기로 했다.
+
+이번에도 좋은 결과를 거둘 수 있는 공모전이 되었으면 좋겠다고 생각하고 있다.
diff --git a/src/content/blog/2023-04-29-daily.md b/src/content/blog/2023-04-29-daily.md
new file mode 100644
index 0000000..e800e6b
--- /dev/null
+++ b/src/content/blog/2023-04-29-daily.md
@@ -0,0 +1,31 @@
+---
+title: daily diary
+date: 2023-04-29
+tags: 일상
+excerpt: 오랜만에 기록을 다시 시작하며 하루를 정리한 글.
+---
+
+# 일상의 기록
+
+오랫만에 일상의 기록
+23년에는 한 번도 기록을 하지 않았더라구..
+물론 23년에는 승토리와 처음으로 해외 여행도 다녀오고 연휴도 있었고 이직을 하기 때문에 회사일에 집중하기도 했다.
+그래도 너무 오랜만에 MacBook Pro를 켜면서 켜져 있는 VScode 창을 보면서 끝내지 못한 포스팅도 있었고, 기록이 22년 12월을 마지막으로 멈춰 있는 상태를 발견하고나니 반성하자 나 자신..
+
+오늘은 승토리와 함께 아침 일찍부터 낙성대역 근처 카페에 자리잡고 각자 해야 할 일을 하고서,
+저녁에는 이사가야 할 집 구경도 하고 가구의 치수도 재는 등 하려고 나왔다.
+물론 내 생각보다는 너무 늦게 나온편이긴 하지만 이 정도면 선방했지!
+
+5월 19일에 이사도 가고 새로운 직장에 조금씩 익숙해지면 다시 본격적으로 꾸준히 업데이트하며 공부할 계획을 다시 세워보겠다!
+서버 개설 및 유지, 관리와 같은 부분에서 많이 부족하다는 것을 느끼고 있고, 확실히 그동안 해왔던 임베디드와는 전혀 다른 성격을 지나고 있기 때문에 서버 모니터링 & VOC와 같은 내용들을 따라가는게 어렵긴 하다.
+그와 관련해서 내 NAS 서버를 관리하면서 그런 부족한 부분을 채워가보면 어떨까 지금은 생각하고 있다.
+
+우선은 이사에 집중하고 워크스페이스 관리를 좀 더 신경써서 주기적으로 내 스스로의 가치를 올릴 수 있도록 해보겠다.
+
+그리고 요새는 부업과 관련된 내용을 나름대로 정리하고 있다.
+세상에는 내가 모르던 분야에서 다양한 부업으로 제 2의 수익을 내는 방법이 많은것을 알고서 깜짝 놀랐고,
+그런 방법을 습득하고 소소하게라도 제 2의 수입을 안정적으로 만들 수 있는 노력을 할 예정이다.
+
+또한 승토리와 6월에 결혼을 준비하는 단계에 도입하기로 했다.
+이것은 엄청나게 의미 있는 것이며, 인생에서 전환기가 될 수 있다고 생각한다.
+지금 이사가는 집에서 같이 시작하기에는 내가 생각했던 부분보다는 소소할 수 있겠지만, 차근차근 준비해보려고 한다. 💪
diff --git a/src/content/blog/2026-01-lotto-lab.md b/src/content/blog/2026-01-lotto-lab.md
index ef7af23..0324ed8 100644
--- a/src/content/blog/2026-01-lotto-lab.md
+++ b/src/content/blog/2026-01-lotto-lab.md
@@ -1,7 +1,7 @@
---
title: 로또 실험실을 조금 더 재미있게
date: 2026-01-12
-tags: product, lotto
+tags: 개발, product, lotto
excerpt: 작은 실험으로 시작한 로또 페이지를 앞으로 어떻게 발전시키려는지 정리했습니다.
---
diff --git a/src/content/blog/2026-01-welcome.md b/src/content/blog/2026-01-welcome.md
index c996528..ddf65df 100644
--- a/src/content/blog/2026-01-welcome.md
+++ b/src/content/blog/2026-01-welcome.md
@@ -1,7 +1,7 @@
---
title: 새 블로그를 열었습니다
date: 2026-01-18
-tags: intro, blog
+tags: 일상, intro, blog
excerpt: 이제부터 개발 기록과 여행 기록을 이곳에 차곡차곡 쌓아갑니다.
---
diff --git a/src/pages/blog/Blog.css b/src/pages/blog/Blog.css
index 1637ca9..61e6f93 100644
--- a/src/pages/blog/Blog.css
+++ b/src/pages/blog/Blog.css
@@ -48,7 +48,7 @@
.blog-grid {
display: grid;
- grid-template-columns: minmax(0, 0.45fr) minmax(0, 0.55fr);
+ grid-template-columns: minmax(0, 0.3fr) minmax(0, 0.7fr);
gap: 22px;
align-items: start;
}
@@ -58,6 +58,27 @@
gap: 12px;
}
+.blog-category-filter {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+}
+
+.blog-category-chip {
+ border: 1px solid var(--line);
+ background: rgba(255, 255, 255, 0.04);
+ color: var(--text);
+ border-radius: 999px;
+ padding: 6px 12px;
+ font-size: 12px;
+ cursor: pointer;
+}
+
+.blog-category-chip.is-active {
+ border-color: rgba(247, 168, 165, 0.6);
+ background: rgba(247, 168, 165, 0.2);
+}
+
.blog-list__item {
border: 1px solid var(--line);
background: var(--surface);
@@ -79,6 +100,34 @@
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
}
+.blog-pagination {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 10px;
+ padding-top: 10px;
+}
+
+.blog-page-btn {
+ border: 1px solid var(--line);
+ background: rgba(255, 255, 255, 0.05);
+ color: var(--text);
+ border-radius: 999px;
+ padding: 6px 12px;
+ font-size: 12px;
+ cursor: pointer;
+}
+
+.blog-page-btn:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+
+.blog-page-indicator {
+ color: var(--muted);
+ font-size: 12px;
+}
+
.blog-list__title {
margin: 0;
font-weight: 600;
@@ -170,6 +219,31 @@
font-size: 0.85em;
}
+.blog-article__body a {
+ color: #f7d4c9;
+}
+
+.md-image {
+ width: 100%;
+ border-radius: 14px;
+ border: 1px solid rgba(255, 255, 255, 0.12);
+ margin: 12px 0;
+}
+
+.md-quote {
+ margin: 0 0 14px;
+ padding: 12px 16px;
+ border-left: 3px solid rgba(247, 168, 165, 0.6);
+ background: rgba(255, 255, 255, 0.03);
+ color: var(--muted);
+}
+
+.md-hr {
+ border: none;
+ border-top: 1px solid rgba(255, 255, 255, 0.14);
+ margin: 18px 0;
+}
+
.md-code {
padding: 14px;
border-radius: 12px;
@@ -183,6 +257,68 @@
color: var(--muted);
}
+.blog-categories {
+ display: grid;
+ gap: 18px;
+}
+
+.blog-categories__head h2 {
+ margin: 0 0 6px;
+ font-size: 24px;
+ font-family: var(--font-display);
+}
+
+.blog-categories__head p {
+ margin: 0;
+ color: var(--muted);
+}
+
+.blog-categories__grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
+ gap: 14px;
+}
+
+.blog-category-card {
+ border: 1px solid var(--line);
+ background: var(--surface);
+ border-radius: 18px;
+ padding: 16px;
+ text-align: left;
+ color: inherit;
+ cursor: pointer;
+ display: grid;
+ gap: 12px;
+ transition: border-color 0.2s ease;
+}
+
+.blog-category-card:hover {
+ border-color: rgba(255, 255, 255, 0.25);
+}
+
+.blog-category-card__head {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ font-weight: 600;
+}
+
+.blog-category-card__count {
+ font-size: 12px;
+ color: var(--muted);
+}
+
+.blog-category-card__list {
+ display: grid;
+ gap: 6px;
+ color: var(--muted);
+ font-size: 13px;
+}
+
+.blog-category-card__empty {
+ color: var(--muted);
+}
+
@media (max-width: 900px) {
.blog-header,
.blog-grid {
diff --git a/src/pages/blog/Blog.jsx b/src/pages/blog/Blog.jsx
index 3e11a6d..7651ce8 100644
--- a/src/pages/blog/Blog.jsx
+++ b/src/pages/blog/Blog.jsx
@@ -1,24 +1,64 @@
-import React, { useMemo, useState } from 'react';
+import React, { useEffect, useMemo, useState } from 'react';
import { getBlogPosts } from '../../data/blog';
import './Blog.css';
const renderInline = (text) => {
- const pattern = /(\*\*[^*]+\*\*|\*[^*]+\*|`[^`]+`)/g;
- const parts = text.split(pattern).filter(Boolean);
+ const normalized = text.replace(/ /gi, '\n');
+ const pattern =
+ /(!\[[^\]]*\]\([^)]+\)|\[[^\]]+\]\([^)]+\)|\*\*[^*]+\*\*|\*[^*]+\*|`[^`]+`)/g;
+ const segments = normalized.split('\n');
- return parts.map((part, index) => {
- if (part.startsWith('**')) {
- return (
- {part.replace(/\*\*/g, '')}
- );
+ return segments.flatMap((segment, segmentIndex) => {
+ const parts = segment.split(pattern).filter(Boolean);
+ const rendered = parts.map((part, index) => {
+ if (part.startsWith('![')) {
+ const match = part.match(/!\[([^\]]*)\]\(([^)]+)\)/);
+ if (!match) return {part};
+ const [, alt, src] = match;
+ return (
+
+ );
+ }
+ if (part.startsWith('[')) {
+ const match = part.match(/\[([^\]]+)\]\(([^)]+)\)/);
+ if (!match) return {part};
+ const [, label, href] = match;
+ return (
+
+ {label}
+
+ );
+ }
+ if (part.startsWith('**')) {
+ return (
+ {part.replace(/\*\*/g, '')}
+ );
+ }
+ if (part.startsWith('*')) {
+ return {part.replace(/\*/g, '')};
+ }
+ if (part.startsWith('`')) {
+ return {part.replace(/`/g, '')};
+ }
+ return {part};
+ });
+
+ if (segmentIndex < segments.length - 1) {
+ rendered.push( );
}
- if (part.startsWith('*')) {
- return {part.replace(/\*/g, '')};
- }
- if (part.startsWith('`')) {
- return {part.replace(/`/g, '')};
- }
- return {part};
+
+ return rendered;
});
};
@@ -60,6 +100,18 @@ const renderMarkdown = (body) => {
return;
}
+ if (/^---$/.test(line.trim())) {
+ flushList();
+ blocks.push({ type: 'hr' });
+ return;
+ }
+
+ if (/^>\s+/.test(line)) {
+ flushList();
+ blocks.push({ type: 'quote', value: line.replace(/^>\s+/, '') });
+ return;
+ }
+
if (/^[-*]\s+/.test(line)) {
list.push(line.replace(/^[-*]\s+/, ''));
return;
@@ -108,6 +160,13 @@ const renderMarkdown = (body) => {
{block.value}
);
+ if (block.type === 'quote')
+ return (
+