hexo+icarus 다크모드 깜빡임 해결

예전에 메모해놓고 이제 게시글로 적는다!

기본 배경 색 없애기

먼저 css레벨에서 적용되는 css를 초기화해야한다.
브라우저는 기본적으로 css가 없을 시 페이지 이동할 때 이전 페이지의 배경색을 그대로 유지한다.

\theme\icarus\css\include\style\base.styl
7줄 기본 배경색 none으로 수정하기

1
$body-background-color ?= none;

기존 js파일을 두 개로 나누기

로컬스토리지를 읽고 다크모드를 적용하는 함수와 html을 전부 로드한 후 버튼의 innerText를 수정하는 함수로 나누었다.

다크모드를 적용하는 함수는 render blocking이 이루어져야하고 버튼의 innerText의 수정은 HTML이 모두 로드 후 이루어져야하기 때문이다.

따라서 버튼을 수정하는 함수는 defer로 불러오고 다크모드를 확인하고 적용하는 함수는 속성 없는 script태그로 불러온다.

\themes\icarus\source\js\darkmode.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function init() {
const darkScheme = window.matchMedia("(prefers-color-scheme:dark)");
const theme = localStorage.getItem("theme");
if (theme === "dark") {
document.body.classList.add("dark");
return;
}
if (theme === "light") {
return;
}

if (darkScheme) document.body.classList.add("dark");

localStorage.setItem("theme", darkScheme ? "dark" : "light");
}

hexo\themes\icarus\source\js\darkmodebutton.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
const darkModeButton = document.querySelector("#changeModeButton");
const darkScheme = window.matchMedia("(prefers-color-scheme:dark)");

darkScheme.addEventListener("change", (e) => {
console.log("hidark");
const newColorScheme = e.matches ? "dark" : "light";
localStorage.setItem("theme", newColorScheme);

if (newColorScheme === "dark") {
document.body.classList.add("dark");
btn.innerText = "🌙";
return;
}
document.body.classList.remove("dark");
btn.innerText = "🌞";
});

function toggleTheme() {
console.log("hibutton");
document.body.classList.toggle("dark");
darkModeButton.innerText = document.body.classList.contains("dark")
? "🌙"
: "🌞";
localStorage.setItem(
"theme",
document.body.classList.contains("dark") ? "dark" : "light"
);
}

function initButton() {
console.log("initButton");
const data = localStorage.getItem("theme");
if (data == "dark") {
darkModeButton.innerText = "🌙";
return;
}
darkModeButton.innerText = "🌞";
}

initButton();
darkModeButton.addEventListener("click", toggleTheme);

\themes\icarus\layout\common\scripts.jsx
56번쨰 줄에 darkmodebutton.js추가

1
2
<script src={url_for("/js/main.js")} defer></script>
<script src={url_for("/js/darkmodebutton.js")} defer></script>

hexo\themes\icarus\layout\common\head.jsx

js를 로드하고 실행하는 동안 렌더링을 막아야하기 때문에 script에 속성을 지정하지 않아야한다.
적다보니 preload속성을 추가해도 좋을 것 같다.

1
<script src={url_for("/js/darkmode.js")}></script>

고의로 render blocking 만들기

새로고침이 되어서 다크모드가 적용되기 전 까지 렌더링이 이루어지면 안된다.

body의 최상단에 darkmode.js에서 정의한 init함수를 호출해준다.
hexo\themes\icarus\layout\layout.jsx

1
2
3
4
<html lang={language ? language.substr(0, 2) : ""}>
<Head site={site} config={config} helper={helper} page={page} />
<body class={`is-${columnCount}-column`}>
<script>init();</script>

ref

https://www.youtube.com/watch?v=ElsZ-v4Ow08

hexo + github 블로그 다크모드 깜빡임 해결

렌더링이 시작된 후에 defer속성으로 받아온 스크립트가 실행되다보니 화면 전환중에 흰화면으로 잠시 전환되는 현상이 발생하였다.

다음과 같은 방법으로 수정하였다.

body의 기본 배경 색 없애기

먼저 7번째 줄에 있는 body의 background-color를 none으로 수정하였다.

\theme\icarus\css\include\style\base.styl

1
$body-background-color ?= none;

기존 js파일을 두 개로 나누기

기존 js파일의 경우 init 함수에서 버튼 설정과 다크모드 로드를 동시에 해주었는데,
다크모드 로드는 렌더링 전에 실행되어야 하고 버튼 설정은 렌더링 후에(최소 DOM트리 생성 이후) 실행되어야 한다.
때문에 파일을 두 개로 나누었다.

