Skip to content

docs: fix missing search on mobile, cleanup navbar - #618

Open
sanny-io wants to merge 17 commits into
zenstackhq:mainfrom
sanny-io:docs/navbar-cleanup
Open

docs: fix missing search on mobile, cleanup navbar#618
sanny-io wants to merge 17 commits into
zenstackhq:mainfrom
sanny-io:docs/navbar-cleanup

Conversation

@sanny-io

@sanny-io sanny-io commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

The "Star on Github" button was introducing significant complexity to the navbar. This PR simplifies the navbar, which fixes several things.

  • The search bar now properly appears on mobile devices.
  • There is no longer a horizontal scrollbar on all pages on some screen sizes.
  • An empty sidebar no longer appears on some screen sizes.

Before

image

After

image

Closes #567

Summary by CodeRabbit

  • New Features

    • Added a refreshed brand identity, including updated logo, favicon, colors, typography, and GitHub navigation.
    • Added a landing-page experience for the site root.
    • Added documentation for PGlite, soft delete, date/time validation, and the zenstack-mcp community package.
  • Documentation

    • Clarified that omit settings do not replace access policies.
    • Updated external ZenStack linking.
  • Refactor

    • Removed outdated homepage sections, sponsorship content, user logos, and migration notes.

@vercel

vercel Bot commented Jun 13, 2026

Copy link
Copy Markdown

@sanny-io is attempting to deploy a commit to the ZenStack Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@sanny-io, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 16 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 328203bc-cd35-4d78-946b-ab83fa60d226

📥 Commits

Reviewing files that changed from the base of the PR and between ff47235 and 7567795.

📒 Files selected for processing (2)
  • docusaurus.config.js
  • src/css/custom.css

Walkthrough

The site now proxies the Framer landing page at /, updates branding and navigation, removes the previous homepage components, and expands documentation for integrations, plugins, validation, omit settings, and community packages.

Changes

Site migration and presentation

Layer / File(s) Summary
Root landing-page proxy
middleware.ts
The root route fetches the Framer page, preserves query data and selected headers, rewrites origin URLs, and filters unsuccessful responses.
Branding and navigation configuration
docusaurus.config.js, src/css/custom.css
The site uses the new logo, IBM Plex Sans, updated colors, revised navbar styling, and a standard accessible GitHub link.
Legacy homepage removal
src/pages/index.tsx, src/pages/index.module.css, src/pages/_components/*, src/components/Sponsorship.tsx, src/components/UserLogos.tsx
The previous homepage and its feature, sponsorship, and user-logo components were removed.

Documentation refresh

Layer / File(s) Summary
Integration and behavior references
docs/recipe/databases/postgres.md, docs/reference/plugins/soft-delete.md, docs/orm/api/omit.md, docs/reference/zmodel/input-validation.md
Documentation now covers PGlite, soft-delete behavior, omit-setting limitations, and date and time validation APIs.
Package and site link updates
docs/community-packages.md, blog/react-table/index.md
The community package list now includes zenstack-mcp, and the React Table article uses the canonical ZenStack URL.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant middleware
  participant Framer
  Browser->>middleware: Request /
  middleware->>Framer: Fetch landing page
  Framer-->>middleware: Return HTML
  middleware->>middleware: Rewrite Framer URLs
  middleware-->>Browser: Return rewritten page
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The pull request includes unrelated homepage deletion, middleware, branding, and documentation changes beyond the linked mobile-search issue. Limit the pull request to mobile search and navbar changes, or link issues that justify the additional homepage, middleware, branding, and documentation work.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check ❓ Inconclusive The summary shows navbar changes but does not verify that the search bar appears on mobile devices. Provide reviewable evidence of the mobile search implementation or behavior, such as search configuration or responsive navbar changes.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the navbar cleanup and mobile search fix stated in the pull request objectives.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Fix failing CI checks
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ymc9

ymc9 commented Jun 18, 2026

Copy link
Copy Markdown
Member

Hi @sanny-io , a friend of mine is actually working on a fully renovated design of the home page 😄, so I'm putting this PR on hold. Hopefully that'll land soon!

sanny-io and others added 12 commits July 31, 2026 02:21
* docs: add PGlite section

* add newline
* doc: release 3.8.0

* update
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore: new logo

* update logo
* chore: update theme to match new branding

- Change primary color to #ff3f02 (matching zenstack.dev/framer)
- Use IBM Plex Sans as the base font
- Style navbar title in orange, all caps, tighter logo gap
- Replace logo.png/logo-dark.png with new-logo.png

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore: rewrite root to framer landing page

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore: move custom home page to /home so root rewrite takes effect

Vercel rewrites only apply when no static file matches the path, so the
Docusaurus-generated root index.html was shadowing the / -> Framer rewrite.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore: remove old home page and /framer rewrites

The custom home page (moved to /home earlier) is superseded by the
Framer-hosted landing page served at the root. Sponsorship/UserLogos
components were only used by it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* update framer custom domain

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…enstackhq#629)

* fix: rewrite canonical URL of Framer-hosted homepage to zenstack.dev

The / route is rewritten to zenstack.framer.website (vercel.json), so the
page served at zenstack.dev declared the Framer domain as its canonical
and og:url. Add Vercel Edge Middleware that proxies the Framer page and
rewrites those URLs to https://zenstack.dev. On any failure it falls
through to the existing plain rewrite.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: remove log

* fix: forward query string and safe request headers to Framer upstream

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: forward referer and x-forwarded-for headers to Framer upstream

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@middleware.ts`:
- Around line 41-46: Update the response-header construction in the
rewritten-response flow to use an explicit allowlist of headers appropriate for
the proxied response, such as content-type and cache-control, instead of copying
upstream.headers and deleting only content-encoding and content-length. Preserve
the existing response status and rewritten body while ensuring Framer-specific
headers are not forwarded.
- Line 31: Update the upstream fetch in the middleware to use a bounded timeout
via an abort signal, and handle its failure so AbortError and other fetch errors
return undefined without blocking homepage requests. Preserve the existing
response handling and fallback behavior for non-HTML responses.

In `@src/css/custom.css`:
- Around line 13-19: Update the `.navbar__title` styling to use a text color
with at least 4.5:1 contrast against its white background. Prefer a dedicated
contrast-safe color token if the existing `--ifm-color-primary` must remain
unchanged; otherwise replace the primary token with a sufficiently darker color
while preserving the related palette.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9fbcc8c9-5817-49f3-87f3-f0e8a0633f33

📥 Commits

Reviewing files that changed from the base of the PR and between 34cb969 and ff47235.

⛔ Files ignored due to path filters (3)
  • static/img/new-logo-full.png is excluded by !**/*.png, !**/*.png
  • static/img/new-logo.png is excluded by !**/*.png, !**/*.png
  • vercel.json is excluded by !**/*.json
