diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 430843ff0..6d7bd6ba0 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -86,6 +86,9 @@ jobs: - name: Copy install script to site root run: cp scripts/install.sh _site/install.sh + - name: Write CNAME for custom domain + run: echo "beamtalk.dev" > _site/CNAME + - name: Upload Pages artifact uses: actions/upload-pages-artifact@v4 diff --git a/CLAUDE.md b/CLAUDE.md index a5cb61df9..5d9998696 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,7 +10,7 @@ Beamtalk is a Smalltalk/Newspeak-inspired language compiling to BEAM via Rust. T - **Structured errors:** Use `#beamtalk_error{}` for all user-facing/public API errors. Internal runtime helpers may use `{ok, Value} | {error, Reason}` if translated at public boundaries. - **Codegen:** All Core Erlang codegen MUST use `Document` / `docvec!` API. **NEVER use `format!()` or string concatenation to produce Core Erlang fragments — not even for "simple" atoms, arities, or map keys.** This rule has been violated repeatedly and required a dedicated cleanup effort (BT-875). Do not reintroduce violations. - **Erlang logging:** Use OTP logger macros (`?LOG_ERROR`, etc.), never `io:format` or `logger:error()`. -- **License headers:** All source files need `Copyright 2026 James Casey` / `SPDX-License-Identifier: Apache-2.0`. +- **License headers:** All source **code** files (`.rs`, `.erl`, `.bt`, `.hrl`) need `Copyright 2026 James Casey` / `SPDX-License-Identifier: Apache-2.0`. Do **not** add license headers to `.md` files. - **Implicit returns:** Use `^` ONLY for early returns, never on last expression. - **Test assertions:** Every expression in test files MUST have a `// =>` assertion (even `// => _`). No assertion = no execution. diff --git a/crates/beamtalk-cli/src/commands/doc/assets.rs b/crates/beamtalk-cli/src/commands/doc/assets.rs index fbbc29941..d5a8f3a67 100644 --- a/crates/beamtalk-cli/src/commands/doc/assets.rs +++ b/crates/beamtalk-cli/src/commands/doc/assets.rs @@ -15,77 +15,125 @@ use super::renderer::method_anchor; /// CSS stylesheet content for generated documentation. const CSS_STYLESHEET: &str = r":root { - --bg: #ffffff; - --fg: #1e1e2e; - --fg-muted: #6c6f85; - --accent: #1e66f5; - --accent-hover: #1443a6; - --accent-bg: #eff4fc; - --border: #dce0e8; - --code-bg: #f5f5f7; - --method-bg: #fafbfd; - --sidebar-bg: #f8f9fb; - --sidebar-w: 260px; - --header-h: 56px; - --shadow: 0 1px 3px rgba(0,0,0,0.08); + --bg: #FAFAF8; + --fg: #111827; + --fg-muted: #6B7280; + --accent: #6366F1; + --accent-hover: #4F46E5; + --accent-bg: #EEF2FF; + --border: #E5E7EB; + --code-bg: #18181B; + --code-fg: #E4E4E7; + --inline-code-bg: #F3F4F6; + --method-bg: #FFFFFF; + --sidebar-bg: #F9F9F7; + --nav-bg: #FFFFFF; + --sidebar-w: 272px; + --nav-h: 60px; + --shadow: 0 1px 3px rgba(0,0,0,0.07); + --shadow-md: 0 4px 12px rgba(0,0,0,0.12); --radius: 8px; - - /* Badge colors */ - --badge-abstract-bg: #fef3e2; - --badge-abstract-fg: #b35c00; - - /* Syntax highlighting */ - --hl-keyword: #8839ef; - --hl-string: #40a02b; - --hl-number: #fe640b; - --hl-comment: #9ca0b0; - --hl-selector: #1e66f5; - --hl-symbol: #df8e1d; - --hl-class: #ea76cb; - --hl-self: #d20f39; + --badge-abstract-bg: #FEF3C7; + --badge-abstract-fg: #92400E; + /* Syntax highlighting — tuned for dark code blocks */ + --hl-keyword: #C084FC; + --hl-string: #86EFAC; + --hl-number: #FCA5A5; + --hl-comment: #71717A; + --hl-selector: #93C5FD; + --hl-symbol: #FCD34D; + --hl-class: #F9A8D4; + --hl-self: #F87171; } @media (prefers-color-scheme: dark) { :root { - --bg: #1e1e2e; - --fg: #cdd6f4; - --fg-muted: #a6adc8; - --accent: #89b4fa; - --accent-hover: #b4d0fb; - --accent-bg: #313244; - --border: #45475a; - --code-bg: #313244; - --method-bg: #24273a; - --sidebar-bg: #181825; - --shadow: 0 1px 3px rgba(0,0,0,0.3); - - --badge-abstract-bg: #3d2e1a; - --badge-abstract-fg: #fab387; - - --hl-keyword: #cba6f7; - --hl-string: #a6e3a1; - --hl-number: #fab387; - --hl-comment: #6c7086; - --hl-selector: #89b4fa; - --hl-symbol: #f9e2af; - --hl-class: #f5c2e7; - --hl-self: #f38ba8; + --bg: #0A0A09; + --fg: #FAFAF8; + --fg-muted: #9CA3AF; + --accent: #818CF8; + --accent-hover: #A5B4FC; + --accent-bg: #1E1B4B; + --border: #27272A; + --code-bg: #111110; + --code-fg: #E4E4E7; + --inline-code-bg: #27272A; + --method-bg: #18181B; + --sidebar-bg: #111110; + --nav-bg: #0A0A09; + --shadow: 0 1px 3px rgba(0,0,0,0.4); + --shadow-md: 0 4px 12px rgba(0,0,0,0.5); + --badge-abstract-bg: #3D2B08; + --badge-abstract-fg: #FCD34D; } } * { margin: 0; padding: 0; box-sizing: border-box; } body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif; + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; + font-size: 15px; color: var(--fg); background: var(--bg); - line-height: 1.65; + line-height: 1.7; +} + +/* --- Top navigation bar --- */ +.top-nav { + position: fixed; + top: 0; left: 0; right: 0; + height: var(--nav-h); + background: var(--nav-bg); + border-bottom: 1px solid var(--border); + display: flex; + align-items: center; + padding: 0 1.5rem; + z-index: 100; + gap: 1.5rem; +} + +.nav-logo { + font-weight: 700; + font-size: 1.05rem; + color: var(--fg); + text-decoration: none; + letter-spacing: -0.02em; + flex-shrink: 0; +} +.nav-logo:hover { color: var(--fg); text-decoration: none; } + +.nav-links { + display: flex; + align-items: center; + gap: 0.25rem; + flex: 1; +} + +.nav-links a { + font-size: 0.875rem; + font-weight: 500; + color: var(--fg-muted); + text-decoration: none; + padding: 0.35rem 0.7rem; + border-radius: 6px; + transition: background 0.12s, color 0.12s; } +.nav-links a:hover { + background: var(--accent-bg); + color: var(--accent); + text-decoration: none; +} +.nav-links a.nav-github { + margin-left: auto; + border: 1px solid var(--border); +} +.nav-links a.nav-github:hover { border-color: var(--accent); } /* --- Layout --- */ .page-wrapper { display: flex; min-height: 100vh; + padding-top: var(--nav-h); } .sidebar { @@ -93,7 +141,7 @@ body { background: var(--sidebar-bg); border-right: 1px solid var(--border); position: fixed; - top: 0; + top: var(--nav-h); left: 0; bottom: 0; overflow-y: auto; @@ -102,64 +150,55 @@ body { transition: transform 0.2s; } -.sidebar-header { - padding: 0 1.25rem 1rem; +.sidebar-search-wrap { + padding: 0 1rem 0.75rem; border-bottom: 1px solid var(--border); - margin-bottom: 0.75rem; -} - -.sidebar-header h2 { - font-size: 1.1rem; - font-weight: 700; - margin: 0; -} - -.sidebar-header h2 a { - color: var(--fg); - text-decoration: none; + margin-bottom: 0.5rem; } .sidebar-search { display: block; width: 100%; - margin-top: 0.6rem; padding: 0.45rem 0.7rem; border: 1px solid var(--border); border-radius: 6px; - font-size: 0.85rem; + font-size: 0.83rem; background: var(--bg); color: var(--fg); outline: none; + font-family: inherit; } - .sidebar-search:focus { border-color: var(--accent); - box-shadow: 0 0 0 2px var(--accent-bg); + box-shadow: 0 0 0 3px var(--accent-bg); } -.sidebar-nav { list-style: none; } - -.sidebar-nav li { - margin: 0; +.sidebar-section-label { + padding: 0.75rem 1.25rem 0.3rem; + font-size: 0.69rem; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--fg-muted); } +.sidebar-nav { list-style: none; } +.sidebar-nav li { margin: 0; } .sidebar-nav a { display: block; padding: 0.3rem 1.25rem; - font-size: 0.88rem; - color: var(--fg); + font-size: 0.875rem; + color: var(--fg-muted); text-decoration: none; - border-left: 3px solid transparent; - transition: background 0.15s, border-color 0.15s; + transition: background 0.1s, color 0.1s; } - .sidebar-nav a:hover { background: var(--accent-bg); + color: var(--accent); + text-decoration: none; } - .sidebar-nav a.active { background: var(--accent-bg); - border-left-color: var(--accent); color: var(--accent); font-weight: 600; } @@ -167,15 +206,14 @@ body { .main-content { margin-left: var(--sidebar-w); flex: 1; - max-width: 900px; - padding: 2rem 2.5rem; + max-width: 860px; + padding: 2.5rem 3rem; } -/* Mobile toggle */ .sidebar-toggle { display: none; position: fixed; - top: 12px; + top: calc(var(--nav-h) + 10px); left: 12px; z-index: 20; background: var(--bg); @@ -189,19 +227,14 @@ body { } @media (max-width: 768px) { - .sidebar { - transform: translateX(-100%); - } + .sidebar { transform: translateX(-100%); } .sidebar.open { transform: translateX(0); box-shadow: 4px 0 20px rgba(0,0,0,0.15); } .sidebar-toggle { display: block; } - .main-content { - margin-left: 0; - padding: 2rem 1.25rem; - padding-top: 3.5rem; - } + .main-content { margin-left: 0; padding: 2rem 1.25rem; padding-top: 3.5rem; } + .nav-links a:not(.nav-github):not(:first-child) { display: none; } } /* --- Typography --- */ @@ -209,113 +242,121 @@ a { color: var(--accent); text-decoration: none; } a:hover { color: var(--accent-hover); text-decoration: underline; } h1 { - font-size: 1.75rem; + font-size: 1.875rem; font-weight: 700; margin-bottom: 0.5rem; - letter-spacing: -0.02em; + letter-spacing: -0.025em; + line-height: 1.2; } - h2 { - font-size: 1.3rem; + font-size: 1.2rem; font-weight: 600; margin-top: 2.5rem; margin-bottom: 0.75rem; - padding-bottom: 0.4rem; + padding-bottom: 0.5rem; border-bottom: 1px solid var(--border); + letter-spacing: -0.01em; } - -h3 { font-size: 1.1rem; font-weight: 600; margin-top: 1.5rem; margin-bottom: 0.4rem; } -h4 { font-size: 0.95rem; font-weight: 600; margin-top: 1rem; margin-bottom: 0.3rem; color: var(--fg-muted); } -p { margin-bottom: 0.75rem; } +h3 { font-size: 1.05rem; font-weight: 600; margin-top: 1.75rem; margin-bottom: 0.4rem; } +h4 { + font-size: 0.78rem; + font-weight: 700; + margin-top: 1rem; + margin-bottom: 0.3rem; + color: var(--fg-muted); + text-transform: uppercase; + letter-spacing: 0.05em; +} +p { margin-bottom: 0.875rem; } code { font-family: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'Consolas', monospace; - font-size: 0.87em; - background: var(--code-bg); + font-size: 0.85em; + background: var(--inline-code-bg); + color: var(--fg); padding: 0.15em 0.4em; border-radius: 4px; } pre { background: var(--code-bg); - padding: 1rem 1.25rem; + color: var(--code-fg); + padding: 1.25rem 1.5rem; border-radius: var(--radius); overflow-x: auto; - margin-bottom: 1rem; - border: 1px solid var(--border); - line-height: 1.5; + margin-bottom: 1.25rem; + line-height: 1.6; } +pre code { background: none; color: inherit; padding: 0; font-size: 0.875rem; } -pre code { background: none; padding: 0; font-size: 0.85em; } - -/* --- Tables (rendered from markdown) --- */ -table { - border-collapse: collapse; - width: 100%; - margin-bottom: 1rem; - font-size: 0.9rem; -} -th, td { - border: 1px solid var(--border); - padding: 0.4rem 0.75rem; - text-align: left; +/* --- Tables --- */ +table { border-collapse: collapse; width: 100%; margin-bottom: 1.25rem; font-size: 0.9rem; } +th, td { border: 1px solid var(--border); padding: 0.5rem 0.875rem; text-align: left; } +th { + background: var(--accent-bg); + font-weight: 600; + font-size: 0.78rem; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--accent); } -th { background: var(--accent-bg); font-weight: 600; } -tbody tr:nth-child(even) { background: var(--code-bg); } +tbody tr:hover { background: var(--sidebar-bg); } -/* --- Lists (rendered from markdown) --- */ +/* --- Lists --- */ .class-doc ul, .class-doc ol, .method-doc ul, .method-doc ol, .readme ul, .readme ol { - margin-bottom: 0.75rem; + margin-bottom: 0.875rem; padding-left: 1.5rem; } -.class-doc li, .method-doc li, .readme li { margin-bottom: 0.25rem; } +.class-doc li, .method-doc li, .readme li { margin-bottom: 0.3rem; } /* --- Breadcrumb --- */ -.breadcrumb { font-size: 0.85rem; color: var(--fg-muted); margin-bottom: 0.75rem; } +.breadcrumb { + font-size: 0.8rem; + color: var(--fg-muted); + margin-bottom: 1rem; + display: flex; + align-items: center; + gap: 0.4rem; +} .breadcrumb a { color: var(--fg-muted); } -.breadcrumb a:hover { color: var(--accent); } +.breadcrumb a:hover { color: var(--accent); text-decoration: none; } /* --- Class page --- */ -.class-doc { margin-bottom: 2rem; } +.class-doc { margin-bottom: 2.5rem; } .superclass { display: inline-block; - font-size: 0.9rem; + font-size: 0.875rem; color: var(--fg-muted); - margin-bottom: 1rem; + margin-bottom: 1.25rem; background: var(--accent-bg); - padding: 0.25rem 0.75rem; + padding: 0.2rem 0.75rem; border-radius: 999px; } .badge { display: inline-block; - font-size: 0.75rem; - font-weight: 600; + font-size: 0.68rem; + font-weight: 700; padding: 0.15rem 0.5rem; border-radius: 999px; vertical-align: middle; + text-transform: uppercase; + letter-spacing: 0.04em; } -.badge-sealed { - background: var(--accent-bg); - color: var(--fg-muted); -} -.badge-abstract { - background: var(--badge-abstract-bg); - color: var(--badge-abstract-fg); -} +.badge-sealed { background: var(--inline-code-bg); color: var(--fg-muted); } +.badge-abstract { background: var(--badge-abstract-bg); color: var(--badge-abstract-fg); } /* --- Methods --- */ .method { background: var(--method-bg); border: 1px solid var(--border); border-radius: var(--radius); - padding: 1rem 1.25rem; - margin-bottom: 0.75rem; + padding: 1.25rem 1.5rem; + margin-bottom: 1rem; transition: box-shadow 0.15s; } - -.method:hover { box-shadow: var(--shadow); } +.method:hover { box-shadow: var(--shadow-md); } .method:target { border-left: 3px solid var(--accent); } .method-header { @@ -324,38 +365,33 @@ tbody tr:nth-child(even) { background: var(--code-bg); } gap: 0.75rem; flex-wrap: wrap; } - .method-signature { - font-family: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'Consolas', monospace; - font-size: 0.95rem; + font-family: 'JetBrains Mono', 'Fira Code', monospace; + font-size: 0.875rem; font-weight: 600; color: var(--accent); } - -.source-link { - font-size: 0.78rem; - color: var(--fg-muted); - margin-left: auto; -} - -.method-doc { margin-top: 0.5rem; color: var(--fg); } +.source-link { font-size: 0.75rem; color: var(--fg-muted); margin-left: auto; } +.method-doc { margin-top: 0.6rem; } .method-doc p { margin-bottom: 0.5rem; } /* --- TOC --- */ .toc { margin-bottom: 2rem; } .toc ul { list-style: none; column-count: 2; column-gap: 2rem; } -.toc li { margin-bottom: 0.25rem; font-family: monospace; font-size: 0.87rem; } +.toc li { margin-bottom: 0.3rem; font-family: 'JetBrains Mono', monospace; font-size: 0.85rem; } .toc .label { display: inline-block; - font-family: -apple-system, BlinkMacSystemFont, sans-serif; - font-size: 0.72rem; - font-weight: 600; + font-family: 'Inter', sans-serif; + font-size: 0.67rem; + font-weight: 700; color: var(--fg-muted); - background: var(--code-bg); + background: var(--inline-code-bg); padding: 0.1em 0.4em; border-radius: 3px; margin-right: 0.3rem; vertical-align: middle; + text-transform: uppercase; + letter-spacing: 0.04em; } /* --- Class list (index) --- */ @@ -363,62 +399,50 @@ tbody tr:nth-child(even) { background: var(--code-bg); } list-style: none; display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); - gap: 0.6rem; + gap: 0.75rem; } - .class-list li { background: var(--method-bg); border: 1px solid var(--border); border-radius: var(--radius); - padding: 0.75rem 1rem; - transition: box-shadow 0.15s; + padding: 0.875rem 1rem; + transition: box-shadow 0.15s, border-color 0.15s; } - -.class-list li:hover { box-shadow: var(--shadow); } +.class-list li:hover { box-shadow: var(--shadow-md); border-color: var(--accent); } .class-list li a { font-weight: 600; } - .class-list .class-summary { display: block; - font-size: 0.83rem; + font-size: 0.82rem; color: var(--fg-muted); - margin-top: 0.2rem; + margin-top: 0.25rem; + line-height: 1.4; } /* --- Hierarchy tree --- */ -.hierarchy-tree { margin-bottom: 2rem; } +.hierarchy-tree { margin-bottom: 2.5rem; } .hierarchy-tree ul { list-style: none; padding-left: 1.5rem; border-left: 2px solid var(--border); } .hierarchy-tree > ul { border-left: none; padding-left: 0; } -.hierarchy-tree li { margin: 0.2rem 0; font-size: 0.9rem; } +.hierarchy-tree li { margin: 0.25rem 0; font-size: 0.9rem; } .hierarchy-tree a { font-weight: 500; } -/* --- Inherited --- */ -.inherited-section { margin-top: 2rem; } -.inherited-section .method { - background: transparent; - border-style: dashed; - opacity: 0.85; -} +/* --- Inherited methods --- */ +.inherited-section { margin-top: 2.5rem; } +.inherited-section .method { background: transparent; border-style: dashed; opacity: 0.8; } /* --- Search results --- */ -.search-results { - display: none; - margin-bottom: 2rem; -} +.search-results { display: none; margin-bottom: 2rem; } .search-results.active { display: block; } .search-results h2 { border-bottom: none; margin-top: 0; } -.search-result-item { - padding: 0.5rem 0; - border-bottom: 1px solid var(--border); -} +.search-result-item { padding: 0.6rem 0; border-bottom: 1px solid var(--border); } .search-result-item:last-child { border-bottom: none; } .search-result-class { font-weight: 600; } -.search-result-method { font-family: monospace; font-size: 0.88rem; } +.search-result-method { font-family: 'JetBrains Mono', monospace; font-size: 0.85rem; } -/* --- Syntax highlighting --- */ +/* --- Syntax highlighting (on dark code blocks) --- */ .hl-keyword { color: var(--hl-keyword); font-weight: 600; } .hl-string { color: var(--hl-string); } .hl-number { color: var(--hl-number); } @@ -430,8 +454,8 @@ tbody tr:nth-child(even) { background: var(--code-bg); } /* --- Footer --- */ footer { - margin-top: 3rem; - padding-top: 1rem; + margin-top: 4rem; + padding-top: 1.25rem; border-top: 1px solid var(--border); font-size: 0.8rem; color: var(--fg-muted); @@ -439,98 +463,158 @@ footer { /* --- Landing page --- */ .landing-wrapper { - justify-content: center; + flex-direction: column; } +.landing-wrapper .sidebar-toggle { display: none; } .landing-content { - margin-left: 0; - max-width: 960px; + max-width: 1020px; margin: 0 auto; - padding: 3rem 2.5rem; + padding: 4rem 2.5rem 3rem; + width: 100%; } .landing-hero { - text-align: center; - margin-bottom: 3rem; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 4rem; + align-items: center; + margin-bottom: 5rem; } - -.landing-hero h1 { - font-size: 2.5rem; - margin-bottom: 0.5rem; +@media (max-width: 860px) { + .landing-hero { grid-template-columns: 1fr; gap: 2.5rem; } + .landing-content { padding: 2.5rem 1.5rem 2rem; } } +.landing-hero-text h1 { + font-size: 2.75rem; + line-height: 1.1; + letter-spacing: -0.04em; + margin-bottom: 1rem; +} .landing-tagline { - font-size: 1.15rem; + font-size: 1.05rem; color: var(--fg-muted); + line-height: 1.65; + margin-bottom: 1.75rem; +} +.landing-cta { + display: flex; + gap: 0.75rem; + flex-wrap: wrap; + align-items: center; } +.btn-primary { + display: inline-block; + background: var(--accent); + color: #fff; + font-weight: 600; + font-size: 0.875rem; + padding: 0.6rem 1.25rem; + border-radius: 6px; + text-decoration: none; + transition: background 0.15s; +} +.btn-primary:hover { background: var(--accent-hover); color: #fff; text-decoration: none; } + +.btn-secondary { + display: inline-block; + background: transparent; + color: var(--fg-muted); + font-weight: 500; + font-size: 0.875rem; + padding: 0.6rem 1.25rem; + border-radius: 6px; + border: 1px solid var(--border); + text-decoration: none; + transition: border-color 0.15s, color 0.15s; +} +.btn-secondary:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; } + +.landing-code-window { + background: var(--code-bg); + border-radius: 12px; + overflow: hidden; + box-shadow: var(--shadow-md); +} +.code-window-bar { + background: #27272A; + padding: 0.65rem 1rem; + display: flex; + align-items: center; + gap: 0.4rem; +} +.code-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; } +.code-dot-r { background: #EF4444; } +.code-dot-y { background: #F59E0B; } +.code-dot-g { background: #10B981; } +.code-window-title { + margin-left: 0.5rem; + font-family: 'JetBrains Mono', monospace; + font-size: 0.72rem; + color: #52525B; +} +.code-window-body { padding: 1.25rem 1.5rem; } +.code-window-body pre { + margin: 0; padding: 0; background: none; + font-size: 0.82rem; + line-height: 1.75; +} +.code-window-body pre code { background: none; color: var(--code-fg); padding: 0; } + +.landing-section-label { + font-size: 0.72rem; + font-weight: 700; + letter-spacing: 0.1em; + text-transform: uppercase; + color: var(--accent); + margin-bottom: 1rem; +} .landing-cards { display: grid; - grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 1rem; margin-bottom: 2rem; } - .landing-card { display: block; background: var(--method-bg); border: 1px solid var(--border); border-radius: var(--radius); - padding: 1.25rem 1.5rem; + padding: 1.5rem; text-decoration: none; color: var(--fg); - transition: box-shadow 0.15s, border-color 0.15s; + transition: box-shadow 0.15s, border-color 0.15s, transform 0.15s; } - .landing-card:hover { - box-shadow: var(--shadow); + box-shadow: var(--shadow-md); border-color: var(--accent); + transform: translateY(-1px); text-decoration: none; } - +.landing-card-emoji { font-size: 1.35rem; margin-bottom: 0.65rem; display: block; } .landing-card h2 { - font-size: 1.1rem; - margin-top: 0; - margin-bottom: 0.4rem; - border-bottom: none; - padding-bottom: 0; - color: var(--accent); + font-size: 0.95rem; + margin-top: 0; margin-bottom: 0.4rem; + border-bottom: none; padding-bottom: 0; + color: var(--fg); font-weight: 600; } - .landing-card p { - font-size: 0.88rem; + font-size: 0.875rem; color: var(--fg-muted); margin-bottom: 0; + line-height: 1.5; } - .landing-links { text-align: center; - margin-top: 1.5rem; - font-size: 0.9rem; -} - -/* --- Prose pages --- */ -.prose-content { - max-width: 800px; -} - -/* --- Sidebar cross-navigation --- */ -.sidebar-section { - padding: 0.5rem 1.25rem; - border-bottom: 1px solid var(--border); - margin-bottom: 0.5rem; -} - -.sidebar-home-link, .sidebar-docs-link { - display: block; - font-size: 0.85rem; + margin-top: 2rem; + font-size: 0.875rem; color: var(--fg-muted); - padding: 0.2rem 0; } -.sidebar-home-link:hover, .sidebar-docs-link:hover { - color: var(--accent); -} +/* --- Prose pages --- */ +.prose-content { max-width: 760px; } "; /// Write CSS stylesheet. diff --git a/crates/beamtalk-cli/src/commands/doc/layout.rs b/crates/beamtalk-cli/src/commands/doc/layout.rs index f010ae76f..5441263c9 100644 --- a/crates/beamtalk-cli/src/commands/doc/layout.rs +++ b/crates/beamtalk-cli/src/commands/doc/layout.rs @@ -11,9 +11,30 @@ use std::fmt::Write as _; use super::extractor::ClassInfo; use super::renderer::html_escape; -/// Generate HTML page header with sidebar toggle. -pub(super) fn page_header(title: &str, css_path: Option<&str>) -> String { - let css = css_path.unwrap_or("style.css"); +/// Generate HTML page header with top navigation bar. +/// +/// `css` is the relative path to the stylesheet. +/// `nav_prefix` is prepended to cross-site navigation links (e.g. `"../"` when +/// in a subdirectory). An empty `nav_prefix` signals standalone API-only mode +/// (generated by `beamtalk doc`): in that case the prose-docs links (`/docs/`, +/// `/apidocs/`) are omitted because those pages do not exist in the output. +pub(super) fn page_header(title: &str, css: &str, nav_prefix: &str) -> String { + // In site mode (nav_prefix non-empty) we have cross-links to prose docs and + // the root. In standalone mode we only show the GitHub link. + let site_links = if nav_prefix.is_empty() { + String::new() + } else { + format!( + "Language\n\ + Architecture\n\ + API\n" + ) + }; + let logo_href = if nav_prefix.is_empty() { + "index.html".to_string() + } else { + nav_prefix.to_string() + }; format!( "\n\ \n\ @@ -21,11 +42,19 @@ pub(super) fn page_header(title: &str, css_path: Option<&str>) -> String { \n\ \n\