Skip to content

Commit 73b5a9b

Browse files
committed
docs-site: liquid-glass redesign — aside-grammar landing, pill header, ima2 hero fields, SEO
- Landing.astro shared by en/ko/zh-cn: rounded hero field with ima2-generated soft-focus sky/dusk photos (astro:assets, gradient fallback), left-aligned type, pill CTAs, provider chips, in-field dashboard stage (refreshed v2.6.32 screenshot), quickstart terminal band, feature bento with SVG icons, full docs sitemap band, quiet disclaimer - Header.astro override: floating glass pill (max-w 78rem, blur 40px frosted), desktop hover+click dropdown nav with aria-expanded/controls + Esc close, mobile hamburger clearance; PageTitle override keeps single h1#_top on splash - custom.css: monochrome light/dark tokens bridged from the GUI design system, glass chrome (header/sidebar/search/dropdowns), solid content cards, scroll- driven motions (view() timeline) gated by prefers-reduced-motion - fonts: Geist Variable + Pretendard Variable via customCss module ids - SEO: robots.txt + sitemap pointer, JSON-LD WebSite/SoftwareApplication, theme-color, per-landing og:locale (en_US/ko_KR/zh_CN), offer-form titles - ko translation for guides/sub-agent-surface (GUI help modal deep link)
1 parent 8e63e73 commit 73b5a9b

15 files changed

Lines changed: 1398 additions & 297 deletions

File tree

docs-site/astro.config.mjs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,37 @@ import starlight from "@astrojs/starlight";
44