darkmode.js엔 chinsun9님의 게시글을 읽고 matchMedia를 읽어서 실행하는 부분을 추가하였다.
(너무 친절하게 답변해주셨다..!)

localstorage와 matchMedia를 이용하여 다크모드를 로드하는 함수이다.
\themes\icarus\source\js\darkmode.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function init() {
const darkScheme = window.matchMedia("(prefers-color-scheme:dark)");
const theme = localStorage.getItem("theme");
if (theme === "dark") {
document.body.classList.add("dark");
return;
}
if (theme === "light") {
return;
}

if (darkScheme) document.body.classList.add("dark");

localStorage.setItem("theme", darkScheme ? "dark" : "light");
}

버튼 관련 설정을 하는 js 파일이다.
hexo\themes\icarus\source\js\darkmodebutton.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
const darkModeButton = document.querySelector("#changeModeButton");
const darkScheme = window.matchMedia("(prefers-color-scheme:dark)");

darkScheme.addEventListener("change", (e) => {
console.log("hidark");
const newColorScheme = e.matches ? "dark" : "light";
localStorage.setItem("theme", newColorScheme);

if (newColorScheme === "dark") {
document.body.classList.add("dark");
btn.innerText = "🌙";
return;
}
document.body.classList.remove("dark");
btn.innerText = "🌞";
});

function toggleTheme() {
console.log("hibutton");
document.body.classList.toggle("dark");
darkModeButton.innerText = document.body.classList.contains("dark")
? "🌙"
: "🌞";
localStorage.setItem(
"theme",
document.body.classList.contains("dark") ? "dark" : "light"
);
}

function initButton() {
console.log("initButton");
const data = localStorage.getItem("theme");
if (data == "dark") {
darkModeButton.innerText = "🌙";
return;
}
darkModeButton.innerText = "🌞";
}

initButton();
darkModeButton.addEventListener("click", toggleTheme);

작성한 js파일의 위치 지정해주기

  1. darkmodebutton.js
    버튼은 렌더링후에(최소 DOM트리 생성 이후) 실행되어야 하므로 아래부분에 추가해주었다.
    \themes\icarus\layout\common\scripts.jsx
    56번쨰 줄에 darkmodebutton.js추가

    1
    2
    <script src={url_for("/js/main.js")} defer></script>
    <script src={url_for("/js/darkmodebutton.js")} defer></script>
  2. darkmode.js
    body 최상단에서 실행하기위해 head태그에서 js파일을 로드했다.
    적다보니 preload속성을 추가해도 좋을 것 같다.

    hexo\themes\icarus\layout\common\head.jsx

    1
    <script src={url_for("/js/darkmode.js")}></script>
  3. darkmode.js의 init 함수
    다크모드를 로드하는 건 렌더링 이전에 실행되어야한다.
    때문에 body태그 최상단에 rendering block 요소로 defer나 async 속성을 지정하지 않고 넣어주었다.
    hexo\themes\icarus\layout\layout.jsx

    1
    2
    3
    4
    <html lang={language ? language.substr(0, 2) : ""}>
    <Head site={site} config={config} helper={helper} page={page} />
    <body class={`is-${columnCount}-column`}>
    <script>init();</script>

ref

[A1] 웹뷰에서 다크모드 상속받기: 일관성있는 사용자 경험을 위하여
blog fix 다크 모드 화면 깜박임 제거

hexo + blog runMicroTasks 에러 해결하기

