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
Empty file removed .github/workflows/cd
Empty file.
12 changes: 7 additions & 5 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Actions are pinned to commits; the token is not left in .git/config for later steps.
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
persist-credentials: false

- name: Refresh GitHub stats
if: github.event_name != 'push'
Expand All @@ -31,12 +34,11 @@ jobs:
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add data/stats.json
git commit -m "Update GitHub stats"
git push
git push "https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" HEAD:main
fi

- name: Check data files
run: |
for f in data/*.json; do python3 -m json.tool "$f" > /dev/null || { echo "Invalid JSON: $f"; exit 1; }; done
run: python3 scripts/check_data.py

- name: Build Builder pages and badges
run: python3 scripts/build_builders.py
Expand All @@ -47,7 +49,7 @@ jobs:
cp -r index.html article.html game.html lab.html 404.html .nojekyll assets data articles builders badges _site/

- name: Publish to gh-pages
uses: peaceiris/actions-gh-pages@v4
uses: peaceiris/actions-gh-pages@84c30a85c19949d7eee79c4ff27748b70285e453 # v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./_site
Expand Down
7 changes: 5 additions & 2 deletions 404.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,25 @@
<html lang="ko">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://www.googletagmanager.com; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com; font-src 'self' data: https://cdn.jsdelivr.net https://fonts.gstatic.com; img-src 'self' data: blob: https://github.com https://avatars.githubusercontent.com https://*.google-analytics.com https://*.googletagmanager.com; connect-src 'self' https://api.github.com https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com; object-src 'none'; base-uri 'self'; form-action 'self'">
<script src="/assets/js/ga.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>페이지를 찾을 수 없습니다 | Causal Inference Hub</title>
<link rel="icon" href="/assets/img/team-mark.png">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pretendard@1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css" integrity="sha384-/uC/GIvKJ/9RN6i1izHoBY9ubYo60OAsQRNQ3NRFiNsN+N/MVQJHD5OYcZz6wvwn" crossorigin="anonymous">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500&display=swap">
<link rel="stylesheet" href="/assets/css/hub.css">
</head>
<body>
<a class="skip" href="#main">본문으로 건너뛰기</a>
<header class="site-header">
<div class="wrap header-row">
<a class="brand" href="/"><img class="brand-logo" src="/assets/img/team-mark.png" alt="" width="34" height="34"><span class="brand-text"><span class="brand-name">Causal Inference Hub</span><span class="brand-sub">가짜연구소 인과추론팀</span></span></a>
<nav class="site-nav" aria-label="주요 메뉴"><a href="/#curriculum">커리큘럼</a><a href="/game.html">게임</a><a href="/lab.html">실험실</a><a href="/builders/">Builder</a><a href="/#join">참여하기</a></nav>
<button type="button" class="pass-btn" data-passport aria-label="학습 여권 열기">여권 <b data-passport-count>0/9</b></button>
</div>
</header>
<main class="reader">
<main class="reader" id="main">
<div class="wrap">
<h1>페이지를 찾을 수 없습니다</h1>
<p class="meta">예전 블로그 주소로 들어오셨다면 글은 읽을거리로 옮겨졌습니다.</p>
Expand Down
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

## 내용 더하기

PR을 올리기 전에 `python3 scripts/check_data.py`를 실행해 주세요. 배포 워크플로도 같은 검사를 하며, 실패하면 배포하지 않습니다. 링크는 https(또는 http)나 사이트 안의 경로만, id는 영문·숫자·하이픈만, 기수·인원은 정수만 허용합니다. 글(`articles/*.md`)의 HTML은 화면에 그릴 때 정리(sanitize)되어 스크립트가 실행되지 않습니다.

사이트는 빌드 도구 없이 `data/` 폴더의 JSON 파일을 읽어 그립니다. 대부분의 수정은 파일 하나를 고치는 PR이면 됩니다.

| 파일 | 고치는 경우 |
Expand Down
15 changes: 5 additions & 10 deletions article.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
<html lang="ko">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://www.googletagmanager.com; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com; font-src 'self' data: https://cdn.jsdelivr.net https://fonts.gstatic.com; img-src 'self' data: blob: https://github.com https://avatars.githubusercontent.com https://*.google-analytics.com https://*.googletagmanager.com; connect-src 'self' https://api.github.com https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com; object-src 'none'; base-uri 'self'; form-action 'self'">
<script src="assets/js/ga.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>읽을거리 | Causal Inference Hub</title>
<meta name="description" content="가짜연구소 인과추론팀이 쓴 글과 독자를 위한 자료입니다.">
<link rel="icon" href="assets/img/team-mark.png">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pretendard@1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css" integrity="sha384-/uC/GIvKJ/9RN6i1izHoBY9ubYo60OAsQRNQ3NRFiNsN+N/MVQJHD5OYcZz6wvwn" crossorigin="anonymous">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500&display=swap">
<link rel="stylesheet" href="assets/css/hub.css">
</head>
Expand All @@ -19,14 +20,7 @@
<img class="brand-logo" src="assets/img/team-mark.png" alt="" width="34" height="34">
<span class="brand-text"><span class="brand-name">Causal Inference Hub</span><span class="brand-sub">가짜연구소 인과추론팀</span></span>
</a>
<nav class="site-nav" aria-label="주요 메뉴">
<a href="./#cohort">이번 기수</a>
<a href="./#builders">Builder</a>
<a href="./#repos">오픈소스</a>
<a href="./#start">학습 경로</a>
<a href="./#reading">읽을거리</a>
<a href="./#join">참여하기</a>
</nav>
<nav class="site-nav" aria-label="주요 메뉴"><a href="./#curriculum">커리큘럼</a><a href="game.html">게임</a><a href="lab.html">실험실</a><a href="builders/">Builder</a><a href="./#join">참여하기</a></nav>
<button type="button" class="pass-btn" data-passport aria-label="학습 여권 열기">여권 <b data-passport-count>0/9</b></button>
</div>
</header>
Expand Down Expand Up @@ -56,7 +50,8 @@ <h2 id="more-title">다른 글</h2>
</div>
</footer>

<script src="https://cdn.jsdelivr.net/npm/marked@12.0.2/marked.min.js"></script>
<script src="assets/vendor/marked-12.0.2.min.js"></script>
<script src="assets/vendor/purify-3.4.16.min.js"></script>
<script src="assets/js/article.js" defer></script>
<script src="assets/js/track.js" defer></script>
<script src="assets/js/passport.js" defer></script>
Expand Down
67 changes: 50 additions & 17 deletions assets/css/hub.css
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ body {
margin: 0; background: var(--paper); color: var(--ink);
font-family: var(--f-body); font-size: 16px; line-height: 1.6;
-webkit-font-smoothing: antialiased; word-break: keep-all;
min-height: 100vh; min-height: 100dvh;
}
img { max-width: 100%; }
a { color: var(--accent); text-underline-offset: 3px; }
Expand Down Expand Up @@ -93,7 +94,7 @@ body::before { content: ""; position: fixed; inset: 0 0 auto; height: env(safe-a
.site-nav::-webkit-scrollbar { display: none; }
.site-nav a { color: var(--muted); text-decoration: none; padding: 8px 10px; border-radius: 8px; white-space: nowrap; font-size: 15px; font-weight: 500; }
.site-nav a:hover { color: var(--ink); background: var(--surface-2); }
.site-nav a[aria-current="true"] { color: var(--ink); background: var(--surface-2); font-weight: 700; }
.site-nav a[aria-current="true"], .site-nav a[aria-current="page"] { color: var(--ink); background: var(--surface-2); font-weight: 700; }
@media (max-width: 820px) {
.header-row { flex-wrap: wrap; gap: 2px; padding-block: 8px; }
.site-nav { margin-left: 0; width: 100%; }
Expand Down Expand Up @@ -249,7 +250,8 @@ body::before { content: ""; position: fixed; inset: 0 0 auto; height: env(safe-a
/* Curriculum shelf */
.goal-row { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; }
.goal-q { font-weight: 700; }
#route-note { margin-top: 10px; min-height: 1.6em; }
#route-note { margin-top: 10px; min-height: 1.6em; scroll-margin-top: 96px; }
#route-note:empty { min-height: 0; margin-top: 0; }
.shelf-bar { display: flex; align-items: flex-end; gap: 16px; margin: 8px 0 12px; }
.shelf-nav { display: flex; gap: 6px; flex-shrink: 0; }
.shelf-nav button { width: 40px; height: 40px; display: grid; place-items: center; border-radius: 50%; border: 1.5px solid var(--rule); background: var(--surface); color: var(--ink); cursor: pointer; }
Expand Down Expand Up @@ -314,8 +316,8 @@ body::before { content: ""; position: fixed; inset: 0 0 auto; height: env(safe-a
.shelf-nav { display: none; }
.chap { width: min(66vw, 240px); }
.chapters .nm { display: none; }
.goal-row .route-chips { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; margin-inline: calc(-1 * var(--gutter-l)) calc(-1 * var(--gutter-r)); padding-inline: var(--gutter-l) var(--gutter-r); }
.goal-row .route-chips .chip { flex-shrink: 0; }
.goal-row .route-chips { gap: 6px; }
.goal-row .route-chips .chip { font-size: 13.5px; padding: 6px 12px; }
}

/* This week (curriculum tab): spotlight, recent updates, continue */
Expand Down Expand Up @@ -474,12 +476,22 @@ body::before { content: ""; position: fixed; inset: 0 0 auto; height: env(safe-a
.event .ppl b { font-family: var(--f-display); font-weight: 800; letter-spacing: -.02em; font-size: 24px; color: var(--ink); }
.event .bar { height: 6px; border-radius: 3px; background: var(--surface-2); overflow: hidden; margin-top: 4px; }
.event .bar i { display: block; height: 100%; background: var(--accent); }
.event .btn.sm { margin-top: auto; align-self: flex-start; }
@media (max-width: 560px) {
.events { grid-template-columns: minmax(0, 1fr); gap: 8px; }
.event { display: grid; grid-template-columns: 56px minmax(0, 1fr); gap: 2px 12px; padding: 14px; }
.event .ord { grid-row: span 4; font-size: 26px; }
.event .ppl, .event .btn.sm { margin-top: 4px; }
.event .soon-tag { justify-self: start; }
}

/* Goals */
.route-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.chip { font: inherit; font-size: 14px; font-weight: 600; cursor: pointer; border: 1.5px solid var(--rule); background: var(--surface); color: var(--ink); padding: 7px 14px; border-radius: 999px; min-height: 40px; display: inline-flex; align-items: center; gap: 6px; }
.chip:hover { border-color: var(--ink); }
.chip[aria-checked="true"] { background: var(--ink); color: var(--surface); border-color: var(--ink); }
.chip.quiz-chip { border-style: dashed; border-color: color-mix(in srgb, var(--accent) 55%, var(--rule)); background: var(--accent-soft); color: var(--accent); }
.chip.quiz-chip:hover { border-style: solid; border-color: var(--accent); }
.route-note { color: var(--muted); font-size: 14px; }

.map-card { background: var(--surface); border: 1px solid var(--rule); border-radius: 16px; padding: 12px 0 16px; min-width: 0; }
Expand Down Expand Up @@ -617,6 +629,7 @@ dialog.station::backdrop { background: rgba(10, 14, 20, .55); }
.p-timeline li { position: relative; display: grid; grid-template-columns: 52px minmax(0, 1fr); gap: 10px; padding: 8px 0 8px 18px; }
.p-timeline li::before { content: ""; position: absolute; left: -7px; top: 14px; width: 12px; height: 12px; border-radius: 50%; background: var(--c); box-shadow: 0 0 0 3px var(--surface); }
.p-timeline li.r::before { box-sizing: border-box; background: var(--surface); border: 2px solid var(--c); }
.tl-c { white-space: nowrap; }
.tl-c b { display: block; font-size: 15px; font-weight: 800; font-variant-numeric: tabular-nums; }
.tl-c span { font-size: 11.5px; color: var(--muted); }
.tl-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; font-size: 14px; }
Expand Down Expand Up @@ -658,14 +671,6 @@ dialog.station::backdrop { background: rgba(10, 14, 20, .55); }
.notice { background: var(--surface); border: 1px solid var(--rule); border-radius: 12px; padding: 18px; margin-bottom: 16px; }

/* Start-point quiz entry, quiz dialog, finish card, Builder share */
.start-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px; margin: -2px 0 10px; }
.start-btn { font: inherit; font-size: 14px; font-weight: 600; display: inline-flex; align-items: center; gap: 8px; min-height: 40px; padding: 0 14px; border-radius: 999px; border: 1.5px dashed color-mix(in srgb, var(--accent) 55%, var(--rule)); background: var(--accent-soft); color: var(--ink); cursor: pointer; }
.start-btn b { color: var(--accent); font-weight: 800; }
.start-btn:hover { border-style: solid; border-color: var(--accent); }
.start-btn:focus-visible, .start-game:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.start-game { font-size: 14px; font-weight: 600; color: var(--muted); text-decoration: none; }
.start-game::after { content: " ›"; }
.start-game:hover { color: var(--ink); text-decoration: underline; }
.btn.sm { min-height: 36px; padding: 0 12px; font-size: 14px; margin-top: 0; }
button.btn { font: inherit; font-weight: 600; cursor: pointer; border: 0; }
button.btn.ghost { border: 1.5px solid var(--rule); }
Expand Down Expand Up @@ -828,6 +833,11 @@ button.btn.sm { font-size: 14px; }
.daily { display: grid; grid-template-columns: 220px minmax(0, 1fr); gap: 20px 28px; padding: 20px 24px; border-radius: 18px; background: var(--surface); border: 1.5px solid color-mix(in srgb, var(--accent) 30%, var(--rule)); }
.daily-head h2 { font-size: 18px; letter-spacing: -.01em; }
.daily-date { font-size: 13px; color: var(--muted); margin-top: 2px; }
.daily-warm { display: flex; flex-wrap: wrap; gap: 4px 12px; margin-top: 12px; font-size: 13.5px; font-weight: 700; }
.daily-warm span { flex-basis: 100%; font-size: 12px; font-weight: 600; color: var(--muted); }
.daily-warm a { text-decoration: none; }
.daily-warm a::after { content: " ›"; }
.daily-warm a:hover { text-decoration: underline; }
.daily-streak { display: inline-block; margin-top: 10px; font-size: 13px; font-weight: 700; padding: 3px 10px; border-radius: 999px; background: #FFF1C2; color: #6B4B00; }
.daily-streak.quiet { background: var(--surface-2); color: var(--muted); font-weight: 600; }
.daily-body { display: grid; gap: 10px; min-width: 0; }
Expand All @@ -845,7 +855,7 @@ button.btn.sm { font-size: 14px; }
.daily-why { font-size: 14.5px; }
.daily-more { display: flex; flex-wrap: wrap; gap: 6px 16px; font-size: 14px; font-weight: 700; }
.daily-next { font-size: 12.5px; color: var(--muted); }
@media (max-width: 760px) { .daily { grid-template-columns: minmax(0, 1fr); padding: 18px; } .daily-opts button { flex: 1 1 100%; } }
@media (max-width: 760px) { .daily { grid-template-columns: minmax(0, 1fr); padding: 18px; } .daily-warm span { flex-basis: auto; } .daily-opts button { flex: 1 1 100%; } }

/* Confounding lab (lab.html) */
.lab { padding-block: 32px 72px; }
Expand Down Expand Up @@ -900,19 +910,42 @@ button.btn.sm { font-size: 14px; }
.lab-legend .dot { width: 10px; height: 10px; border-radius: 50%; }
.lab-legend .dot.on { background: var(--l-main); }
.lab-legend .dot.off { background: var(--l-exp); }
.lab-legend .ln { width: 16px; border-top: 2px solid var(--muted); }
.lab-legend .ln.dash { border-top-style: dashed; }
.lab-legend { flex-wrap: wrap; gap: 4px 16px; }
.lab-learn { margin-top: 28px; max-width: 820px; }
.lab-learn h2 { font-size: 18px; margin-bottom: 10px; }
.lab-learn h2 { font-size: 18px; display: inline; }
.lab-learn summary { cursor: pointer; list-style: none; display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; margin-bottom: 10px; }
.lab-learn summary::-webkit-details-marker { display: none; }
.lab-learn summary::before { content: "▸"; color: var(--muted); }
.lab-learn details[open] summary::before { content: "▾"; }
.lab-learn summary span { font-size: 13px; color: var(--muted); }
.lab-learn details[open] summary span { display: none; }
.lab-learn summary:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; border-radius: 6px; }
.lab > .wrap > .lab-challenge { margin-top: 18px; max-width: 46em; font-size: 15px; }
.lab-learn ol { margin: 0 0 18px; padding-left: 20px; display: grid; gap: 10px; font-size: 15px; }
.lab-learn a { font-weight: 700; margin-left: 4px; }
@media (max-width: 900px) { .lab-grid { grid-template-columns: minmax(0, 1fr); } }
@media (max-width: 900px) {
.lab-grid { grid-template-columns: minmax(0, 1fr); }
/* keep the chart in view while the sliders move */
.lab-chart { order: -1; position: sticky; top: var(--header-h, 56px); z-index: 5; padding: 12px 14px; box-shadow: 0 8px 20px rgba(20, 22, 43, .08); }
.lab-chart h2 { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.lab-chart-sub { display: none; }
.lab-legend { margin-top: 4px; font-size: 12px; }
}
.toast > span:nth-child(2) { flex: 1; min-width: 0; }
@media (max-width: 900px) { .toast { bottom: auto; top: 72px; transform: translate(-50%, -180%); } .toast.show { transform: translate(-50%, 0); } }
@media (max-width: 900px) { .toast { bottom: auto; top: 96px; transform: translate(-50%, -180%); } .toast.show { transform: translate(-50%, 0); } }
.lab-ctl.off input[type=range] { opacity: .45; }
.lab-ctl.off small { color: var(--live); font-weight: 600; }
.lab-note { font-size: 12.5px; color: var(--muted); }
#lab-svg .fit-l { stroke-width: 1.5; opacity: .9; }
#lab-svg .fit-l.on { stroke: var(--l-main); }
#lab-svg .fit-l.off { stroke: var(--l-exp); }
#lab-svg .fit-gap { stroke: var(--live); stroke-width: 3; }
#lab-svg .fit-t { fill: var(--live); font-size: 12px; font-weight: 700; paint-order: stroke; stroke: var(--surface); stroke-width: 4px; }
#lab-svg .fit-t { fill: var(--live); font-size: 13px; font-weight: 800; paint-order: stroke; stroke: var(--surface); stroke-width: 5px; stroke-linejoin: round; }
@media (max-width: 900px) { .lab-chart { order: -1; } }

/* way back from a repo to the Builder it was opened from */
.back-person { font: inherit; font-size: 13.5px; font-weight: 700; align-self: flex-start; margin: 0 0 8px; padding: 4px 10px; border-radius: 999px; border: 1px solid var(--rule); background: var(--surface-2); color: var(--ink); cursor: pointer; }
.back-person:hover { border-color: var(--ink); }
.back-person:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
Loading