55
// Project GitHub Pages site: https://lidge-jun.github.io/opencodex
66
// `site` + `base` make Starlight emit correct absolute URLs and asset paths under the repo subpath.
7+
const SITE_URL = "https://lidge-jun.github.io/opencodex";
8+
9+
// JSON-LD: WebSite + SoftwareApplication (docs SEO baseline; canonical/og/sitemap
10+
// are emitted by Starlight itself).
11+
const jsonLd = JSON.stringify({
12+
"@context": "https://schema.org",
13+
"@graph": [
14+
{
15+
"@type": "WebSite",
16+
"@id": `${SITE_URL}/#website`,
17+
url: `${SITE_URL}/`,
18+
name: "opencodex",
19+
description:
20+
"Universal provider proxy for OpenAI Codex — use any LLM with Codex CLI, App, and SDK.",
21+
inLanguage: ["en", "ko", "zh-CN"],
22+
},
23+
{
24+
"@type": "SoftwareApplication",
25+
"@id": `${SITE_URL}/#software`,
26+
name: "opencodex",
27+
alternateName: "ocx",
28+
applicationCategory: "DeveloperApplication",
29+
operatingSystem: "macOS, Linux, Windows",
30+
offers: { "@type": "Offer", price: "0", priceCurrency: "USD" },
31+
softwareHelp: { "@type": "CreativeWork", url: `${SITE_URL}/` },
32+
downloadUrl: "https://www.npmjs.com/package/@bitkyc08/opencodex",
33+
url: "https://github.com/lidge-jun/opencodex",
34+
},
35+
],
36+
});
37+
738
export default defineConfig({
839
site: "https://lidge-jun.github.io",
940
base: "/opencodex",
@@ -20,12 +51,24 @@ export default defineConfig({
2051
replacesTitle: false,
2152
},
2253
favicon: "/favicon.png",
54+
customCss: [
55+
"@fontsource-variable/geist",
56+
"pretendard/dist/web/variable/pretendardvariable-dynamic-subset.css",
57+
"./src/styles/custom.css",
58+
],
59+
components: {
60+
Header: "./src/components/Header.astro",
61+
PageTitle: "./src/components/PageTitle.astro",
62+
},
2363
head: [
2464
{ tag: "meta", attrs: { property: "og:image", content: "https://lidge-jun.github.io/opencodex/og.png" } },
2565
{ tag: "meta", attrs: { property: "og:image:width", content: "1200" } },
2666
{ tag: "meta", attrs: { property: "og:image:height", content: "630" } },
2767
{ tag: "meta", attrs: { name: "twitter:card", content: "summary_large_image" } },
2868
{ tag: "meta", attrs: { name: "twitter:image", content: "https://lidge-jun.github.io/opencodex/og.png" } },
69+
{ tag: "meta", attrs: { name: "theme-color", media: "(prefers-color-scheme: light)", content: "#ffffff" } },
70+
{ tag: "meta", attrs: { name: "theme-color", media: "(prefers-color-scheme: dark)", content: "#212121" } },
71+
{ tag: "script", attrs: { type: "application/ld+json" }, content: jsonLd },
2972
],
3073
social: [
3174
{ icon: "github", label: "GitHub", href: "https://github.com/lidge-jun/opencodex" },

docs-site/bun.lock

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs-site/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
},
1313
"dependencies": {
1414
"@astrojs/starlight": "^0.41.1",
15+
"@fontsource-variable/geist": "^5.2.9",
1516
"astro": "^7.0.3",
17+
"pretendard": "^1.3.9",
1618
"sharp": "^0.35.2"
1719
}
1820
}

docs-site/public/robots.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
User-agent: *
2+
Allow: /
3+
4+
Sitemap: https://lidge-jun.github.io/opencodex/sitemap-index.xml

docs-site/src/assets/dashboard.png

149 KB
Loading
2.2 MB
Loading
2.75 MB
Loading
Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
---
2+
// Custom Starlight header: default chrome (title / search / social / theme /
3+
// language) plus a desktop-only hover-dropdown nav in the liquid-glass grammar.
4+
// Mobile (<72rem) hides the nav; the built-in hamburger flow is untouched.
5+
import LanguageSelect from '@astrojs/starlight/components/LanguageSelect.astro';
6+
import Search from '@astrojs/starlight/components/Search.astro';
7+
import SiteTitle from '@astrojs/starlight/components/SiteTitle.astro';
8+
import SocialIcons from '@astrojs/starlight/components/SocialIcons.astro';
9+
import ThemeSelect from '@astrojs/starlight/components/ThemeSelect.astro';
10+
11+
const { locale } = Astro.locals.starlightRoute;
12+
// Root locale is `undefined`; BASE_URL already ends with a slash.
13+
const base = import.meta.env.BASE_URL.replace(/\/?$/, '/');
14+
const prefix = locale ? `${base}${locale}/` : base;
15+
16+
type NavLink = { label: string; href: string };
17+
type NavGroup = { label: string; links: NavLink[] };
18+
19+
const t = (en: string, ko: string, zh: string) =>
20+
locale === 'ko' ? ko : locale === 'zh-cn' ? zh : en;
21+
22+
const groups: NavGroup[] = [
23+
{
24+
label: t('Getting Started', '시작하기', '开始使用'),
25+
links: [
26+
{ label: t('Installation', '설치', '安装'), href: `${prefix}getting-started/installation/` },
27+
{ label: t('Quickstart', '빠른 시작', '快速开始'), href: `${prefix}getting-started/quickstart/` },
28+
{ label: t('How It Works', '동작 원리', '工作原理'), href: `${prefix}getting-started/how-it-works/` },
29+
],
30+
},
31+
{
32+
label: t('Guides', '가이드', '指南'),
33+
links: [
34+
{ label: t('Providers', '프로바이더', '提供商'), href: `${prefix}guides/providers/` },
35+
{ label: t('Model Routing', '모델 라우팅', '模型路由'), href: `${prefix}guides/model-routing/` },
36+
{ label: t('Codex Integration', 'Codex 통합', 'Codex 集成'), href: `${prefix}guides/codex-integration/` },
37+
{ label: t('Codex App Model Picker', 'Codex App 모델 선택기', 'Codex App 模型选择器'), href: `${prefix}guides/codex-app-models/` },
38+
{ label: t('Sidecars: Search & Vision', '사이드카: 검색 & 비전', '边车:搜索与视觉'), href: `${prefix}guides/sidecars/` },
39+
{ label: t('Web Dashboard', '웹 대시보드', '网页控制台'), href: `${prefix}guides/web-dashboard/` },
40+
{ label: t('Sub-agent Surface', '서브에이전트 서피스', '子代理界面'), href: `${prefix}guides/sub-agent-surface/` },
41+
],
42+
},
43+
{
44+
label: t('Reference', '레퍼런스', '参考'),
45+
links: [
46+
{ label: 'CLI', href: `${prefix}reference/cli/` },
47+
{ label: t('Configuration', '설정', '配置'), href: `${prefix}reference/configuration/` },
48+
{ label: t('Adapters', '어댑터', '适配器'), href: `${prefix}reference/adapters/` },
49+
{ label: t('Architecture', '아키텍처', '架构'), href: `${prefix}reference/architecture/` },
50+
],
51+
},
52+
];
53+
54+
const contributing: NavLink = {
55+
label: t('Contributing', '기여하기', '贡献'),
56+
href: `${prefix}contributing/`,
57+
};
58+
---
59+
60+
<div class="header">
61+
<div class="title-wrapper sl-flex">
62+
<SiteTitle />
63+
</div>
64+
<nav class="glass-nav print:hidden" aria-label={t('Main', '주 메뉴', '主导航')}>
65+
<ul>
66+
{
67+
groups.map((group, i) => (
68+
<li class="nav-item">
69+
<button type="button" class="nav-link" aria-haspopup="true" aria-expanded="false" aria-controls={`nav-drop-${i}`}>
70+
{group.label}
71+
<svg viewBox="0 0 16 16" width="10" height="10" fill="none" aria-hidden="true">
72+
<path d="M3.5 6l4.5 4.5L12.5 6" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" />
73+
</svg>
74+
</button>
75+
<div class="nav-drop" id={`nav-drop-${i}`}>
76+
<ul>
77+
{group.links.map((link) => (
78+
<li>
79+
<a href={link.href}>{link.label}</a>
80+
</li>
81+
))}
82+
</ul>
83+
</div>
84+
</li>
85+
))
86+
}
87+
<li class="nav-item">
88+
<a class="nav-link" href={contributing.href}>{contributing.label}</a>
89+
</li>
90+
</ul>
91+
</nav>
92+
<div class="sl-flex print:hidden search-wrapper">
93+
<Search />
94+
</div>
95+
<div class="sl-hidden md:sl-flex print:hidden right-group">
96+
<div class="sl-flex social-icons">
97+
<SocialIcons />
98+
</div>
99+
<ThemeSelect />
100+
<LanguageSelect />
101+
</div>
102+
</div>
103+
104+
<style>
105+
.header {
106+
display: flex;
107+
gap: var(--sl-nav-gap);
108+
justify-content: space-between;
109+
align-items: center;
110+
height: 100%;
111+
/* floating glass pill, detached from the viewport edges */
112+
border-radius: var(--ocx-radius-pill, 999px);
113+
border: 1px solid var(--sl-color-hairline-light);
114+
background-color: var(--ocx-glass-rail, canvas);
115+
backdrop-filter: var(--ocx-glass-blur, blur(22px));
116+
-webkit-backdrop-filter: var(--ocx-glass-blur, blur(22px));
117+
box-shadow: 0 4px 24px rgb(0 0 0 / 0.07);
118+
padding-inline: 1rem;
119+
}
120+
121+
.title-wrapper {
122+
overflow: clip;
123+
padding: 0.25rem;
124+
margin: -0.25rem;
125+
min-width: 0;
126+
}
127+
128+
/* below the sidebar breakpoint the fixed hamburger overlays the pill's right
129+
end — reserve room so search and the menu button never collide */
130+
@media (max-width: 49.99rem) {
131+
:global([data-has-sidebar]) .header {
132+
padding-inline-end: calc(var(--sl-menu-button-size) + 0.75rem);
133+
}
134+
}
135+
136+
.right-group,
137+
.social-icons {
138+
gap: 1rem;
139+
align-items: center;
140+
}
141+
.social-icons::after {
142+
content: '';
143+
height: 2rem;
144+
border-inline-end: 1px solid var(--sl-color-gray-5);
145+
}
146+
147+
/* ---- hover-dropdown nav (desktop only) ---- */
148+
.glass-nav {
149+
display: none;
150+
}
151+
152+
@media (min-width: 72rem) {
153+
.glass-nav {
154+
display: block;
155+
}
156+
.glass-nav > ul {
157+
display: flex;
158+
align-items: center;
159+
gap: 0.125rem;
160+
list-style: none;
161+
margin: 0;
162+
padding: 0;
163+
}
164+
.nav-item {
165+
position: relative;
166+
}
167+
.nav-link {
168+
display: inline-flex;
169+
align-items: center;
170+
gap: 0.375rem;
171+
padding: 0.375rem 0.875rem;
172+
border: 0;
173+
border-radius: var(--ocx-radius-pill, 999px);
174+
background: transparent;
175+
color: var(--sl-color-gray-2);
176+
font: inherit;
177+
font-size: var(--sl-text-sm);
178+
font-weight: 500;
179+
line-height: 1;
180+
text-decoration: none;
181+
cursor: pointer;
182+
white-space: nowrap;
183+
transition: background-color 0.12s, color 0.12s;
184+
}
185+
.nav-link svg { opacity: 0.55; transition: transform 0.15s; }
186+
.nav-item:hover .nav-link,
187+
.nav-item:focus-within .nav-link {
188+
background-color: var(--ocx-hover, rgba(128, 128, 128, 0.1));
189+
color: var(--sl-color-white);
190+
}
191+
.nav-item:hover .nav-link svg,
192+
.nav-item:focus-within .nav-link svg { transform: rotate(180deg); }
193+
194+
.nav-drop {
195+
position: absolute;
196+
top: 100%;
197+
left: 50%;
198+
translate: -50% 0;
199+
padding-top: 0.625rem;
200+
opacity: 0;
201+
visibility: hidden;
202+
transition: opacity 0.16s ease, visibility 0.16s, translate 0.16s ease;
203+
z-index: var(--sl-z-index-navbar);
204+
}
205+
.nav-item:hover .nav-drop,
206+
.nav-item:focus-within .nav-drop,
207+
.nav-item:has(> .nav-link[aria-expanded='true']) .nav-drop {
208+
opacity: 1;
209+
visibility: visible;
210+
translate: -50% 0.25rem;
211+
}
212+
.nav-drop > ul {
213+
list-style: none;
214+
margin: 0;
215+
padding: 0.375rem;
216+
min-width: 15rem;
217+
border: 1px solid var(--sl-color-gray-5);
218+
border-radius: var(--ocx-radius, 12px);
219+
background-color: var(--ocx-glass-panel, canvas);
220+
backdrop-filter: var(--ocx-glass-blur, blur(22px));
221+
-webkit-backdrop-filter: var(--ocx-glass-blur, blur(22px));
222+
box-shadow: var(--ocx-shadow, 0 10px 28px rgba(0, 0, 0, 0.2));
223+
}
224+
.nav-drop a {
225+
display: block;
226+
padding: 0.5rem 0.75rem;
227+
border-radius: var(--ocx-radius-sm, 8px);
228+
color: var(--sl-color-gray-2);
229+
font-size: var(--sl-text-sm);
230+
text-decoration: none;
231+
transition: background-color 0.12s, color 0.12s;
232+
}
233+
.nav-drop a:hover,
234+
.nav-drop a:focus-visible {
235+
background-color: var(--ocx-hover, rgba(128, 128, 128, 0.1));
236+
color: var(--sl-color-white);
237+
}
238+
}
239+
240+
@media (prefers-reduced-motion: reduce) {
241+
.nav-link svg,
242+
.nav-drop {
243+
transition: none;
244+
}
245+
}
246+
</style>
247+
248+
<script>
249+
// Click/Enter/Space toggles the dropdowns (hover and focus-within still work
250+
// via CSS); Escape or an outside click closes any open menu.
251+
const buttons = Array.from(document.querySelectorAll<HTMLButtonElement>('.glass-nav .nav-link[aria-haspopup]'));
252+
const closeAll = (except?: HTMLButtonElement) => {
253+
for (const b of buttons) if (b !== except) b.setAttribute('aria-expanded', 'false');
254+
};
255+
for (const btn of buttons) {
256+
btn.addEventListener('click', () => {
257+
const open = btn.getAttribute('aria-expanded') === 'true';
258+
closeAll(btn);
259+
btn.setAttribute('aria-expanded', String(!open));
260+
});
261+
}
262+
document.addEventListener('click', (e) => {
263+
if (!(e.target instanceof Element) || !e.target.closest('.glass-nav')) closeAll();
264+
});
265+
document.addEventListener('keydown', (e) => {
266+
if (e.key === 'Escape') closeAll();
267+
});
268+
</script>

0 commit comments

Comments
 (0)