hexo generate중에 에러가 발생했다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
FATAL {
err: Error: ** Processing: C:\Users\SDPark-pc\AppData\Local\Temp\f657ac55-7bfc-474b-8bd4-783a7a740fb7
1024x1024 pixels, 4x8 bits/pixel, RGB+alpha
Input IDAT size = 16536 bytes
Input file size = 16593 bytes

Trying:
zc = 9 zm = 8 zs = 0 f = 0 IDAT size = 16536
zc = 9 zm = 8 zs = 3 f = 0 0
at C:\Users\SDPark-pc\Desktop\REACT\blog\hexo\node_modules\exec-buffer\node_modules\execa\index.js:231:11
at runMicrotasks (<anonymous>)
at processTicksAndRejections (internal/process/task_queues.js:93:5) {
code: 3221225477,
killed: false,
stdout: '',
stderr: '** Processing: C:\\Users\\SDPark-pc\\AppData\\Local\\Temp\\f657ac55-7bfc-474b-8bd4-783a7a740fb7\r\n' +
'1024x1024 pixels, 4x8 bits/pixel, RGB+alpha\r\n' +
'Input IDAT size = 16536 bytes\r\n' +
'Input file size = 16593 bytes\r\n' +
'\r\n' +
'Trying:\r\n' +
' zc = 9 zm = 8 zs = 0 f = 0\t\tIDAT size = 16536\r\n' +
' zc = 9 zm = 8 zs = 1 f = 0\r zc = 1 zm = 8 zs = 2 f = 0\r zc = 9 zm = 8 zs = 3 f = 0',
failed: true,
signal: null,
cmd: 'C:\\Users\\SDPark-pc\\Desktop\\REACT\\blog\\hexo\\node_modules\\hexo-filter-cleanup\\node_modules\\optipng-bin\\vendor\\optipng.exe -strip all -clobber -fix -o 2 -out C:\\Users\\SDPark-pc\\AppData\\Local\\Temp\\044b15b2-8a65-4748-97c3-ad4280ef2c14 C:\\Users\\SDPark-pc\\AppData\\Local\\Temp\\f657ac55-7bfc-474b-8bd4-783a7a740fb7',
timedOut: false
}
} Something's wrong. Maybe you can find the solution here: %s https://hexo.io/docs/troubleshooting.html

hfc에서 발생한 에런지 hexo에서 발생한 에러인지 모르지만 해결하였다.

다음과 같이 execa라는 모듈을 재설치 해주었다.

1
2
npm rm execa
npm i execa

hexo +github 블로그 Spawn failed 에러 해결하기

블로그 포스팅을 하던 중에 다음과 같은 에러가 발생하였다.

1
2
3
4
5
Error: Spawn failed
at ChildProcess. (D:\blog\node_modules\hexo-util\lib\spawn.js:51:21)
at ChildProcess.emit (events.js:311:20)
at ChildProcess.cp.emit (D:\blog\node_modules\cross-spawn\lib\enoent.js:34:29)
at Process.ChildProcess.\_handle.onexit (internal/child_process.js:275:12)

deploy 자체가 안 되어서 헤메던 중에 해결방안을 발견하였다.

.deploy*git에서 index.lock 파일을 삭제하니까 해결되었다.
directory

ref

https://blog.zhheo.com/p/128998ac.html

hexo + icarus 블로그 lighthouse 성능지표 개선하기

블로그의 성능면에서 개선할 것이 없나 고민하던 중 크롬에서 제공하는 성능지표인 lightouse 분석을 하고 개선하기로 하였다.

lighthouse

내 블로그의 lighthouse 점수이다.
퍼포먼스쪽이 처참하다.
어차피 다 수정해야겠지만 빨간 권장사항들을 먼저 수정하기로 하였다.
lighthouse 점수

적절한 이미지 size 사용하기

첫 번째 권장사항이다. 적절한 크기의 이미지를 사용하라는 지침이다.
나는 블로그에 사진을 업로드 할 때 따로 이미지의 크기를 지정해주지 않아 리페인트 작업에 많은 리소스를 사용한 것 같다.
또한 png파일들을 주로 사용하다보니 이미지의 크기가 큰 것도 영향이 있었다.

Serve images that are appropriately-sized to save cellular data and improve load time.
lighthouse 이미지 개선

정적 이미지 사용하기

현재 나는 깃허브 issue를 이용해 이미지를 관리하고 있다.
또한 이미지 형식이 png라 많은 리소스를 사용한다.
이를 수정해주었다.

source에 postimage라는 폴더를 새로 만들어주었다.

폴더구조

정적파일을 생성할 떄에 source안의 폴더들은 public안에도 생성된다.

정적파일 폴더구조

이 작업들을 하나씩 다 해야한다..

이미지의 크기 명시하기

이미지는 파일 자체의 크기도 크고 이미지 사이즈를 명시하지 않으면 리페인트 작업에 리소스를 사용하게 된다.
이미지 태그를 사용하여 사이즈를 명시하고 이미지를 불러주었다.

수정 전

