Skip to content
Open
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
116 changes: 116 additions & 0 deletions packages/chronicle/src/themes/default/Layout.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -279,3 +279,119 @@
line-height: var(--rs-line-height-mini);
flex-shrink: 0;
}

.mobileMenuBtn {
display: none;
align-items: center;
justify-content: center;
background: none;
border: none;
cursor: pointer;
padding: var(--rs-space-1);
color: var(--rs-color-foreground-base-primary);
}

.mobileHeader {
display: none;
align-items: center;
justify-content: space-between;
height: var(--navbar-height);
padding: 0 var(--rs-space-5);
background: var(--rs-color-background-base-primary);
border-bottom: 0.5px solid var(--rs-color-border-base-primary);
backdrop-filter: blur(1px);
}

.mobileMenu {
display: none;
position: fixed;
top: var(--navbar-height);
left: 0;
right: 0;
bottom: 0;
z-index: 100;
background: var(--rs-color-background-base-primary);
overflow-y: auto;
padding: var(--rs-space-7) var(--rs-space-5);
}

.mobileMenu[data-open='true'] {
display: block;
}

.mobileNav {
display: none;
}

@media (max-width: 768px) {
.sidebar {
display: none;
}

.mobileHeader {
display: flex;
}

.mobileMenuBtn {
display: flex;
}

.mobileMenu[data-open='true'] {
display: block;
}

.subNav {
display: none;
}

.content {
padding: var(--rs-space-10) var(--rs-space-5);
}

.card {
width: 100%;
border-left: none;
box-shadow: none;
}

.cardWrapper {
padding: 0;
}

.mobileNav {
display: flex;
gap: var(--rs-space-10);
padding: var(--rs-space-3) var(--rs-space-5);
background: var(--rs-color-background-base-primary);
}

.mobileNavLink {
flex: 1;
display: flex;
align-items: center;
gap: var(--rs-space-3);
padding: var(--rs-space-4) var(--rs-space-3);
border: 0.5px solid var(--rs-color-border-base-primary);
border-radius: var(--rs-radius-4);
text-decoration: none;
font-family: var(--rs-font-body);
font-size: var(--rs-font-size-regular);
font-weight: var(--rs-font-weight-medium);
line-height: var(--rs-line-height-regular);
letter-spacing: var(--rs-letter-spacing-regular);
color: var(--rs-color-foreground-base-tertiary);
min-width: 0;
}

.mobileNavLink[data-direction='next'] {
justify-content: flex-end;
background: var(--rs-color-background-base-secondary);
color: var(--rs-color-foreground-base-primary);
}

.mobileNavLabel {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
81 changes: 80 additions & 1 deletion packages/chronicle/src/themes/default/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import {
CodeBracketSquareIcon,
RectangleStackIcon,
DocumentTextIcon,
Squares2X2Icon
Squares2X2Icon,
Bars3Icon,
XMarkIcon
} from '@heroicons/react/24/outline';
import { Flex, IconButton, Button, Sidebar } from '@raystack/apsara';
import { PlayIcon } from '@radix-ui/react-icons';
Expand Down Expand Up @@ -70,6 +72,7 @@ export function Layout({
const navigate = useNavigate();
const { page, version } = usePageContext();
const scrollRef = useRef<HTMLDivElement>(null);
const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
const isApiRoute = pathname === '/apis' || pathname.startsWith('/apis/');
const isApiBase = (basePath: string) =>
pathname === basePath || pathname.startsWith(`${basePath}/`);
Expand Down Expand Up @@ -106,10 +109,72 @@ export function Layout({
requestAnimationFrame(() => {
el.scrollTop = savedScrollTop;
});
setMobileSidebarOpen(false);
}, [pathname]);

return (
<Flex direction='column' className={cx(styles.layout, classNames?.layout)}>
<div className={styles.mobileHeader}>
<SidebarLogo config={config} />
<Flex align='center' gap={3}>
{config.search?.enabled && <Search />}
<ClientThemeSwitcher size={16} />
<button
className={styles.mobileMenuBtn}
onClick={() => setMobileSidebarOpen(o => !o)}
aria-label={mobileSidebarOpen ? 'Close menu' : 'Open menu'}
>
{mobileSidebarOpen
? <XMarkIcon width={16} height={16} />
: <Bars3Icon width={16} height={16} />}
</button>
Comment thread
rsbh marked this conversation as resolved.
</Flex>
</div>
<div className={styles.mobileMenu} data-open={mobileSidebarOpen}>
Comment on lines +122 to +133
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add explicit menu-toggle semantics and prevent implicit form submit.

Please add type="button" on the toggle, plus aria-expanded and aria-controls (with a matching id on the mobile menu) so assistive tech can track the menu state correctly.

Suggested patch
-          <button
+          <button
+            type='button'
             className={styles.mobileMenuBtn}
             onClick={() => setMobileSidebarOpen(o => !o)}
             aria-label={mobileSidebarOpen ? 'Close menu' : 'Open menu'}
+            aria-expanded={mobileSidebarOpen}
+            aria-controls='mobile-navigation'
           >
@@
-      <div className={styles.mobileMenu} data-open={mobileSidebarOpen}>
+      <div id='mobile-navigation' className={styles.mobileMenu} data-open={mobileSidebarOpen}>
🤖 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 `@packages/chronicle/src/themes/default/Layout.tsx` around lines 122 - 133, The
mobile menu toggle button (styles.mobileMenuBtn) currently lacks explicit button
semantics and accessible state; update the button rendered where
setMobileSidebarOpen and mobileSidebarOpen are used to include type="button",
aria-expanded={mobileSidebarOpen} and aria-controls set to a new stable id
(e.g., "mobile-menu"); also add that same id to the mobile menu element
(styles.mobileMenu) so assistive tech can associate the control with the menu
and avoid implicit form submission.

{showTopLinks ? (
<div className={styles.topLinks}>
{contentEntries.map(entry => (
<Sidebar.Item
key={entry.href}
href={entry.href}
active={activeContentDir === entry.contentDir}
leadingIcon={renderConfigIcon(entry.icon, entry.label, <DocumentTextIcon width={16} height={16} />)}
classNames={{ root: styles.topLinkItem, text: styles.topLinkText }}
render={<RouterLink to={entry.href} />}
>
{entry.label}
</Sidebar.Item>
))}
{apiEntries.map(api => (
<Sidebar.Item
key={`${api.basePath}-${api.name}`}
href={api.basePath}
active={isApiBase(api.basePath)}
leadingIcon={renderConfigIcon(api.icon, api.name, <CodeBracketSquareIcon width={16} height={16} />)}
classNames={{ root: styles.topLinkItem, text: styles.topLinkText }}
render={<RouterLink to={api.basePath} />}
>
{api.name} API
</Sidebar.Item>
))}
</div>
) : null}
{tree.children.map((item, i) => (
isApiRoute ? (
<ApiSidebarNode
key={item.type === 'page' ? item.url : (item.name?.toString() ?? i)}
item={item}
pathname={pathname}
/>
) : (
<SidebarNode
key={item.type === 'page' ? item.url : (item.name?.toString() ?? i)}
item={item}
pathname={pathname}
/>
)
))}
</div>
<Flex className={cx(styles.body, classNames?.body)}>
{hideSidebar ? null : (
<Sidebar
Expand Down Expand Up @@ -218,6 +283,20 @@ export function Layout({
<main className={cx(styles.content, classNames?.content)}>
{children}
</main>
<div className={styles.mobileNav}>
{prev ? (
<RouterLink to={prev.url} className={styles.mobileNavLink}>
<ArrowLeftIcon width={16} height={16} />
<span className={styles.mobileNavLabel}>{prev.title}</span>
</RouterLink>
) : <div />}
{next ? (
<RouterLink to={next.url} className={styles.mobileNavLink} data-direction='next'>
<span className={styles.mobileNavLabel}>{next.title}</span>
<ArrowRightIcon width={16} height={16} />
</RouterLink>
) : <div />}
</div>
</div>
</div>
</Flex>
Expand Down
14 changes: 14 additions & 0 deletions packages/chronicle/src/themes/default/Page.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,17 @@
.headerLoader {
margin-bottom: var(--rs-space-5);
}

@media (max-width: 768px) {
.page {
gap: var(--rs-space-5);
}

.article {
max-width: 100%;
}

.title {
margin-bottom: var(--rs-space-5);
}
}
Loading