sqzass.git

sqzass

~ A static site generator written in Rust
git clone https://github.com/debuman/sqzass.git

docs/templates/base.html (10092B)

      1 {% from "partials/sidebar.html" import nav %}
      2 {% from "partials/toc.html" import toc_list %}
      3 {%- set ko = page.language == "ko" %}
      4 {#- 서브경로에 올라갈 수도 있으므로 홈 링크에 site.base_path를 붙인다.
      5     페이지 URL들은 이미 이 접두사를 품고 있어서 손댈 필요가 없다. #}
      6 {%- set home = site.base_path ~ ("/ko/" if ko else "/") %}
      7 <!doctype html>
      8 {#- The default is written here rather than left to `prefers-color-scheme`, so
      9     a reader with a light OS still gets the dark page the design assumes — and,
     10     more importantly, so the generated highlight stylesheet's `[data-theme]`
     11     rules match without JavaScript. The script below only overrides it. #}
     12 <html lang="{{ page.language }}" data-theme="dark">
     13 <head>
     14 <meta charset="utf-8">
     15 <meta name="viewport" content="width=device-width, initial-scale=1">
     16 <title>{% if page.url == home %}{{ site.title }} — {{ t("tagline") }}{% else %}{{ page.title }} · {{ site.title }}{% endif %}</title>
     17 {% if page.description %}<meta name="description" content="{{ page.description }}">{% endif %}
     18 <link rel="canonical" href="{{ page.permalink }}">
     19 {#- 피드 자동 발견. 이 언어에 날짜 있는 페이지가 하나도 없으면 site.feed가 없고,
     20     그때는 링크도 걸지 않는다 — 빈 피드를 가리키는 링크가 제일 나쁘다. #}
     21 {%- if site.feed %}
     22 <link rel="alternate" type="application/atom+xml" title="{{ site.title }}" href="{{ site.feed }}">
     23 {%- endif %}
     24 {#- 파비콘은 CSS 필터가 닿지 않는다. 탭 배경이 어두울 때 검은 선화는 보이지
     25     않으므로 흰 선 버전을 따로 준다. media를 이해 못 하는 브라우저는 마지막
     26     것을 쓰므로, 밝은 탭이 기본인 쪽을 뒤에 둔다. #}
     27 <link rel="icon" type="image/png" media="(prefers-color-scheme: dark)" href="{{ asset("images/favicon-dark.png") }}">
     28 <link rel="icon" type="image/png" href="{{ asset("images/favicon.png") }}">
     29 <link rel="apple-touch-icon" href="{{ asset("images/apple-touch-icon.png") }}">
     30 {#- `lang`이지 `t`가 아니다. `t`는 번역 함수이고, 루프 변수가 그 이름을 가리면
     31     이 블록 안에서 라벨을 하나 넣는 순간 조용히 깨진다. #}
     32 {%- for lang in page.translations %}
     33 <link rel="alternate" hreflang="{{ lang.code }}" href="{{ site.origin }}{{ lang.url }}">
     34 {%- endfor %}
     35 {#- 소셜 카드. sqzass는 <head>에 아무것도 주입하지 않으므로 이건 우리 마크업이다.
     36     입력은 전부 이미 컨텍스트에 있다 — 새로 만들 데이터가 없다. #}
     37 <meta property="og:type" content="{{ "website" if page.is_section else "article" }}">
     38 <meta property="og:site_name" content="{{ site.title }}">
     39 <meta property="og:title" content="{{ page.title }}">
     40 <meta property="og:url" content="{{ page.permalink }}">
     41 <meta property="og:locale" content="{{ "ko_KR" if ko else "en_US" }}">
     42 {%- if page.description %}
     43 <meta property="og:description" content="{{ page.description }}">
     44 {%- endif %}
     45 {#- summary_large_image는 이미지가 있을 때만 뜻이 있다. 이미지가 없으면 summary가
     46     맞고, 이미지 없이 large를 선언하면 카드가 빈 상자로 나온다. #}
     47 <meta name="twitter:card" content="summary">
     48 <link rel="stylesheet" href="{{ asset("css/main.css") }}">
     49 {%- if site.highlight_css %}
     50 <link rel="stylesheet" href="{{ site.highlight_css }}">
     51 {%- endif %}
     52 <script>
     53 /* Runs before first paint so a stored preference does not flash the other
     54    theme. Inline on purpose: an external file would arrive too late. */
     55 (function(){try{var t=localStorage.getItem("theme");if(t)document.documentElement.dataset.theme=t}catch(e){}})();
     56 
     57 </script>
     58 </head>
     59 <body>
     60 <a class="skip" href="#content">{{ t("skip_to_content") }}</a>
     61 
     62 <header class="masthead">
     63   {#- alt는 비운다. 바로 옆 텍스트가 이미 같은 이름을 말하므로, 여기에 이름을
     64       또 넣으면 스크린리더가 "sqzass sqzass"라고 읽는다. #}
     65   <a class="brand" href="{{ home }}">
     66     <img src="{{ asset("images/logo.png") }}" alt="" width="121" height="120">
     67     <span>{{ site.title }}</span>
     68   </a>
     69 
     70   {#- 문서 갈래. 사이드바와 같은 데이터를 한 단계 위에서 보여 주므로, 좁은 화면에서는
     71       사이드바 서랍이 같은 일을 해서 감춘다. #}
     72   <nav class="sections" aria-label="{{ t("sections") }}">
     73     {%- for s in site.sections %}
     74     <a href="{{ s.url }}"{% if page.section and page.section.url == s.url or page.url == s.url %} aria-current="true"{% endif %}>{{ s.title }}</a>
     75     {%- endfor %}
     76   </nav>
     77 
     78   {#- 크롬은 전부 같은 컨트롤이다. 하나만 텍스트 링크로 두면 그 하나가 고장난
     79       것처럼 보인다. #}
     80   <div class="tools">
     81     {%- if site.search %}
     82     <button id="search-trigger" class="chip search-trigger" type="button"
     83             data-index="{{ site.search }}">
     84       <span>{{ t("search") }}</span>
     85       {#- 라벨은 search.js가 읽는 사람의 수정자 키로 바꿔 쓴다. 이건 JS가 없을 때 값. #}
     86       <kbd>Ctrl /</kbd>
     87     </button>
     88     {%- endif %}
     89     {%- for lang in page.translations %}
     90     <a class="chip" href="{{ lang.url }}" hreflang="{{ lang.code }}">{{ lang.name }}</a>
     91     {%- endfor %}
     92     {#- 아이콘은 인라인 SVG다. 이 레포에 "글리프 말고 텍스트"라는 결정이 있지만,
     93         그 근거는 ◐/☾ 같은 문자가 폰트 폴백 스택에 없어 네모로 나온다는 것이었다.
     94         SVG는 폰트를 타지 않으므로 그 이유가 적용되지 않는다. 대신 아이콘만 있는
     95         컨트롤은 이름이 없으므로 aria-label을 반드시 준다. #}
     96     <a class="chip chip-icon chip-wide" href="https://github.com/debuman/sqzass"
     97        aria-label="GitHub" title="GitHub">
     98       <svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true" focusable="false"><path fill="currentColor" d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>
     99     </a>
    100     {#- 지금 테마가 아니라 **누르면 갈 곳**을 보여 준다. 다크에서는 해가 뜨고,
    101         라이트에서는 달이 뜬다 — 버튼은 상태가 아니라 동작을 말해야 한다. #}
    102     <button class="chip chip-icon theme-toggle" type="button"
    103             aria-label="{{ t("theme") }}" title="{{ t("theme") }}">
    104       <svg class="i-sun" viewBox="0 0 24 24" width="16" height="16" aria-hidden="true" focusable="false"
    105            fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
    106         <circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/>
    107       </svg>
    108       <svg class="i-moon" viewBox="0 0 24 24" width="16" height="16" aria-hidden="true" focusable="false"
    109            fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
    110         <path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/>
    111       </svg>
    112     </button>
    113   </div>
    114 </header>
    115 
    116 <div class="layout">
    117   {#- <details>는 열린 채로 나간다. 좁은 화면에서만 ui.js가 닫는다 — JS가 없으면
    118       펼쳐진 채로 남는데, 그건 못 쓰는 상태가 아니라 그냥 긴 상태다. #}
    119   <details class="sidebar" id="nav-drawer" open>
    120     <summary>{{ t("documentation") }}</summary>
    121     <nav aria-label="{{ t("documentation") }}">
    122       {{ nav(site.sections, page.url, page.section.url if page.section else "") }}
    123     </nav>
    124   </details>
    125 
    126   <main id="content">
    127     <div class="title-block">
    128       {%- if page.section %}
    129       <nav class="crumbs" aria-label="{{ t("breadcrumb") }}">
    130         <a href="{{ home }}">{{ t("home") }}</a>
    131         <a href="{{ page.section.url }}">{{ page.section.title }}</a>
    132       </nav>
    133       {%- endif %}
    134       {#- 페이지가 아닌 템플릿(404)은 자기 제목을 갖는다. #}
    135       <h1>{% block heading %}{{ page.title }}{% endblock %}</h1>
    136       {%- if page.description %}
    137       <p class="lede">{{ page.description }}</p>
    138       {%- endif %}
    139     </div>
    140 
    141     {#- The front matter fence: metadata ends, content begins. Same boundary
    142         it marks in the source file. #}
    143     <div class="fence" aria-hidden="true">+++</div>
    144 
    145     {% block content %}{% endblock %}
    146   </main>
    147 
    148   {%- if page.toc and page.toc_entries %}
    149   <aside class="toc" data-label="{{ t("on_this_page") }}" aria-label="{{ t("on_this_page") }}">
    150     {{ toc_list(page.toc_entries) }}
    151   </aside>
    152   {%- endif %}
    153 </div>
    154 
    155 {%- include "partials/colophon.html" %}
    156 
    157 {%- if site.search %}
    158 <dialog id="search-dialog" class="search-dialog" aria-label="{{ t("search_the_docs") }}">
    159   <div class="search-panel">
    160     <input type="search" autocomplete="off" spellcheck="false" aria-label="{{ t("search_the_docs") }}">
    161     <ul class="search-results"></ul>
    162     <div class="search-foot">
    163       <span class="search-status"></span>
    164       <span class="search-hint"></span>
    165     </div>
    166   </div>
    167 </dialog>
    168 {%- endif %}
    169 
    170 <script>
    171 document.querySelector(".theme-toggle").addEventListener("click", function () {
    172   var root = document.documentElement;
    173   var next = root.dataset.theme === "light" ? "dark" : "light";
    174   root.dataset.theme = next;
    175   try { localStorage.setItem("theme", next); } catch (e) {}
    176 });
    177 </script>
    178 {%- if site.search %}
    179 <script src="{{ asset("js/search.js") }}" defer></script>
    180 {%- endif %}
    181 <script src="{{ asset("js/ui.js") }}" defer
    182         data-copy="{{ t("copy") }}" data-copied="{{ t("copied") }}"></script>
    183 </body>
    184 </html>