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
2 changes: 1 addition & 1 deletion docs/specs/website-design-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Content spans at most 1200 px, with 32 px desktop gutters and 20 px mobile gutte

## [WEB-CONTENT] Minimal information architecture

`/` introduces the toolkit and features the example post. `/docs/` explains the architectural basics and links to `/docs/getting-started/`, `/docs/crates/` and `/docs/profiler/`. `/blog/` and the tag/category pages are generated by techdoc. `/blog/one-engine/` contains one short example post with the header image. The plugin also owns the feed, sitemap and structural assets; do not copy its templates into this project.
`/` introduces the toolkit, links every published crate and npm package to its registry, and features the example post. The header carries Docs, Journal and the GitHub repo; the homepage hero repeats the repo link as a button. The footer credits © Nimblesite, linked to nimblesite.co. `/docs/` explains the architectural basics and links to `/docs/getting-started/`, `/docs/crates/` and `/docs/profiler/`. `/blog/` and the tag/category pages are generated by techdoc. `/blog/one-engine/` contains one short example post with the header image. The plugin also owns the feed, sitemap and structural assets; do not copy its templates into this project.

The canonical origin defaults to `http://localhost:8080` for this local sketch and is configured with `SITE_URL` before a production build. Do not invent a live domain or publish through a new workflow.