1
![lighthouse 개선](https://user-images.githubusercontent.com/79688915/144422469-6fc049ec-191b-4ca1-ae0e-d667723971d2.png)

수정 후

1
<img src="/postimage/hexo_icarus_블로그_성능지표_개선하기/성능.png" alt="lighthouse 개선" width="650" height="300px">

해결 방안

lighthouse 개선

hexo SEO 적용하기

지난 글들에 이어 블로그에 SEO를 적용해보았다.

크롤링 개선

  • description

    게시글마다 description을 추가해주었다.
    1
    2
    3
    4
    5
    6
    7
    8
    ---
    title: SEO 알아보기(2)
    date: 2021-11-30 16:15:36
    tags: [seo]
    description: GOOGLE 검색 센터를 통해 검색엔진 최적화(SEO)에 대해 더 자세하게 알아보자.
    category: [seo]
    toc: true
    ---

호환성 검사

블로그의 HTML을 검사하였다.

HTMl 검사

<meta>태그가 있다.
HTMl 검사

./theme/icarus/layout/common/head.jsx

1
2
3
4
5
{
meta && meta.length ? <MetaTags meta={meta} /> : null;
}

<title>{getPageTitle(page, config.title, helper)}</title>;

다만 이 부분은 hexo 내부 로직이라 혹시나 발생할 사이드이펙트 때문에 수정하지는 않았다.

검색엔진에 등록하기

검색엔진에 내 사이트 정보를 등록하였다. 안 하여도 되지만 조금 더 빨리 크롤링 된다고 한다.
등록 뒤에도 며칠 기다려야 한다..! 사이트마다 어떻게 등록했는지는 사진을 찍어두지 못하였다.

  • 네이버 웹마스터 도구 에서 사이트를 등록하였다. 등록 후엔 간단 체크로 체크해볼 수 있다. 사이트맵과 rss제출도 할 수 있다.

    ./theme/icarus/layout/common/head.jsx 소유권을 인증하는 <meta>태그를 삽입하였다.

    1
    <meta name="naver-site-verification" content="...">
  • Google

    구글 서치 콘솔에도 등록하였고, 사이트맵과 rss제출도 할 수 있다.

    ./theme/icarus/layout/common/head.jsx 소유권을 인증하는 <meta>태그를 삽입하였다.

    1
    <meta name="google-site-verification" content="...">
  • daum

    다음 검색 등록은 rss제출과 사이트맵 제출을 요구하지 않았다.

플러그인

hexo에선 SEO적용을 돕는 여러 플러그인을 지원한다.
나는 아래와 같은 플러그인을 적용하였다.

  • hexo-autonofollow

    hexo-autonofollow는 블로그 내의 <a>태그마다 자동으로 nofollow 속성을 추가해주는 플러그인이다.
    사이트와 연관되지 않은 url들의 크롤링을 막는 기능을 한다.

    다음과 같이 설치할 수 있다.

    1
    npm install hexo-autonofollow --save

    _config.yml 파일에 다음과 같이 적용한다.

    1
    2
    3
    4
    5
    nofollow:
    enable: true
    exclude: //제외할 호스트
    - exclude1.com
    - exclude2.com
  • hexo-generator-seo-friendly-sitemap

    hexo-generator-seo-friendly-sitemap은 자동으로 sitemap을 생성해주는 플러그인이다.

    다음과 같이 설치할 수 있다.

    1
    npm install hexo-autonofollow --save

    _config.yml 파일에 다음과 같이 적용한다.

    1
    2
    3
    4
    sitemap:
    path: sitemap.xml //sitemap의 경로
    tag: false
    category: false
  • hexo-generator-seo-friendly-sitemap

    hexo-generator-seo-friendly-sitemap은 자동으로 sitemap을 생성해주는 플러그인이다.

    다음과 같이 설치할 수 있다.

    1
    npm install hexo-autonofollow --save

    _config.yml 파일에 다음과 같이 적용한다.

    1
    2
    3
    4
    sitemap:
    path: sitemap.xml //sitemap의 경로
    tag: false
    category: false
  • hexo-generator-feed

    hexo-generator-feed은 자동으로 RSS 피드를 생성해주는 플러그인이다.

    다음과 같이 설치할 수 있다.

    1
    npm install hexo-generator-feed --save

    _config.yml 파일에 다음과 같이 적용한다.

    1
    2
    3
    4
    feed:
    type: rss2 //rss
    path: rss2.xml //경로
    limit: 20 //url수
  • hexo-generator-robotstxt

    hexo-generator-robotstxt은 자동으로 robots.txt를 생성해주는 플러그인이다.

    다음과 같이 설치할 수 있다.

    1
    npm install hexo-generator-robotstxt --save

    _config.yml 파일에 다음과 같이 적용한다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    feed:
    robotstxt:
    useragent: "*"
    disallow:
    - /one_file_to_disallow.html
    - /2nd_file_to_disallow.html
    - /3rd_file_to_disallow.html
    allow:
    - /one_file_to_allow.html
    - /2nd_file_to_allow.html
    - /3rd_file_to_allow.html
    sitemap: /sitemap.xml

ref

hexoplugin

마무리

SEO작업을 마무리 하였고, 결과 반영까지 기다리는 중이다..!
최소 5일정도 걸린다고 하니 그 떄까지 기다려보고 문제가 있으면 다시 수정해보아야겠다.

카탈로그 수정

카탈로그의 위치를 수정하였다.

카탈로그는 아래 사진의 오른쪽에 저렇게 목차가 나오고 클릭할 경우 해당 부분으로 이동하는 위젯이다.

image

처음엔 카탈로그가 왼 쪽에 있고 고정되지 않아, 글의 목차와 내용을 간략하게 보려고 할 경우 카탈로그가 존재하는 위치까지 이동해주어야 했다.

카탈로그를 오른쪽에 배치하고 고정되도록 만들기로 하였다.

코드 살펴보기

개발자 도구를 열어 카탈로그에 position: fixed와 position:sticky를 추가해주었다.

왼쪽에 있는 다른 위젯들과 겹쳐 가독성이 상당히 안 좋았다.

레이아웃의 html을 살펴보았다.

image

게시글이 작성된 부분의 css이다.

1
"column order-2 column-main is-9-tablet is-9-desktop is-9-widescreen"

왼 쪽부터 클래스명의 order-1, order-2 등으로 레이아웃을 정하는 듯 하였다.

또한 is-9-tablet 과 같은 클래스는 기기마다 차지할 크기를 알려주는 클래스명이다.

개발자도구에서 클래스명이 column 상위 태그에 다음과 같은 HTML을 추가해 보았다.

1
<div class="column order-3 column-main is-9-tablet is-9-desktop is-9-widescreen">hihi</div>

성공적으로 추가되었다.
image

레이아웃 수정하기

성공한 줄 알았으나 실제 카탈로그에 적용하니 레이아웃 밖으로 나갔다 ㅠ

클래스명을 살펴보니 클래스명을 통해 설정한 레이아웃과 위젯의 비율이 오버한 것 같았다.

nav-bar를 아래의 영역은 hexo\themes\icarus\layout\layout.jsx에서 관리된다.

총 column의 합이 12가 되도록 수정해주어야 한다.

columncount가 1일 경우 12 2일 경우 9 3일 경우 8을 사용하도록 수정해주었다.

image

위젯 수정하기

hexo\themes\icarus\layout\common\widgets.jsx또한 수정해주었다.

우측에 카탈로그를 추가하게 되면 column count가 왼쪽 위젯 게시글 오른쪽 위젯으로 3이 된다.

위에서 3일경우 8을 사용하도록 수정해주었기 때문에 3일때의 위젯의 레이아웃 비율을 2를 사용하여 합이 총 12(8+2+2)가 되도록 수정해주었다.

image

sticky 적용하기

개발자 도구를 열고 position:sticky를 적용하니 잘 되지 않았다ㅠ

widgets.jsx 파일을 살펴보니 is-sticky라는 클래스명이 있었다. 이를 개발자도구에 적용하니 제대로 작동하였다.

파일에서 찾아서 수정하려하니 생각보다 어려웠다.

그러던 중 widgets.jsx 파일을 살펴보니 columncount가 3이면 제일 오른쪽에 있는 모듈을 보이지 않게 해주는 함수를 찾았다.

내 경우 오른쪽에는 카탈로그 밖에 없고, 오른쪽에 다른 위젯이 있을 경우 겹치거나 보이지 않아서 가독성이 좋지 않다.

그래서 이부분에 is-sticky를 추가해주었다.

image

완성본

카탈로그

정상적으로 작동하게 되었다..!

hexo+ icarus 다크모드 구현하기

나는 다크모드를 아주 애용한다.

다크모드를 적용하며 유익한 경험이여서 글로 작성해두려고 한다.

hexo + icarus 작동원리

우선 hexo + icarus의 원리부터 알아야한다.

아래에 작성할 모든 디렉터리는 hexo루트 폴더 기준이다.

루드 디렉토리의 내부 파일을 수정한다.

그 후 정적파일인 public 폴더를 생성하고 배포하면 사이트에 반영되는 구조이다.

1
hexo generate --deploy

커스텀 디자인을 하기 위해선 이 ./themes 폴더를 수정해야한다.

버튼 만들기

./themes/icarus/layout/common 폴더엔 icarus의 여러 요소들의 jsx파일이 작성되어있다.

hexo\themes\icarus\layout\common\navbar.jsx파일에서 검색버튼 옆에 버튼을 만들었다.

1
<button type="button" id="changeModeButton" class="navbar-item" />

버튼을 커스텀하기

./themes/icarus/include/style 폴더엔 여러 css들이 작성되어있다.

확장자가 styl인데 기존에 작성된 파일을 보니 들여쓰기로 depth를 관리하는 듯 했다.

그리고 이 파일들은 ./themes/source/css/style.styl에서 모두 import되고 이를 빌드하여 ./public/css에 하나의 css파일을 생성한다.

./themes/icarus/include/style/navbar.styl 파일에 다음과 같은 내용을 추가하였다.

1
2
3
4
5
6
#changeModeButton {
background: none;
border: none;
cursor: pointer;
font-size: 2rem;
}

