오늘의 스킨제작: highlight.js, 구문강조 박스 꾸미기
목표
```python
print("python")
마크다운 문법으로 코드 블록을 사용한 예시입니다. 티스토리를 기준으로 마크다운 코드 블록은 다음과 같이 변환됩니다.
```html
<pre>
<code class="language-python">
print("python")
</code>
</pre>
구문 강조 도구인 highlight.js를 사용하면 구문 강조를 위한 태그와 도구가 다녀갔다는 흔적인 클래스를 남기게 됩니다.
<pre>
<code class="language-python hljs">
print(<span class="hljs-string">"python"</span>)
</code>
</pre>
오늘은 변환한 코드블럭이 어떤 언어를 사용하는지 보여주는 박스를 추가했습니다.
사용한 트릭 & 팁
티스토리 외부 자바스크립트에서 스킨 URL을 다루는 방법
images
에 추가하는 스킨 파일은 skin.html
에서는 상대경로 표기로 접근할 수 있지만 외부에서 불러오는 스크립트 파일은 변환 대상이 아니기 때문에 곤란합니다. 그래서 한 가지 트릭을 사용합니다.
<meta name="skin_var" id="skin_root_url" content="./images">
이렇게 <head>
태그 안에 위의 메타태그를 사용하면 id 속성을 이용해 손쉽게 접근할 수 있습니다.
var root_url = document.querySelector('#skin_root_url').getAttribute('content');
ES6의 템플릿 문자열
ECMAScript 6(ES6), 혹은 ECMAScript 2015라고 불리는 자바스크립트 표준이 있습니다. jQuery만 주구장창 해왔던 제가 '어라 이거 더 이상 안 써도 괜찮겠는데?'라는 생각이 들게 한 여러 기능과 문법이 추가되었습니다. 그중 하나가 지금 다루는 템플릿 문자열입니다.
템플릿 문자열은 `
(억음부호, 보통 탭 위에 있습니다.)로 묶습니다. 줄 바꿈을 위해서 \n
을 사용하지 않아도 되는데다 ${변수}
로 값을 포함할 수 있으니 결과물에 가까운 입력 문자열을 볼 수 있다는 편리함이 큰 장점입니다.
var foo = 600;
var bar = 'bar';
var temp_str = `<div><ul>
<li>${foo}</li>
<li>${bar}</li>
</ul></div>`;
// 출력
// <div><ul>
// <li>600</li>
// <li>bar</li>
// </ul></div>
투명한 배경의 png 이미지에 그림자 입히기
CSS에서 그림자가 필요하다고 하면 보통은 box-shadow
속성을 사용하게 됩니다.하지만 배경이 투명한 PNG 등에서는 원하는 그림자가 아닌 사각형으로 붕 뜬 그림자만 보게 되는데요.
이때 사용할 수 있는 속성이 filter
속성의 drop-shadow
입니다. 모질라 재단의 데모 통해 효과를 직접 살펴보고 어떻게 사용하는지를 살펴볼 수 있습니다.
filter: drop-shadow(0px 0px 3px rgba(255,255,255,0.6));
결과물
이제 당분간은 코드 블록이랑 씨름하지 않을 생각입니다.
<pre>
<code class="language-python hljs">
print(<span class="hljs-string">"python"</span>)
</code>
</pre>
위의 내용이 아래의 내용으로 바뀌게 됩니다.
<pre class="ss_codeblock_wrapper">
<code class="language-python hljs">
소스코드 내용
</code>
<div class="ss_codeblock_menubar">
<img class="logo" src="https://tistory3.daumcdn.net/tistory/1798630/skin/images/language-python.png">
<span class="lang">Python</span>
</div>
</pre>
소스코드
<!-- 스크립트용 스킨 URL -->
<meta name="skin_var" id="skin_root_url" content="./images">
var root_url = document.querySelector('#skin_root_url').getAttribute('content');
document.addEventListener('DOMContentLoaded', (event) => {
document.querySelectorAll('pre code').forEach((block) => {
hljs.highlightBlock(block);
plugin_codeblock(block);
});
});
var code_langs_dict = {
'language-apache': 'APACHE',
'language-bash': 'BASH',
'language-coffeescript': 'CoffeeScript',
'language-cpp': 'C++',
'language-cs': 'C#',
'language-css': 'CSS',
'language-go': 'Go',
'language-ini': 'INI(TOML)',
'language-java': 'Java',
'language-javascript': 'JavaScript',
'language-json': 'JSON',
'language-kotlin': 'Kotlin',
'language-less': 'Less',
'language-lua': 'Lua',
'language-xml': 'XML',
'language-markdown': 'Markdown',
'language-nginx': 'NGINX',
'language-objectivec': 'Objective-C',
'language-perl': 'Perl',
'language-php': 'PHP',
'language-plaintext': 'TEXT',
'language-python': 'Python',
'language-ruby': 'Ruby',
'language-rust': 'Rust',
'language-scss': 'SASS(SCSS)',
'language-shell': 'Shell',
'language-sql': 'SQL',
'language-swift': 'Swift',
'language-typescript': 'TypeScript',
'language-yaml': 'YAML'
}
function plugin_codeblock(block) {
let wrapper = block.parentElement;
let menubar = document.createElement("DIV");
for (let i = 0, cls; cls = block.classList[i]; i++) {
if (/language-.*/.test(cls)) {
let logo_src = `${root_url}/${cls}.png`;
let name = code_langs_dict[cls];
menubar.innerHTML = `<img class="logo" src="${logo_src}"><span class="lang">${name}</span>`
} else if (cls != "hljs") {
let logo_src = `${root_url}/language-${cls}.png`;
let name = code_langs_dict[`language-${cls}`];
menubar.innerHTML = `<img class="logo" src="${logo_src}"><span class="lang">${name}</span>`
}
}
wrapper.classList.add('ss_codeblock_wrapper');
menubar.classList.add('ss_codeblock_menubar');
wrapper.appendChild(menubar);
}
.ss_codeblock_wrapper {
position: relative;
}
.ss_codeblock_wrapper .ss_codeblock_menubar {
position: absolute;
left: 0; right: 0; top: 0;
min-height: 1em;
background: rgba(0,0,0,0.38);
box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
color: rgba(255,255,255,0.86);
padding: 0.3rem 0.5rem;
display: grid;
grid-template-columns: auto 1fr;
column-gap: 0.5rem;
}
.ss_codeblock_wrapper .lang {
align-self: center;
justify-self: start;
font-family: 'Noto Sans KR', sans-serif;
font-weight: bold;
}
.ss_codeblock_wrapper .logo {
justify-self: center;
align-self: center;
height: 1.2em;
width: auto;
filter: drop-shadow(0px 0px 3px rgba(255,255,255,0.6));
}
.ss_codeblock_wrapper code {
padding-top: 2.3em;
overflow: auto;
max-height: 300px;
}
.ss_codeblock_wrapper code.open {
max-height: none;
}
'프로그래밍 > 웹 프로그래밍' 카테고리의 다른 글
CSS: 그리드(Grid) 컨테이너 속성 (0) | 2019.11.18 |
---|---|
CSS: 그리드(Grid) 예제 (0) | 2019.11.17 |
오늘의 스킨제작: highlight.js 가지고 놀기 (0) | 2019.11.16 |
오늘의 스킨제작: highlight.js, 소스코드 하이라이팅 (0) | 2019.11.15 |
오늘의 스킨제작: 라이트박스수정하기 (0) | 2019.11.14 |
댓글