Expand Down
2 changes: 1 addition & 1 deletion website/.oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"overrides": [
{
// Eleventy requires default exports for config and JavaScript data entrypoints.
"files": ["eleventy.config.js", "src/_data/site.js"],
"files": ["eleventy.config.js", "src/_data/site.js", "src/_data/navigation.js"],
"rules": { "import/no-default-export": "off" }
},
{
Expand Down
2 changes: 1 addition & 1 deletion website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"dev": "eleventy --serve",
"build": "eleventy",
"test": "python3 tests/prose_test.py",
"lint": "oxlint --deny-warnings eleventy.config.js src/_data/site.js"
"lint": "oxlint --deny-warnings eleventy.config.js src/_data/site.js src/_data/navigation.js"
},
"devDependencies": {
"@11ty/eleventy": "3.1.7",
Expand Down
17 changes: 17 additions & 0 deletions website/src/_data/navigation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// [WEB-CONTENT] Header and footer links. The repo is one click from every page.
const repo = "https://github.com/Nimblesite/lspkit";

const navigation = {
repo,
main: [
{ text: "Docs", url: "/docs/" },
{ text: "Journal", url: "/blog/" },
{ text: "GitHub ↗", url: repo, external: true },
],
footer: [{ title: "The foundation for any language's tooling.", items: [
{ text: "GitHub ↗", url: repo },
{ text: `© ${new Date().getFullYear()} Nimblesite ↗`, url: "https://www.nimblesite.co" },
] }],
};

export default navigation;
9 changes: 0 additions & 9 deletions website/src/_data/navigation.json

This file was deleted.

1 change: 1 addition & 0 deletions website/src/_includes/icons/github.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 4 additions & 1 deletion website/src/assets/css/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ h3 { font-size: 25px; line-height: 1.25; letter-spacing: -.025em; }
.footer-section { display: flex; justify-content: space-between; gap: 24px; }
.footer-section h3 { font-size: 16px; letter-spacing: -.02em; }
.footer-section ul { display: flex; gap: 24px; font-size: 13px; }
.footer-bottom { margin-top: 24px; padding: 0; font: 12px var(--font-mono); color: var(--color-muted); text-align: left; }
/* [WEB-DESIGN-LAYOUT] Homepage editorial grid. */
.home-shell { max-width: var(--max-width); margin: auto; padding: 0 32px; }
.hero { display: grid; grid-template-columns: 1.8fr 1fr; align-items: end; gap: 64px; padding: 64px 0 48px; }
Expand All @@ -71,6 +70,10 @@ h3 { font-size: 25px; line-height: 1.25; letter-spacing: -.025em; }
.registry svg { width: 22px; height: 22px; }
.registry a { display: inline-flex; min-height: 44px; align-items: center; gap: 6px; font: 14px var(--font-mono); text-decoration: none; }
.registry a span { color: var(--color-primary); }
.hero-links { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 32px; }
.repo-link { display: inline-flex; align-items: center; gap: 10px; min-height: 44px; padding: 0 18px; background: var(--color-text); color: var(--color-bg); font-size: 14px; font-weight: bold; text-decoration: none; }
.repo-link svg { width: 18px; height: 18px; }
.footer-bottom { display: none; }
.engineering-plate { border-top: 1px solid var(--color-border); }
.engineering-plate img { width: 100%; height: auto; display: block; }
.engineering-plate figcaption { display: flex; justify-content: space-between; gap: 20px; padding: 14px 0; border-bottom: 1px solid var(--color-border); font: 10px/1.5 var(--font-mono); letter-spacing: .08em; text-transform: uppercase; color: var(--color-muted); }
Expand Down
5 changes: 4 additions & 1 deletion website/src/index.njk
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ title: lspkit — The foundation for any language's tooling.
</div>
<div class="hero-aside">
<p>Write your language analysis once, as one Rust trait. lspkit serves it to your VS Code extension over the Language Server Protocol and to Claude Code, Cursor and Codex as an MCP server. Find the hot path and the memory leak with a profiler built into VS Code.</p>
<a class="text-link" href="/docs/getting-started/">Get started <span aria-hidden="true">↗</span></a>
<div class="hero-links">
<a class="text-link" href="/docs/getting-started/">Get started <span aria-hidden="true">↗</span></a>
<a class="repo-link" href="{{ navigation.repo }}">{% include "icons/github.svg" %}Star on GitHub</a>
</div>
</div>
</section>
{# [WEB-CONTENT] Every published crate and npm package, linked to its registry. #}
Expand Down
14 changes: 11 additions & 3 deletions website/tests/prose_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
SITE = Path(__file__).resolve().parents[1]
OUTPUT = SITE / "_site"
DOCS = ("docs/getting-started", "docs/crates", "docs/profiler", "docs/examples")
REPO = "https://github.com/Nimblesite/lspkit"


class Document(HTMLParser):
Expand Down Expand Up @@ -110,11 +111,18 @@ def test_reading_pages_have_header_images_and_explicit_prose_classes(self):
css = OUTPUT / "assets/css/styles.css"
self.assertLessEqual(len(css.read_text().splitlines()), 2000)

def test_journal_has_illustrated_posts_and_only_docs_and_journal_navigation(self):
def test_journal_has_illustrated_posts_and_docs_journal_and_repo_navigation(self):
for path in OUTPUT.rglob("*.html"):
page = Document(path)
navigation = [a["href"] for a in page.attributes("a") if "nav-link" in a.get("class", "").split()]
self.assertEqual(navigation, ["/docs/", "/blog/"], path)
links = page.attributes("a")
navigation = [a["href"] for a in links if "nav-link" in a.get("class", "").split()]
self.assertEqual(navigation, ["/docs/", "/blog/", REPO], path)
self.assertIn("https://www.nimblesite.co", [a.get("href") for a in links], path)
home = [a.get("href") for a in Document(OUTPUT / "index.html").attributes("a")]
self.assertIn(REPO, home)
for package in ("crates.io/crates/lspkit", "crates.io/crates/lspkit-server", "crates.io/crates/lsp-profiler",
"crates.io/crates/lsp-profiler-dotnet", "npmjs.com/package/@nimblesite/lsp-profiler-ui"):
self.assertTrue(any(href and href.endswith(package) for href in home), package)
journal = self.check_page(OUTPUT / "blog/index.html")
self.assertTrue(journal.attributes("img"), "Journal needs a real header image")
self.assertTrue(journal.attributes("h2"), "Post titles need a heading hierarchy")
Expand Down
Loading