버튼에 로직 추가하기

./themes/source/js 폴더엔 여러 js파일들이 있다.

이는 ./themes/icarus/layout/common/scripts.js에서 한 번에 모아서 ./public/index.js에 작성된다.

여기에 다크모드 버튼 관련 파일을 작성하였다.

페이지에 접속 시 로컬스토리지를 확인하고 darkMode가 true일경우 html의 body에 darkMode 태그를 추가하고 버튼의 모양을 달 모양으로 해주었다.

값이 존재하지 않을경우 버튼의 모양을 해 모양으로 해주었다. 기본 값은 다크모드의 미적용이기 때문에 따로 처리를 해주진 않았다.

그리고 버튼에 이벤트를 등록해주었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
const darkModeButton = document.querySelector("#changeModeButton");

function init() {
loadDarkModeData();
darkModeButton.addEventListener("click", clickEvent);
}

const loadDarkModeData = () => {
const data = localStorage.getItem("darkMode");
const body = document.querySelector("body");
if (data === "true") {
darkModeButton.innerText = "🌙";
body.classList.add("darkMode");
} else {
darkModeButton.innerText = "🌞";
}
};

const setDarkMode = () => {
const body = document.querySelector("body");
if (body.classList.contains("darkMode")) {
darkModeButton.innerText = "🌞";
body.classList.remove("darkMode");
localStorage.setItem("darkMode", false);
} else {
darkModeButton.innerText = "🌙";
body.classList.add("darkMode");
localStorage.setItem("darkMode", true);
}
};

