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
5 changes: 5 additions & 0 deletions docs/app/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ repository root.
build output and must not be committed.
- Prefer a little consumer-side duplication when a shared API would otherwise
become complex or couple unrelated sites.
- Keep the docs-only marketing brand layer in `assets/docs-theme.css`,
`assets/docs-header-theme.css`, and `assets/docs-overlays-theme.css`, following
Reflex PR #7549. These opt into the shared `docs-*` hooks without copying the
shared base theme or component implementations. Preserve live chart styles,
reduced-motion handling, and both color modes when updating this layer.

## Verification

Expand Down
25 changes: 19 additions & 6 deletions docs/app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,22 @@ the sitemap, Markdown aliases, and internal documentation links.

## Working on the shared Reflex theme

The theme, button and search assets, navbar frame, announcement, sidebar shell,
and footer primitives come directly from `reflex-site-shared`. Do not copy them
into XY. XY owns its wordmark, navigation destinations, page actions, and footer
links.
The base theme, button and search assets, navbar frame, announcement, sidebar
shell, and footer primitives come directly from `reflex-site-shared`. Do not
copy those implementations into XY. XY owns its wordmark, navigation
destinations, page actions, footer links, and docs-specific brand layer.

The three `assets/docs-*-theme.css` / `assets/docs-theme.css` stylesheets adapt
the docs-only styling from [Reflex PR #7549](https://github.com/reflex-dev/reflex/pull/7549):
Inter body text, Geist headings, warm neutral light/dark palettes, compact
controls, and the opal header artwork. They are loaded after the shared base
theme. The header styles reuse the shared announcement markup and dismissal
state; the artwork URL includes the `/docs/xy` mount prefix. Fonts are
bundled from the pinned Fontsource frontend packages, without a font CDN.
Editorial styles use shared `docs-*` hooks and the hooks added by
`xy_docs_layout`; keep them scoped so live chart examples retain their own
typography and data colors. The shared Markdown layout still owns navigation
destinations, while XY adds a two-column Back/Next treatment.

To preview unpublished edits from a Reflex checkout, first install the locked
XY docs environment, then install just the shared package editably (run from
Expand All @@ -94,5 +106,6 @@ avoids generating upstream stubs just to preview the site. Use `--no-sync` for
subsequent commands, and restart after shared Python or theme asset changes.
`uv sync --frozen --group dev` restores the published Git dependency. The local
editable installation is intentionally not a machine-specific lockfile change.
The neutral recolor requires the updated shared package; refresh the Git lock
once those upstream changes are published before deploying this styling.
The committed Git lock includes the shared docs styling hooks and named mobile
drawer from the merged design PR. Refresh all Reflex monorepo packages together
when updating that revision, then run the production checks above.
203 changes: 203 additions & 0 deletions docs/app/assets/docs-header-theme.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
/* XY docs header brand layer, adapted from reflex-dev/reflex#7549. */
/* Docs keep a 64px fixed header and a 40px announcement contribution.
The banner artwork and glass treatment match the marketing site. */
.docs-nav-frame {
left: 0;
--docs-control-shadow: 0 0 0 .5px rgb(0 0 0 / 12.5%), 0 1px 1px rgb(0 0 0 / 4%), 0 2px 4px rgb(0 0 0 / 4%);
}
.docs-nav-frame > .docs-navbar-header {
height: 64px;
padding-inline: 32px;
background: var(--background);
border-bottom: 1px solid var(--border-subtle);
border-radius: 0;
box-shadow: none;
transition: border-radius 200ms ease, background-color 200ms ease;
}
.docs-nav-frame:has([data-announcement]) > .docs-navbar-header {
margin-top: -24px;
border-radius: 24px 24px 0 0;
}
.docs-nav-frame > .docs-navbar-header > .docs-navbar-inner {
max-width: 1536px;
}
.docs-navbar-brand { gap: 12px; }
.docs-navbar-mobile-panel {
padding: 12px 24px 20px;
border-bottom: 1px solid var(--border);
border-radius: 0 0 24px 24px;
box-shadow: 0 12px 24px -16px rgb(0 0 0 / 20%);
}
.docs-navbar-mobile-link { padding-inline: 8px; }
.docs-navbar-mobile-link svg { color: var(--muted-foreground); }

/* The in-page mobile drawer shares the desktop sidebar's typography. */
.docs-sidebar-drawer { font-family: var(--font-sans); color: var(--foreground); }
.docs-sidebar-drawer p,
.docs-sidebar-drawer h3,
.docs-sidebar-drawer summary { font-weight: 400; }
.docs-sidebar-drawer h2 {
font-family: var(--font-sans);
font-size: 12px;
font-weight: 500;
letter-spacing: .025em;
text-transform: none;
}
.docs-sidebar-drawer .docs-sidebar-drawer-title {
font-size: 14px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0;
}
.docs-sidebar-drawer #sidebar-container { min-height: 0; }
.docs-sidebar-drawer #sidebar-container > div { padding-left: 16px; padding-right: 20px; }
.docs-sidebar-drawer a:where(:not([aria-current='page'], [aria-current='true'])) p {
color: var(--muted-foreground);
}
.docs-sidebar-drawer a:hover p { color: var(--foreground); }
.docs-sidebar-drawer a[aria-current='page'] p { font-weight: 500; }
.docs-sidebar-drawer a:focus-visible,
.docs-sidebar-drawer summary:focus-visible {
outline: 2px solid var(--ring);
outline-offset: 3px;
}
@layer utilities {
html .docs-sidebar-drawer {
top: var(--docs-header-height) !important;
height: calc(100dvh - var(--docs-header-height)) !important;
}
}

.docs-nav-frame [data-announcement] {
position: relative;
isolation: isolate;
height: 64px;
padding-bottom: 24px;
width: 100%;
overflow: hidden;
background: #e8e7f1;
color: #000;
border: 0;
font-family: Inter, Arial, sans-serif;
}
/* Shared banner content, links, and state stay in reflex-site-shared. */
.docs-nav-frame [data-announcement]::before {
content: '';
position: absolute;
z-index: -2;
inset: -48px -12%;
pointer-events: none;
background: url('/docs/xy/header/opal.avif') center 38% / cover no-repeat;
filter: blur(6px);
animation: docs-announcement-glass-drift 26s ease-in-out infinite alternate;
}
.docs-nav-frame [data-announcement]::after {
content: '';
position: absolute;
inset: 0;
z-index: -1;
pointer-events: none;
background: radial-gradient(ellipse 34% 90% at 50% 36%, rgb(255 255 255 / 30%), transparent),
linear-gradient(180deg, rgb(255 255 255 / 28%), transparent 45%);
box-shadow: inset 0 1px 0 rgb(255 255 255 / 75%);
}
.docs-nav-frame [data-announcement] > a {
display: block;
height: 100%;
width: 100%;
color: #000;
background: transparent;
}
.docs-nav-frame [data-announcement] > a > span:first-child {
display: flex;
height: 100%;
width: 100%;
margin-inline: auto;
padding-inline: 48px;
align-items: center;
justify-content: center;
gap: 10px;
}
.docs-nav-frame [data-announcement] > a > span:first-child > span:first-child {
display: inline-flex;
flex-shrink: 0;
align-items: center;
height: 20px;
padding-inline: 8px;
border-radius: 999px;
background: #000;
color: #fff;
font: 400 12px/18px Inter, Arial, sans-serif;
}
.docs-nav-frame [data-announcement] > a > span:first-child > span:nth-child(2) {
color: #000;
font: 400 14px/20px Inter, Arial, sans-serif;
white-space: nowrap;
}
.docs-nav-frame [data-announcement] > a > span:first-child > span:nth-child(3) {
display: inline-flex;
align-items: center;
gap: 6px;
flex-shrink: 0;
padding: 5px 12px;
border-radius: 999px;
background: #fff;
color: #000;
font: 400 13px/20px Inter, Arial, sans-serif;
box-shadow: var(--docs-control-shadow);
white-space: nowrap;
}
.docs-nav-frame [data-announcement] > button {
position: absolute;
/* The shared button's -translate-y-1/2 centers its 28px box at y=20. */
top: 20px;
Comment thread
carlosabadia marked this conversation as resolved.
Resolved
right: 16px;
display: inline-flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
border: 0;
border-radius: 999px;
background: transparent;
color: #000;
cursor: pointer;
}
.docs-nav-frame [data-announcement] > button:hover { background: rgb(255 255 255 / 35%); }
.docs-nav-frame [data-announcement] > a:focus-visible,
.docs-nav-frame [data-announcement] > button:focus-visible { outline: 2px solid #000; }
.docs-nav-frame [data-announcement] > a:focus-visible { outline-offset: -4px; }
.docs-nav-frame [data-announcement] > button:focus-visible { outline-offset: 2px; }
@keyframes docs-announcement-glass-drift {
from { transform: translate3d(-5%, 0, 0) scale(1.04); }
to { transform: translate3d(5%, 0, 0) scale(1); }
}
@media (max-width: 1279px) {
.docs-nav-frame:has(.docs-navbar-menu[open]) > .docs-navbar-header {
border-radius: 0;
}
}
@media (max-width: 640px) {
.docs-nav-frame [data-announcement] > a > span:first-child > span:nth-child(3) { display: none; }
}
@media (max-width: 639px) {
.docs-nav-frame > .docs-navbar-header { padding-inline: 16px; }
.docs-navbar-inner { gap: 16px; }
.docs-navbar-brand { gap: 10px; }
.docs-navbar-mobile-panel { padding-inline: 16px; }
.docs-nav-frame [data-announcement] > a > span:first-child { padding-inline: 40px; gap: 8px; }
.docs-nav-frame [data-announcement] > button { right: 8px; }
}
@media (max-width: 379px) {
.docs-nav-frame [data-announcement] > a > span:first-child > span:nth-child(2) { font-size: 13px; }
}
@media (prefers-reduced-motion: reduce) {
.docs-nav-frame [data-announcement]::before { animation: none; }
.docs-nav-frame > .docs-navbar-header,
.docs-navbar-mobile-link { transition: none; }
}

.docs-nav-frame [data-announcement] > a > span:first-child > span:first-child { border: 0; text-transform: none; letter-spacing: normal; }
.docs-nav-frame [data-announcement] .announcement-shimmer { animation: none; background: none; -webkit-text-fill-color: currentColor; }
.docs-nav-frame [data-announcement] .announcement-shimmer::after { content: none; }
.docs-nav-frame [data-announcement] > a:hover { background: transparent; }
Loading
Loading