📒 Files selected for processing (19)
  • blog/react-table/index.md
  • docs/community-packages.md
  • docs/orm/api/omit.md
  • docs/recipe/databases/postgres.md
  • docs/reference/plugins/soft-delete.md
  • docs/reference/zmodel/input-validation.md
  • docusaurus.config.js
  • middleware.ts
  • src/components/Sponsorship.tsx
  • src/components/UserLogos.tsx
  • src/css/custom.css
  • src/pages/_components/AICoding.tsx
  • src/pages/_components/Notes.tsx
  • src/pages/_components/ORM.tsx
  • src/pages/_components/Schema.tsx
  • src/pages/_components/Service.tsx
  • src/pages/_components/ValueProps.tsx
  • src/pages/index.module.css
  • src/pages/index.tsx
💤 Files with no reviewable changes (10)
  • src/pages/_components/Notes.tsx
  • src/pages/_components/Schema.tsx
  • src/pages/_components/Service.tsx
  • src/pages/_components/AICoding.tsx
  • src/pages/index.module.css
  • src/pages/_components/ORM.tsx
  • src/pages/_components/ValueProps.tsx
  • src/components/UserLogos.tsx
  • src/components/Sponsorship.tsx
  • src/pages/index.tsx

Comment thread middleware.ts
}
}

const upstream = await fetch(`${FRAMER_ORIGIN}/${search}`, { headers: requestHeaders });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files | sed -n '1,120p'

echo "== middleware locations =="
fd -a 'middleware\.(ts|js)$' . 2>/dev/null || true

echo "== middleware.ts context =="
if [ -f middleware.ts ]; then
  wc -l middleware.ts
  cat -n middleware.ts | sed -n '1,120p'
fi

echo "== vercel.json relevant =="
if [ -f vercel.json ]; then
  cat -n vercel.json
fi

Repository: zenstackhq/zenstack-docs

Length of output: 6882


🌐 Web query:

Vercel Edge Middleware Edge fetch AbortController signal timeout documentation

💡 Result:

Vercel's Edge Runtime, which powers Routing Middleware and Edge Functions, natively supports the standard Web fetch API and AbortController / AbortSignal APIs [1][2]. To implement a timeout for a fetch request within Vercel Middleware or Edge Functions, you should use AbortSignal.timeout(ms) [3]. This is the recommended approach for limiting the time an external API call is allowed to take [3]. ### Recommended Pattern You can create a signal that automatically aborts after a specified duration: const controller = new AbortController; const signal = AbortSignal.timeout(5000); // Aborts after 5 seconds try { const response = await fetch('https://api.example.com', { signal }); // Handle response } catch (error) { if (error.name === 'TimeoutError') { // Handle specific timeout } } ### Handling Client Disconnection Vercel automatically provides a request.signal which represents the client's connection state [4][5]. If the user disconnects, this signal will fire, and you can propagate this to your fetch calls to cancel them and save resources [4][6]: export async function GET(request: Request) { const controller = new AbortController; // Abort our fetch if the client disconnects request.signal.addEventListener('abort', => controller.abort); const response = await fetch('https://api.example.com', { signal: controller.signal, }); return response; } ### Key Considerations - Middleware Timeout Limits: Middleware has a strict execution time limit (e.g., 25 seconds) [3]. Setting fetch timeouts lower than this limit helps prevent MIDDLEWARE_INVOCATION_TIMEOUT errors [3][7]. - API Compatibility: The Edge Runtime aims for Web Standard compliance [1]. Ensure you are using modern standards; some older polyfills or manual setTimeout implementations may be unnecessary or lead to I/O errors if not handled within the correct request context [8]. - Troubleshooting: If you encounter issues with signals not triggering as expected, ensure you are testing in a production-like environment, as local development or specific proxy configurations can sometimes impact how abort events are propagated [9].

Citations:


Add a bounded timeout to the upstream fetch.

The middleware matches /, and middleware.ts:31 waits on the Framer fetch without any timeout. Since the middleware falls back to undefined on non-HTML responses, return undefined on signals that are not AbortError; otherwise a slow upstream can block the landing page for all homepage requests.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@middleware.ts` at line 31, Update the upstream fetch in the middleware to use
a bounded timeout via an abort signal, and handle its failure so AbortError and
other fetch errors return undefined without blocking homepage requests. Preserve
the existing response handling and fallback behavior for non-HTML responses.

Comment thread middleware.ts
Comment on lines +41 to +46
const headers = new Headers(upstream.headers);
// the body was re-encoded, so the upstream encoding/length no longer apply
headers.delete('content-encoding');
headers.delete('content-length');

return new Response(rewritten, { status: upstream.status, headers });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Allowlist forwarded response headers instead of copying all of them.

Line 41 copies every header from the upstream Framer response, deleting only content-encoding and content-length. Any other header Framer sends (for example set-cookie, a content-security-policy tuned for its own domain, or caching directives scoped to zenstack.framer.website) is forwarded verbatim onto a response served from zenstack.dev. This can leak Framer-scoped cookies onto the docs domain or apply security/caching semantics that do not fit the proxied context.

Build the response headers from an explicit allowlist (for example content-type, cache-control) instead of a denylist.

🔒 Proposed fix to allowlist response headers
-        const headers = new Headers(upstream.headers);
-        // the body was re-encoded, so the upstream encoding/length no longer apply
-        headers.delete('content-encoding');
-        headers.delete('content-length');
+        const headers = new Headers();
+        for (const name of ['content-type', 'cache-control']) {
+            const value = upstream.headers.get(name);
+            if (value) {
+                headers.set(name, value);
+            }
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const headers = new Headers(upstream.headers);
// the body was re-encoded, so the upstream encoding/length no longer apply
headers.delete('content-encoding');
headers.delete('content-length');
return new Response(rewritten, { status: upstream.status, headers });
const headers = new Headers();
for (const name of ['content-type', 'cache-control']) {
const value = upstream.headers.get(name);
if (value) {
headers.set(name, value);
}
}
return new Response(rewritten, { status: upstream.status, headers });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@middleware.ts` around lines 41 - 46, Update the response-header construction
in the rewritten-response flow to use an explicit allowlist of headers
appropriate for the proxied response, such as content-type and cache-control,
instead of copying upstream.headers and deleting only content-encoding and
content-length. Preserve the existing response status and rewritten body while
ensuring Framer-specific headers are not forwarded.

Comment thread src/css/custom.css
Comment on lines +13 to +19
--ifm-color-primary: #ff3f02;
--ifm-color-primary-dark: #e63902;
--ifm-color-primary-darker: #cc3202;
--ifm-color-primary-darkest: #b32c01;
--ifm-color-primary-light: #ff521b;
--ifm-color-primary-lighter: #ff6535;
--ifm-color-primary-lightest: #ff794e;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## locate custom.css"
git ls-files | grep -F 'src/css/custom.css' || true

echo "## relevant custom.css section"
if [ -f src/css/custom.css ]; then
  nl -ba src/css/custom.css | sed -n '1,90p'
fi

echo "## search navbar__title and primary usage in css"
rg -n --ignore-case 'navbar__title|var\(--ifm-color-primary|color:|background|text' src/css/custom.css -C 2