const clickEvent = () => {
setDarkMode();
console.log(localStorage.getItem("darkMode"));
};

init();

그리고 ./themes/icarus/layout/common/scripts.js에 아래 내용을 추가해주었다.

정확히 어떤 구조인지는 모르지만 main.js가 이런 식으로 작성되어 있길래 똑같이 적었다.

defer는 script로딩시 버튼이 없으면 안되기 때문에 추가해주었다.

1
<script src={url_for("/js/darkmode.js")} defer></script>

다크모드 css 추가하기

다음으로 darkmode.styl 파일을 작성해주었다.

처음 보는 확장자명이기도 했고 ./themes/include/style에 작성할지 ./themes/source/css에 작성할지 고민되었다.

고민끝에 include폴더는 컴포넌트마다의 css를 작성하는 느낌이 강해서 ./themes/source/css/darkmode.styl에 작성하였다.

css는 개발자 도구를 여러 하나하나 클래스명을 확인하며 적어주었다.

빌드과정에서 압축될 것이여서 하나씩 풀어 적었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
body.darkMode
background-color: #171717;
color:#F9F9F9;
.navbar ,& .navbar-menu
background-color: #444444;
section,article,.card{
background-color: inherit;
color:inherit;
}
& .navbar-item
color:#F9F9F9;
&:hover{
color:#3273dc;
background-color:#393E46;
}
& .menu-list>li *{
color:rgba(255,255,255,0.9);
}

& .menu-list a:hover{
color:#3273dc;
background-color:#393E46;
}

& .menu-list a.is-active{
background:#141010;
}
& .is-active
color:#3273dc;
& article
& > * {
color:rgba(255,255,255,0.7);
};
& h1,h2,h3,h4,h5,h6{
color:rgba(255,255,255,0.9);
}
& .article-meta{
color:rgba(255,255,255,0.38);
}
& .card-content
& > * {
color:rgba(255,255,255,0.7);
};

& .title, .location{
color:rgba(255,255,255,0.9);
}

& .menu{
color:rgba(255,255,255,0.7);
}

& .level-start{
color:rgba(255,255,255,0.7);
}

& .menu-label{
color:rgba(255,255,255,0.9);
}

