diff --git a/docs/assets/logo.svg b/docs/assets/logo.svg
new file mode 100644
index 0000000..188ac30
--- /dev/null
+++ b/docs/assets/logo.svg
@@ -0,0 +1,3 @@
+
diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css
index aa219ac..feb3dee 100644
--- a/docs/stylesheets/extra.css
+++ b/docs/stylesheets/extra.css
@@ -1,75 +1,243 @@
-/* === Light scheme — devslab brand ========================================= */
+/* ---------------------------------------------------------------------------
+ * easy-paging docs — devslab brand theme
+ *
+ * Aesthetic: header blends into the body — devslab.kr style.
+ * - Light mode: white header, white body, dark text/logo
+ * - Dark mode: zinc-900 header, zinc-900 body, white text/logo
+ * - Visual separation between header and body is a hairline border only
+ * - Cyan-electric used as a small accent (links, hovers, active tab)
+ * - Pretendard typography (same family as devslab.kr)
+ * - Bilingual "EN / 한" language switcher (no CJK glyph, no generic globe)
+ *
+ * Material's default paints the header in primary color. We override the
+ * primary CSS variables to match the body bg so there's no visible color
+ * block at the top — just navigation sitting cleanly above content.
+ * --------------------------------------------------------------------------- */
+
+/* === Typography: Pretendard (same family as devslab.kr) =================== */
+@import url("https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.css");
+
+:root {
+ --md-text-font: "Pretendard",
+ -apple-system,
+ "Apple SD Gothic Neo",
+ "Noto Sans KR",
+ system-ui,
+ sans-serif;
+ --md-code-font: "JetBrains Mono", "SFMono-Regular", Menlo,
+ Consolas, "Liberation Mono", monospace;
+}
+
+/* === Palette (light scheme) ==============================================
+ * Scoped to `[data-md-color-scheme="default"]`, not `:root`. Material's
+ * own default-scheme stylesheet re-defines these variables inside that
+ * exact scope, and at equal specificity the later rule wins — meaning
+ * anything we set in `:root` was getting silently overridden, leaving
+ * body links the wrong color (and sometimes invisible against the
+ * white body bg). Same trick already worked for the dark scheme below;
+ * apply it consistently here.
+ *
+ * Header bg = body bg (white). Header text = dark (zinc-900) for contrast.
+ * Cyan-electric stays as a small accent on links/hovers.
+ * ========================================================================== */
[data-md-color-scheme="default"] {
- --md-primary-fg-color: #18181b; /* zinc-900 */
- --md-primary-fg-color--light: #27272a; /* zinc-800 */
- --md-primary-fg-color--dark: #09090b; /* zinc-950 */
+ /* Primary = header surface; match body bg so the header blends in. */
+ --md-primary-fg-color: #ffffff;
+ --md-primary-fg-color--light: #fafafa;
+ --md-primary-fg-color--dark: #f4f4f5; /* zinc-100 — used for elevated states */
+ --md-primary-bg-color: #18181b; /* zinc-900 — text/logo on the white header */
+ --md-primary-bg-color--light: rgba(24, 24, 27, 0.75);
- --md-accent-fg-color: #0891b2; /* cyan-600 */
- --md-accent-fg-color--transparent: rgba(8, 145, 178, 0.12);
+ /* Accent — small surfaces (links, hovers, selection)
+ *
+ * cyan-700 instead of cyan-500: against a white body the brighter
+ * cyan-500 lands at ~2.4:1 contrast — under WCAG AA's 4.5:1 minimum
+ * for normal text. cyan-700 (#0e7490) gives ~5.4:1 which passes AA
+ * while keeping the "electric" feel.
+ */
+ --md-accent-fg-color: #0e7490; /* cyan-700 — AA-readable on white */
+ --md-accent-fg-color--transparent: rgba(14, 116, 144, 0.10);
+
+ /* Body link color — decouple from primary, follow accent. */
--md-typeset-a-color: #0e7490; /* cyan-700 */
}
/* === Dark scheme — header blends into the zinc-900 body =================== */
[data-md-color-scheme="slate"] {
- --md-primary-fg-color: #18181b; /* zinc-900 — same as body bg */
- --md-primary-fg-color--light: #27272a; /* zinc-800 */
+ --md-primary-fg-color: #18181b; /* zinc-900 — same as body bg below */
+ --md-primary-fg-color--light: #27272a; /* zinc-800 — slightly raised */
--md-primary-fg-color--dark: #09090b; /* zinc-950 */
+ --md-primary-bg-color: #fafafa; /* white text/logo on dark header */
+ --md-primary-bg-color--light: rgba(250, 250, 250, 0.75);
- --md-accent-fg-color: #22d3ee; /* cyan-400 — pops on dark */
+ --md-accent-fg-color: #22d3ee; /* cyan-400 — pops on dark */
--md-accent-fg-color--transparent: rgba(34, 211, 238, 0.12);
+
+ /* Body links on dark — slightly brighter cyan for readability. */
--md-typeset-a-color: #22d3ee; /* cyan-400 */
+ /* Body bg matches devslab.kr's actual zinc-900. */
--md-default-bg-color: #18181b;
--md-default-bg-color--light: #27272a;
--md-default-bg-color--lighter: #3f3f46;
- --md-code-bg-color: #09090b; /* zinc-950 */
+ --md-code-bg-color: #09090b; /* zinc-950 — code blocks pop */
}
-/* === Typeset links ========================================================= */
+/* === Link affordance ====================================================
+ * Always-on subtle underline + cyan color. Color alone wasn't enough in
+ * sections where a link follows bolded descriptive text (e.g. the
+ * "Where to go next" list on the landing page) — the visual weight of
+ * the bold text drowned out the cyan link. The underline restores the
+ * "this is clickable" affordance without relying purely on color.
+ *
+ * Underline is dim at rest and thickens on hover for feedback.
+ * Offset of 0.18em keeps the underline from clipping descender glyphs
+ * (g, j, p, q, y).
+ * ========================================================================== */
.md-typeset a {
+ font-weight: 500;
text-decoration: underline;
text-decoration-thickness: 0.06em;
- text-underline-offset: 0.2em;
- transition: text-decoration-thickness 150ms;
+ text-underline-offset: 0.18em;
+}
+.md-typeset a:hover,
+.md-typeset a:focus {
+ text-decoration: underline;
+ text-decoration-thickness: 0.12em;
+}
+
+/* Code-styled link text — Material's has its own (dark) color which
+ * blocks the link's cyan from showing through. Force inside any body
+ * link to inherit the link color, so [`SomeFile.md`](...) reads as a link. */
+.md-typeset a code {
+ color: inherit;
}
-.md-typeset a:hover { text-decoration-thickness: 0.12em; }
-/* === Active nav link ======================================================= */
-.md-nav__item.md-nav__item--active > .md-nav__link {
+/* Sidebar nav: active page should be obviously cyan. Material defaults
+ * to --md-typeset-a-color which is cyan-700, but the chrome around it
+ * (subtle background, weight) made it easy to miss in light mode. Bump
+ * weight and remove any opacity so the active page is unmistakable. */
+.md-nav__link--active {
color: var(--md-accent-fg-color);
font-weight: 700;
+ opacity: 1;
}
-/* === Buttons =============================================================== */
-.md-button {
- border: 1px solid var(--md-accent-fg-color);
+/* === Buttons ({ .md-button } / { .md-button--primary }) ===================
+ * Same root cause as the body links: Material's default button colors
+ * inherit from primary, which we set to match body bg. Result: outlined
+ * buttons (.md-button) were transparent-on-transparent until hover, and
+ * the primary CTA looked muddy. Rewire to the accent (cyan) family so the
+ * three landing-page buttons (Get Started / GitHub / Maven Central) all
+ * stay visible at rest.
+ * ========================================================================== */
+
+/* Outlined: cyan border + cyan text, fills on hover.
+ * Explicit text-decoration: none — buttons shouldn't inherit the
+ * always-on underline that body links now have. */
+.md-typeset .md-button {
color: var(--md-accent-fg-color);
+ border-color: var(--md-accent-fg-color);
+ text-decoration: none;
}
-.md-button:hover {
- background-color: var(--md-accent-fg-color);
- color: var(--md-default-bg-color);
-}
-.md-button--primary {
+.md-typeset .md-button:hover,
+.md-typeset .md-button:focus {
background-color: var(--md-accent-fg-color);
+ color: #ffffff;
border-color: var(--md-accent-fg-color);
- color: var(--md-default-bg-color);
+ text-decoration: none; /* override the global link-hover underline */
}
-/* === Header & tabs — hairline border ====================================== */
-.md-header,
-.md-tabs { box-shadow: 0 1px 0 rgba(0, 0, 0, 0.06); }
-[data-md-color-scheme="slate"] .md-header,
-[data-md-color-scheme="slate"] .md-tabs { box-shadow: 0 1px 0 rgba(255, 255, 255, 0.06); }
+/* Primary CTA: filled cyan, white text. Use cyan-500 (brighter) for the
+ * fill since white text on it has plenty of contrast — the dim cyan-700
+ * we use for body text would look muddy here. */
+.md-typeset .md-button--primary {
+ background-color: #06b6d4; /* cyan-500 — vibrant fill */
+ color: #ffffff;
+ border-color: #06b6d4;
+ text-decoration: none;
+}
+.md-typeset .md-button--primary:hover,
+.md-typeset .md-button--primary:focus {
+ background-color: #0891b2; /* cyan-600 — darker on hover */
+ border-color: #0891b2;
+ color: #ffffff;
+ text-decoration: none;
+}
-/* === Bilingual language switcher — text instead of a globe glyph ========== */
-.md-header__option .md-select__button::before {
- content: "EN";
- font-size: 0.875rem;
+/* Dark-mode hover: use a brighter cyan since the base is already brighter,
+ * with dark text for contrast. */
+[data-md-color-scheme="slate"] .md-typeset .md-button--primary:hover,
+[data-md-color-scheme="slate"] .md-typeset .md-button--primary:focus {
+ background-color: #67e8f9; /* cyan-300 */
+ border-color: #67e8f9;
+ color: #18181b;
+}
+[data-md-color-scheme="slate"] .md-typeset .md-button:hover,
+[data-md-color-scheme="slate"] .md-typeset .md-button:focus {
+ color: #18181b; /* dark text on bright-cyan filled hover */
+}
+
+/* === Hairline separation (the only visual cue the header exists) ========== */
+.md-header {
+ box-shadow: 0 1px 0 rgba(0, 0, 0, 0.06);
+}
+[data-md-color-scheme="slate"] .md-header {
+ box-shadow: 0 1px 0 rgba(255, 255, 255, 0.06);
+}
+
+/* === Tabs bar (sub-nav under header) — same blending treatment ============ */
+.md-tabs {
+ border-bottom: 1px solid rgba(0, 0, 0, 0.06);
+}
+[data-md-color-scheme="slate"] .md-tabs {
+ border-bottom-color: rgba(255, 255, 255, 0.06);
+}
+.md-tabs__link--active {
+ color: var(--md-accent-fg-color);
+ opacity: 1;
+}
+.md-tabs__link:hover {
+ color: var(--md-accent-fg-color) !important;
+ opacity: 1;
+}
+
+/* === Brand logo — invert only in dark mode ================================
+ * logo-mark.svg ships as #0a0a0a (dark). That's perfect on the white light-
+ * mode header. In dark mode, force it white with brightness(0)+invert(1).
+ * ========================================================================== */
+[data-md-color-scheme="slate"] .md-header__button.md-logo img,
+[data-md-color-scheme="slate"] .md-header__button.md-logo svg {
+ filter: brightness(0) invert(1);
+}
+
+/* === Language switcher: bilingual text label ===============================
+ * Replace Material's "A + 文" CJK glyph with the current page language as
+ * a short badge ("EN" or "한"). The 文 reads as "Chinese" to most Korean
+ * visitors which isn't what we want for a language-switcher affordance.
+ * ========================================================================== */
+.md-header [aria-label="Select language"] > svg,
+.md-header [aria-label*="언어"] > svg {
+ display: none;
+}
+
+.md-header [aria-label="Select language"]::before,
+.md-header [aria-label*="언어"]::before {
font-weight: 700;
+ font-size: 0.85rem;
+ letter-spacing: 0.04em;
+ line-height: 1;
+ padding: 0 0.15rem;
+ display: inline-block;
+ vertical-align: middle;
}
-[data-md-color-scheme="slate"] .md-header__option .md-select__button::before {
+
+html[lang="en"] .md-header [aria-label="Select language"]::before,
+html[lang="en"] .md-header [aria-label*="언어"]::before {
+ content: "EN";
+}
+
+html[lang="ko"] .md-header [aria-label="Select language"]::before,
+html[lang="ko"] .md-header [aria-label*="언어"]::before {
content: "한";
- font-size: 1rem;
- font-weight: 700;
}
-.md-header__option .md-select__button svg { display: none; }
diff --git a/mkdocs.yml b/mkdocs.yml
index c97d004..daca54b 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -15,6 +15,8 @@ docs_dir: docs
theme:
name: material
language: en
+ logo: assets/logo.svg
+ favicon: assets/logo.svg
features:
- navigation.tabs
- navigation.sections
@@ -89,16 +91,25 @@ markdown_extensions:
- footnotes
- tables
- pymdownx.details
- - pymdownx.superfences
+ - pymdownx.superfences:
+ custom_fences:
+ - name: mermaid
+ class: mermaid
+ format: !!python/name:pymdownx.superfences.fence_code_format
- pymdownx.tabbed:
alternate_style: true
- pymdownx.highlight:
anchor_linenums: true
+ line_spans: __span
+ pygments_lang_class: true
- pymdownx.inlinehilite
- pymdownx.snippets:
base_path:
- .
check_paths: true
+ - pymdownx.emoji:
+ emoji_index: !!python/name:material.extensions.emoji.twemoji
+ emoji_generator: !!python/name:material.extensions.emoji.to_svg
- toc:
permalink: true
toc_depth: 3