echo "## compute relative luminance/contrast for asserted colors vs white"
python3 - <<'PY'
def component(s):
    v=int(s,16)/255
    return v/12.92 if v<=0.03928 else ((v+0.055)/1.055)**2.4

def contrast(c1,c2):
    def lum(hex):
        return 1.698*component(hex[1:3])+0.987*component(hex[3:5])+0.317*component(hex[5:])
    l1,l2=lum(c1),lum(c2)
    return(max(l1,l2)+0.05)/(min(l1,l2)+0.05)

colors = {
    "primary": "`#ff3f02`",
    "primary-dark": "`#e63902`",
    "primary-darker": "`#cc3202`",
    "primary-darkest": "`#b32c01`",
    "primary-light": "`#ff521b`",
    "primary-lighter": "`#ff6535`",
    "primary-lightest": "`#ff794e`",
}
for name,hex in colors.items():
    c=contrast(hex,"`#ffffff`")
    print(f"{name} {hex}: {c:.3f}:1 dark text on white ({c>=4.5='contrast-safe'}")
PY

Repository: zenstackhq/zenstack-docs

Length of output: 273


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## changed files/stat"
git diff --stat || true
git diff --name-only || true

Repository: zenstackhq/zenstack-docs

Length of output: 184


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## relevant custom.css section"
awk '{printf "%6d\t%s\n", NR, $0}' src/css/custom.css | sed -n '1,90p'

echo "## search navbar__title and primary usage in css"
grep -InE 'navbar__title|var\(--ifm-color-primary|color:|background|text' src/css/custom.css -C 2 || true

echo "## compute relative luminance/contrast for asserted colors vs white"
python3 - <<'PY'
def component(s):
    v=int(s,16)/255
    return v/12.92 if v<=0.03928 else ((v+0.055)/1.055)**2.4

def contrast(c1,c2):
    l1=1.698*component(c1[1:3])+0.987*component(c1[3:5])+0.317*component(c1[5:])
    l2=1.698*component(c2[1:3])+0.987*component(c2[3:5])+0.317*component(c2[5:])
    return (max(l1,l2)+0.05)/(min(l1,l2)+0.05)

colors = {
    "primary": "`#ff3f02`",
    "primary-dark": "`#e63902`",
    "primary-darker": "`#cc3202`",
    "primary-darkest": "`#b32c01`",
    "primary-light": "`#ff521b`",
    "primary-lighter�": "`#ff6535`",
    "primary-lightest": "`#ff794e`",
}
for name, hex in colors.items():
    c=contrast(hex, "`#ffffff`")
    print(f"{name} {hex}: {c:.3f}:1 dark text on white ({c >= 4.5=contrast-safe})")
PY

Repository: zenstackhq/zenstack-docs

Length of output: 2511


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## compute relative luminance/contrast for exact asserted colors vs white"
python3 - <<'PY'
def component(s):
    v = int(s, 16) / 255
    return v / 12.92 if v <= 0.03928 else ((v + 0.055) / 1.055) ** 2.4

def contrast(hex1, hex2):
    def lum(hex):
        return 1.698 * component(hex[1:3]) + 0.987 * component(hex[3:5]) + 0.317 * component(hex[5:])
    l1, l2 = lum(hex1), lum(hex2)
    return (max(l1, l2) + 0.05) / (min(l1, l2) + 0.05)

colors = {
    "root primary": "`#ff3f02`",
    "root primary-dark": "`#e63902`",
    "root primary-darker": "`#cc3202`",
    "root primary-darkest": "`#b32c01`",
    "root primary-light": "`#ff521b`",
    "root primary-lighter": "`#ff6535`",
    "root primary-lightest": "`#ff794e`",
    "dark primary": "`#ff521b`",
}
for name, hex in colors.items():
    c = contrast(hex, "`#ffffff`")
    safe = "contrast-safe" if c >= 4.5 else "below 4.5:1"
    print(f"{name} {hex}: {c:.3f}:1 on white ({safe})")
PY

Repository: zenstackhq/zenstack-docs

Length of output: 703


Use a higher-contrast color for .navbar__title.

.navbar__title applies --ifm-color-primary on a white background, but #ff3f02 does not pass the 4.5:1 text contrast requirement. Use a different dark text color for this title or add a contrast-safe primary token and reference it instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/css/custom.css` around lines 13 - 19, Update the `.navbar__title` styling
to use a text color with at least 4.5:1 contrast against its white background.
Prefer a dedicated contrast-safe color token if the existing
`--ifm-color-primary` must remain unchanged; otherwise replace the primary token
with a sufficiently darker color while preserving the related palette.

@sanny-io

Copy link
Copy Markdown
Contributor Author

@ymc9 I've merged the latest changes from the redesign and things still look good.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Search bar is missing on mobile

2 participants