& .tag{
background:#444444;
color:rgba(255,255,255,0.7);
}
.searchbox-container
background:#444444;
.searchbox-input, .searchbox-close{
background:#444444;
color:rgb(255,255,255);
}
.footer{
background:#444444;
}
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
color: #abb2bf;
background: #282c34;
}

.hljs-comment,
.hljs-quote {
color: #5c6370;
font-style: italic;
}

.hljs-doctag,
.hljs-keyword,
.hljs-formula {
color: #c678dd;
}

.hljs-section,
.hljs-name,
.hljs-selector-tag,
.hljs-deletion,
.hljs-subst {
color: #e06c75;
}

.hljs-literal {
color: #56b6c2;
}

.hljs-string,
.hljs-regexp,
.hljs-addition,
.hljs-attribute,
.hljs-meta-string {
color: #98c379;
}

.hljs-built_in,
.hljs-class .hljs-title {
color: #e6c07b;
}

.hljs-attr,
.hljs-variable,
.hljs-template-variable,
.hljs-type,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo,
.hljs-number {
color: #d19a66;
}

.hljs-symbol,
.hljs-bullet,
.hljs-link,
.hljs-meta,
.hljs-selector-id,
.hljs-title {
color: #61aeee;
}

.hljs-emphasis {
font-style: italic;
}

.hljs-strong {
font-weight: bold;
}

.hljs-link {
text-decoration: underline;
}


그리고 ./themes/source/css/style.styl에 import 해주었다.

1
@import './darkmode.styl'

정리

전체적인 로직은 다음과 같다.

  1. body.darkMode의 하위 태그들에 적용이 되도록 여러 css를 작성해둔다.
  2. 첫 접속시에 로컬스토리지에 darkMode 관련 값이 저장되어있는지 확인하고 알맞은 처리를 해준다. 새로고침시에 상태가 유지되게 하기 위함이다.
  3. 버튼 클릭시 다크모드가 적용되도록 이벤트를 추가해준다.(body태그에 darkMode 클래스를 toggle하도록)

완성본

다크모드가 적용될 경우 적용되지 않을경우의 버튼이 다르고 글자 색과 배경 색도 다르다.

다크모드 미적용 다크모드 적용

느낀 점

  1. styl 확장자 파일을 작성해보았다.
    • Stylus CSS파일이라고 하는데, 작성해보니 의외로 굉장히 편하였다.
  2. REACT가 아닌 jsx로 작성되어 이러한 파일들을 중앙에서 제어하여 빌드시 html을 만드는 과정에 대한 이해가 늘었다.
    • jsx문법을 이용하여 컴포넌트 단위로 파일을 작성하고 관리하는 것이 얼마나 가독성이 좋은지 다시 한 번 느꼈다.
  3. 역시 다크모드가 눈이 편하다
    • 다크모드가 최고다 ㅠ

아쉬운 점

  1. 구현을 하고 보니 코드블록이나 위젯에는 다크모드가 적용이 안 되었다.
    • 이것도 개발자 도구를 이용하여 css를 까보고 수정해야겠다.
  2. 모바일에서 레이아웃이 아쉽다.
    • 버튼을 추가하다보니 상단바에 스크롤바가 생겼다.
    • 이것도 수정해보아야겠다. 레이아웃에 손을 대거나 전체적으로 크기를 줄이는 방향으로 갈 것 같다.
    • 모바일
  3. 정신없이 작업하고 복기하며 메모하다보니 빠진 부분이 있을까봐 걱정된다.
    • 다음부턴 간략한 개요든지 사진을 틈틈히 찍어두어야겠다 ㅠ

+++ 수정

다크모드를 끄고 게시글에 접근시 다크모드가 적용되는 버그가 있었다.
로컬스토리지에 데이터가 string으로 저장되는데 if(data)의 형태로 확인해서 발생한 버그였다.

1
2
3
4
5
6
7
8
9
10
const loadDarkModeData = () => {
const data = localStorage.getItem("darkMode");
const body = document.querySelector("body");
if (data) {
darkModeButton.innerText = "🌙";
body.classList.add("darkMode");
} else {
darkModeButton.innerText = "🌞";
}
};

위 부분을 다음과 같이 변경하였다.

1
2
3
4
5
6
7
8
9
10
const loadDarkModeData = () => {
const data = localStorage.getItem("darkMode");
const body = document.querySelector("body");
if (data === "true") {
darkModeButton.innerText = "🌙";
body.classList.add("darkMode");
} else {
darkModeButton.innerText = "🌞";
}
};

