Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 63 additions & 21 deletions en/index.html

Large diffs are not rendered by default.

84 changes: 63 additions & 21 deletions index.html

Large diffs are not rendered by default.

84 changes: 63 additions & 21 deletions ru/index.html

Large diffs are not rendered by default.

25 changes: 24 additions & 1 deletion tools/build_pages.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#!/usr/bin/env python3
"""Generate per-language pages ru/en/zh/index.html from the root index.html.
"""Generate per-language pages ru/en/zh/index.html (v1 skin) and v2/{ru,en,zh}/index.html (editorial skin).

Root index.html = auto-detect (navigator.language, fallback en); subdir pages force their language.
v2 pages swap the <style> block for tools/v2.css and set __HTLB_BASE__='../../' so the same
README/book data files are fetched from the repo root (no data duplication).
Run from repo root: python3 tools/build_pages.py
"""
import os, re, sys
Expand All @@ -20,3 +23,23 @@
os.makedirs(os.path.dirname(out), exist_ok=True)
open(out,'w',encoding='utf-8').write(d)
print('built', os.path.relpath(out, ROOT))

# ---------- v2 (editorial skin) ----------
V2MARK = '<script>window.__HTLB_V2__=1;</script>' # injected in root index.html <head>
STYLE_RE = re.compile(r'<style>.*?</style>', re.S)
v2css = open(os.path.join(ROOT,'tools','v2.css'), encoding='utf-8').read()
if not STYLE_RE.search(src):
sys.exit('root index.html: <style> block not found')
if V2MARK not in src:
print('note: root index.html has no %s marker; v2 pages get skin flag only via generation' % V2MARK)
for lang in ('ru','en','zh'):
d = src.replace(tpl, "<script>window.__HTLB_LANG__='%s';window.__HTLB_BASE__='../../';window.__HTLB_V2__=1;document.documentElement.classList.add('v2');</script>" % lang)
# README/book links in header & noscript point to repo root; v2 lives one level deeper
d = d.replace('href="README', 'href="../../README').replace('href="book/', 'href="../../book/')
d = STYLE_RE.sub(lambda _: '<style>\n' + v2css + '\n</style>', d, count=1)
d = d.replace(CANON, '<link rel="canonical" href="https://dlgrv.github.io/HowToLiveBetter/v2/%s/">' % lang)
d = d.replace(OGURL, '<meta property="og:url" content="https://dlgrv.github.io/HowToLiveBetter/v2/%s/">' % lang)
out = os.path.join(ROOT, 'v2', lang, 'index.html')
os.makedirs(os.path.dirname(out), exist_ok=True)
open(out,'w',encoding='utf-8').write(d)
print('built', os.path.relpath(out, ROOT))
192 changes: 192 additions & 0 deletions tools/v2.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/* ============================================================
HTLB v2 — editorial minimal skin (dlgrv.com style)
Same DOM & JS as v1; this file replaces the <style> block
for pages generated under /v2/ (tools/build_pages.py).
Light: white / ink. Dark: #111 / #d0d0d0. One accent: link hover blue.
============================================================ */
:root{
--bg:#ffffff; --ink:#111111; --text:#333333; --mut:#666666; --faint:#8e8e8e;
--line:#e3e3e3; --line-soft:#d5d5d5; --hover:#2481cc; --pop-bg:#ffffff;
--serif:Georgia,"Times New Roman",serif;
--sans:Georgia,"Times New Roman",serif; /* единый шрифт на всей странице (решение пользователя) */
--mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
--nav-h:56px; --side-w:264px;
}
html.dark{
--bg:#111111; --ink:#f5f5f5; --text:#d0d0d0; --mut:#a0a0a0; --faint:#7d7d7d;
--line:#2e2e2e; --line-soft:#3a3a3a; --hover:#5cb1e8; --pop-bg:#1b1b1b;
}
*,*::before,*::after{box-sizing:border-box}
[hidden]{display:none!important}
body{margin:0;background:var(--bg);color:var(--text);font:17.5px/1.7 var(--serif);-webkit-font-smoothing:antialiased}
a{color:inherit;text-decoration:none}
a:focus-visible,button:focus-visible,input:focus-visible,summary:focus-visible,abbr:focus-visible{outline:1px solid var(--hover);outline-offset:2px}
::selection{background:var(--ink);color:var(--bg)}

/* ---------- top bar ---------- */
.nav{position:fixed;top:0;left:0;right:0;height:var(--nav-h);z-index:30;background:var(--bg);border-bottom:1px solid var(--line)}
.nav-in{max-width:1200px;margin:0 auto;height:100%;display:flex;align-items:center;gap:14px;padding:0 24px;position:relative}
.icon-btn.menu-btn{display:none}
.title{display:flex;align-items:center;gap:8px;font:500 14px/1.3 var(--sans);color:var(--text);white-space:nowrap}
.title:hover{color:var(--ink)}
.title .logo{display:none}
/* поиск строго по центру окна (независимо от ширины титула и правого кластера) */
.search{position:absolute;left:50%;transform:translateX(-50%);width:min(340px,32vw)}
.search svg{position:absolute;left:10px;top:50%;transform:translateY(-50%);width:14px;height:14px;color:var(--faint);fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.search input{width:100%;height:36px;padding:0 44px 0 32px;border:1px solid var(--line);border-radius:8px;background:none;color:var(--ink);font:14px/1.4 var(--sans);transition:border-color .15s,box-shadow .15s}
.search input::placeholder{color:var(--faint)}
.search input:focus{outline:0;border-color:var(--ink);box-shadow:0 1px 0 rgba(0,0,0,.03)}
.search kbd{position:absolute;right:8px;top:50%;transform:translateY(-50%);font:500 11px/1 var(--sans);color:var(--faint);border:1px solid var(--line-soft);border-radius:4px;padding:3px 6px;background:var(--bg);pointer-events:none}
.icon-btn{display:grid;place-items:center;gap:0;width:32px;height:32px;border:0;border-radius:0;background:none;color:var(--faint);cursor:pointer;transition:color .15s;flex:none}
.icon-btn:hover{color:var(--ink)}
.icon-btn svg{width:16px;height:16px}
/* все иконки — Lucide stroke, единый калибр 16px; шеврон у «РУ» — аффорданс, оставлен мелким */
button,input,kbd,summary{font-family:var(--sans)}
.lang-dd summary svg{width:11px;height:11px}
.nav-r{display:flex;align-items:center;gap:10px;margin-left:auto}
.switch{display:none!important}
/* кнопка темы: icon-btn с sun/moon — в светлой показываем луну (переход в тёмную), в тёмной — солнце */
.theme-btn .sun{display:none}
html.dark .theme-btn .sun{display:block}
html.dark .theme-btn .moon{display:none}
/* кнопка версии: только в v2-скине (v1-страницы не получают ver-switch) */
.ver-btn{display:none}
html.v2 .ver-btn{display:grid}

/* ---------- language dropdown ---------- */
.lang-dd{position:relative}
.lang-dd>summary{list-style:none;cursor:pointer;display:grid;place-items:center;height:32px;width:auto;min-width:32px;border:0;background:none;color:var(--faint);grid-auto-flow:column;grid-template-columns:auto auto;column-gap:4px;align-items:center;padding:0 6px;transition:color .15s}
.lang-dd>summary::-webkit-details-marker{display:none}
.lang-dd>summary:hover{color:var(--ink)}
.lang-dd>summary svg{width:10px;height:10px;color:var(--faint);fill:none;stroke:currentColor;stroke-width:2}
#lang-label{font:600 12px/1 var(--sans);letter-spacing:.08em}
.lang-menu{position:absolute;right:0;top:calc(100% + 6px);background:var(--pop-bg);border:1px solid var(--line);padding:4px 0;min-width:120px;z-index:40}
.lang-menu button{display:block;width:100%;text-align:left;padding:7px 14px;border:0;background:none;color:var(--mut);font:13px/1.4 var(--sans);cursor:pointer;transition:color .15s,background .15s}
.lang-menu button:hover{color:var(--ink);background:var(--line)}

/* ---------- tooltip popup ---------- */
.backdrop{position:fixed;inset:0;background:rgba(0,0,0,.32);opacity:0;pointer-events:none;transition:opacity .2s;z-index:15}
.backdrop.on{opacity:1;pointer-events:auto}
.pop{position:absolute;z-index:40;max-width:320px;background:var(--pop-bg);border:1px solid var(--line);box-shadow:0 4px 16px rgba(0,0,0,.08);padding:10px 14px;font:14px/1.6 var(--serif);color:var(--mut)}
.pop b{display:block;color:var(--ink);font-weight:600;margin-bottom:2px}

/* ---------- sidebar (filters) ---------- */
.sidebar{position:fixed;top:var(--nav-h);bottom:0;left:0;width:var(--side-w);background:var(--bg);border-right:1px solid var(--line);padding:28px 24px 96px;overflow-y:auto;overscroll-behavior:contain;z-index:20;font-family:var(--sans)}
.group{margin-bottom:26px}
.gt{font:700 11px/1.5 var(--sans);letter-spacing:.12em;text-transform:uppercase;color:var(--ink);margin-bottom:8px}
.gt small{display:block;font:400 12px/1.5 var(--sans);letter-spacing:0;text-transform:none;color:var(--mut);margin-top:2px}
.sec-links{display:flex;flex-direction:column;align-items:stretch;gap:0}
.sec-link{display:flex;align-items:baseline;gap:8px;width:100%;text-align:left;padding:5px 0;border:0;background:none;color:var(--mut);font:13px/1.5 var(--sans);cursor:pointer;transition:color .15s}
.sec-link:hover{color:var(--ink)}
.sec-link[aria-pressed="true"]{color:var(--ink);font-weight:600}
.sec-link i{margin-left:auto;font:400 11px/1 var(--sans);font-style:normal;font-variant-numeric:tabular-nums;color:var(--faint)}
.chips{display:flex;flex-direction:column;align-items:flex-start;gap:0}
.chip{border:0;background:none;color:var(--mut);font:13px/1.5 var(--sans);padding:5px 0;cursor:pointer;transition:color .15s;text-align:left}
.chip:hover{color:var(--ink)}
.chip[aria-pressed="true"]{color:var(--ink);text-decoration:underline;text-underline-offset:3px;text-decoration-color:var(--ink)}
.toggle{display:flex;align-items:center;gap:8px;padding:4px 0;color:var(--mut);font:13px/1.5 var(--sans);cursor:pointer}
.toggle:hover{color:var(--ink)}
.toggle input{accent-color:var(--ink);margin:0}
.reset{display:inline-block;margin-top:10px;padding:0;border:0;background:none;color:var(--mut);font:13px/1.5 var(--sans);cursor:pointer;text-decoration:underline;text-underline-offset:3px;text-decoration-color:var(--line-soft);transition:color .15s}
.reset:hover{color:var(--ink);text-decoration-color:var(--ink)}
.hint{margin-top:26px;padding-top:16px;border-top:1px solid var(--line)}
.hint p{margin:0 0 8px;font:12px/1.6 var(--sans);color:var(--faint)}
.hint a{color:var(--mut);text-decoration:underline;text-decoration-color:var(--line-soft);text-underline-offset:2px}
.hint a:hover{color:var(--hover)}

/* ---------- content ---------- */
.content{padding:calc(var(--nav-h) + 40px) 48px 128px calc(var(--side-w) + 56px)}
.sec-block{max-width:760px;margin:0 auto 88px}
.sec-h{display:flex;align-items:baseline;gap:16px;padding-top:16px;margin-bottom:8px}
.sec-h h2{margin:0;font:600 24px/1.35 var(--serif);letter-spacing:-.01em;color:var(--ink)}
.sec-h .shown{margin-left:auto;flex:none;font:12px/1 var(--sans);color:var(--faint);font-variant-numeric:tabular-nums}
.intro{margin:14px 0 30px;font:16px/1.7 var(--serif);color:var(--mut)}

/* card: no box — hairline top only */
.card{border-top:1px solid var(--line);padding:26px 0 20px;content-visibility:auto;contain-intrinsic-size:auto 300px}
.card-h{display:flex;align-items:baseline;gap:12px}
.idx{flex:none;min-width:28px;font:15px/1.5 var(--serif);font-variant-numeric:tabular-nums;color:var(--faint)}
.card-h h3{margin:0;font:600 19px/1.45 var(--serif);color:var(--ink);overflow-wrap:anywhere}
.anchor{flex:none;color:var(--faint);font:14px/1.5 var(--serif);opacity:0;transition:opacity .15s,color .15s}
.card:hover .anchor,.anchor:focus-visible{opacity:1}
.anchor:hover{color:var(--hover)}
.badges{display:flex;flex-wrap:wrap;gap:4px 14px;margin:8px 0 0 40px;font:11px/1.6 var(--sans);letter-spacing:.02em}
.badge{color:var(--faint)}
.badge.r3{color:var(--ink);font-weight:600}
.badge.r2{color:var(--mut)}
.badge.r1{color:var(--faint)}
.badge.danger{color:var(--ink);font-weight:600;text-decoration:underline;text-underline-offset:2px}
.badge.warn{color:var(--mut);font-style:italic}
.human{margin:12px 0;padding:2px 0 2px 16px;border-left:3px solid var(--line);font:italic 16px/1.7 var(--serif);color:var(--mut);overflow-wrap:anywhere}
.rows{display:grid;grid-template-columns:86px minmax(0,1fr);gap:4px 14px;margin:0 0 12px 40px}
.rows .k{font:700 11px/1.8 var(--sans);letter-spacing:.1em;text-transform:uppercase;color:var(--faint)}
.rows .v{font:16px/1.65 var(--serif);color:var(--text);min-width:0;overflow-wrap:anywhere}
.rows .v.gain{color:var(--ink)}
.src{margin:0 0 0 40px}
.src summary{display:inline-block;cursor:pointer;font:12px/1.6 var(--sans);color:var(--faint);text-decoration:underline;text-decoration-style:dotted;text-decoration-color:var(--line-soft);text-underline-offset:3px;transition:color .15s}
.src summary:hover{color:var(--ink)}
.src .body{margin-top:8px;padding:2px 0 2px 14px;border-left:2px solid var(--line);font:14.5px/1.7 var(--serif);color:var(--mut)}
.src .body p{margin:0 0 .6em}
.src .body a{color:var(--mut);text-decoration:underline;text-decoration-color:var(--line-soft);text-underline-offset:2px}
.src .body a:hover{color:var(--hover)}

/* ---------- press polish: scroll-spy, marginalia ---------- */
/* подсветка текущего раздела в сайдбаре */
.sec-link.spy-on{color:var(--ink)}
.sec-link.spy-on span{text-decoration:underline;text-decoration-color:var(--ink);text-underline-offset:3px}
h1,h2,h3{text-wrap:balance}
body{hyphens:auto;-webkit-hyphens:auto}
/* маргиналии по-Тафти: Примечания уходят в правое поле; колонка прижата влево,
чтобы поле было широким; на узких экранах всё остаётся в потоке */
@media (min-width:1440px){
.doc{margin-left:0;margin-right:auto}
.card{position:relative}
.card .marg{position:absolute;top:2px;left:calc(100% + 36px);width:200px}
.card > .rows [data-k="note"],.card > .rows [data-k="note"] + .v{display:none}
}
@media (max-width:1439px){
.card .marg{display:none}
}
.marg{font:13.5px/1.65 var(--serif);color:var(--mut);overflow-wrap:anywhere}
.marg .k{font:700 10px/1.8 var(--sans);letter-spacing:.1em;text-transform:uppercase;color:var(--faint);display:block}
.term{cursor:help;text-decoration:underline;text-decoration-style:dotted;text-decoration-color:var(--faint);text-underline-offset:3px}
.term:hover,.term:focus-visible{color:var(--ink)}

/* status / empty / foot */
.status{max-width:720px;margin:0 auto 8px;font:12px/1.6 var(--sans);color:var(--faint)}
.status code{font-family:var(--mono);font-size:11px}
.empty{max-width:720px;margin:48px auto;text-align:center;font:16px/1.7 var(--serif);color:var(--mut)}
.empty button{margin-top:14px;padding:8px 18px;border:1px solid var(--line);border-radius:2px;background:none;color:var(--text);font:13px/1.4 var(--sans);cursor:pointer;transition:border-color .15s,color .15s}
.empty button:hover{border-color:var(--ink);color:var(--ink)}
.foot{max-width:720px;margin:64px auto 0;padding-top:20px;border-top:1px solid var(--line);font:12.5px/1.7 var(--sans);color:var(--faint)}
.foot a{color:var(--mut);text-decoration:underline;text-decoration-color:var(--line-soft);text-underline-offset:2px}
.foot a:hover{color:var(--hover)}
.meta-line a{color:var(--mut);text-decoration:underline;text-decoration-color:var(--line-soft);text-underline-offset:2px}
.meta-line a:hover{color:var(--hover)}

/* noscript / doc */
.doc{max-width:720px;margin:0 auto;padding:calc(var(--nav-h) + 48px) 24px 64px}
.doc-head h1{margin:0 0 20px;font:600 36px/1.2 var(--serif);letter-spacing:-.015em;color:var(--ink)}
.doc-head p{margin:0 0 12px}
.doc-head .stat{margin:12px 0;font:14px/1.6 var(--sans);color:var(--faint)}
.meta-line{margin:0 0 12px}
.doc a{color:var(--mut);text-decoration:underline;text-decoration-color:var(--line-soft);text-underline-offset:2px}
.doc a:hover{color:var(--hover)}

/* ---------- mobile ---------- */
@media (max-width:1099px){
.icon-btn.menu-btn{display:grid}
.search{position:static;transform:none;flex:1;width:auto;max-width:none}
.sidebar{transform:translateX(-100%);transition:transform .2s;box-shadow:none;width:min(var(--side-w),84vw);padding:24px 24px 96px}
.sidebar.open{transform:none;box-shadow:0 4px 24px rgba(0,0,0,.15)}
.content{padding:calc(var(--nav-h) + 16px) 20px 96px}
.badges,.human,.rows,.src{margin-left:0}
}
@media (max-width:560px){
.title span:last-child{display:none}
.search{max-width:none}
.search kbd{display:none}
.nav-in{padding:0 16px;gap:10px}
.doc-head h1{font-size:27px}
.sec-h h2{font-size:21px}
}
Loading