hexo+icarus 위젯 삭제하고 댓글 기능 추가하기

위젯 없애기

블로그 내에서 표시되는 위젯들은 루트디렉토리의 _config.icarus.yml에서 수정 가능하다.

수정하려면 윗줄의 -를 포함하여 다음 -까지 위젯 관련 정보들을 지워주면 된다.
주석 줄에 있어서 -도 주석인 줄 알고 안 지웠더니 빌드에 실패하였다.
나는 카테고리나 태그같은 게시글 관련 위젯 말고는 전부 지워주었다.

위젯 윗부분에 donation도 삭제 해주었다.

1
2
3
4
- # 주석
position: ''
type: ''
(...)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# Table of contents widget configurations

- # Where should the widget be placed, left sidebar or right sidebar
position: right
type: toc
# Whether to show the index of each heading
index: true
# Whether to collapse sub-headings when they are out-of-view
collapsed: true
# Maximum level of headings to show (1-6)
depth: 3

# Recommendation links widget configurations

# Categories widget configurations

- # Where should the widget be placed, left sidebar or right sidebar
position: left
type: categories

# Recent posts widget configurations

- # Where should the widget be placed, left sidebar or right sidebar
position: left
type: recent_posts

# Archives widget configurations

- # Where should the widget be placed, left sidebar or right sidebar
position: left
type: archives

# Tags widget configurations

- # Where should the widget be placed, left sidebar or right sidebar
position: left
type: tags

댓글 기능 추가

disqus를 이용하였다.

사용방법이 무척 간단하다.

  1. disqus 접속한다.

    홈페이지
  2. I want to instal …를 클릭한다.

    선택 페이지
  3. 웹사이트 명을 입력해준다.
    이 정보는 shortname에 사용된다.
    아래의 shortname을 복사해둔다.

    설정하기
  4. Basic 요금제를 구독한다.

    무료 요금제를 고른다.
  5. Universal Code를 선택한다.

    목록에 없다 ㅠ
  6. icarus엔 shortname만 필요하다.
    configure를 눌러 다음으로 가고 정보를 입력해준다.
    website URL을 채워주면 된다.

    정보를 입력한다.
  7. 정책을 골라준다.
    큰 차이점은 basic은 비로그인 댓글도 허용하고
    strict는 로그인 댓글만 허용한다.
    다만 google, twitter, facebook 자동로그인을 사용할 수 있다.
    나는 strict을 골라주었다.

    정책
  8. dismiss를 누르고 hexo 루트디렉토리의 _config.icarus.yml 파일을 수정한다.

    1
    2
    3
    4
    5
    6
    comment:
    type: disqus
    # Disqus shortname
    shortname: "shortname"
    # Donate plugin configurations
    # https://ppoffice.github.io/hexo-theme-icarus/categories/Plugins/Donation/
  9. 변경사항을 적용한다.

1
hexo clean & hexo generate --deploy & git add . & git commit -m "메시지" & git push origin main
  1. 변경사항이 적용되었음을 확인할 수 있다..!

hexo에서 이미지 업로드하기

++ 수정

아래의 방법은 간단하지만 랜더링 최적화를 위해선 정적 이미지파일을 사용하는 것을 추천합니다!

hexo는 Markdown으로 게시글을 작성한다. 때문에 이미지를 업로드 하는 과정이 쉽지 않다.

hexo-icarus테마-수정하기 게시글을 작성하며 사진을 업로드 할 때는

_posts의 상의 폴더인 source 폴더에 img 폴더를 만들고 거기에 이미지를 저장해서 img태그src속성에 상대경로를 적어 사용하였다.

이렇게 하니 home으로 접근할 때는 정상적으로 이미지를 출력하지만 tag 탭이라든지 경로가 바뀌면 이미지가 다음과 같이 깨졌다.

이미지가 깨졌어요

어떻게 해결할지 고민하다가 hexo 소스들을 정리해둔 private 레포가 생각났다.

블로그 관련 레포에 모든 정보를 모아서 관리하면 수월할 것이라 생각해 issue탭에 이미지를 업로드하고(깃허브 서버에 올린 뒤) 이 링크를 받아서 사용하였다.

그랬더니 현재 경로와 상관없이 정상적으로 이미지 출력에 성공하였다.

하지만 이미지를 하나씩 업로드 하는 방식이 효율적이진 않은데, 다음에 이미지를 업로드 할 때는 레포에 업로드 해보고 이용할 수 있는지 해봐야겠다.