Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7443d0f
feat: add center and add shadow prop
paanSinghCoder Feb 26, 2026
7809f71
feat: add hideOnScroll, flex props
paanSinghCoder Feb 27, 2026
8fa1c84
docs: update Navbar documentation to include Flex props and new hideO…
paanSinghCoder Feb 27, 2026
df4e7bd
refactor: remove outdated comment from NavbarRootProps interface
paanSinghCoder Feb 27, 2026
ef62db5
refactor: update Navbar demo sections for clarity and consistency
paanSinghCoder Feb 27, 2026
4f02ce1
feat: enhance DemoPreview component with customizable preview class
paanSinghCoder Feb 27, 2026
8e799ae
feat: enhance Navbar demo with shadow prop and updated styles
paanSinghCoder Feb 27, 2026
33859b9
feat: add hideOnScroll demo to Navbar documentation
paanSinghCoder Feb 27, 2026
7500a58
docs: clarify navbar hideOnScroll behavior in documentation
paanSinghCoder Feb 27, 2026
2602aca
chore: add Navbar into examples page
paanSinghCoder Feb 27, 2026
dcafa37
feat: refactor Navbar layout to use grid system and absolute center t…
paanSinghCoder Feb 27, 2026
64381d7
feat: enhance NavbarRoot component with scroll behavior improvements
paanSinghCoder Feb 27, 2026
403121c
Merge branch 'main' into feat/navbar-improvements-center
paanSinghCoder Mar 18, 2026
75cbe6b
chore: remove global class
paanSinghCoder Mar 18, 2026
17a21a2
docs: styling
paanSinghCoder Mar 18, 2026
2b1be28
refactor: simplify scroll detection code
paanSinghCoder Mar 18, 2026
155212d
feat: add scrollContainerRef prop
paanSinghCoder Mar 18, 2026
70be60b
refactor: add base-ui/utils pkg and use for merging refs
paanSinghCoder Mar 18, 2026
99b5348
refactor: remove state for tracking hidden/visible
paanSinghCoder Mar 18, 2026
e57a5c2
chore: remove shadow prop
paanSinghCoder Mar 18, 2026
dc12514
Merge branch 'main' into feat/navbar-improvements-center
paanSinghCoder Mar 20, 2026
40dfe1e
chore: remove conditional group
paanSinghCoder Mar 20, 2026
929ed3a
chore: remove unused class names
paanSinghCoder Mar 20, 2026
8872390
Merge branch 'main' into feat/navbar-improvements-center
paanSinghCoder Mar 20, 2026
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
4,561 changes: 2,322 additions & 2,239 deletions apps/www/src/app/examples/page.tsx

Large diffs are not rendered by default.

18 changes: 15 additions & 3 deletions apps/www/src/components/demo/demo-preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ export default function DemoPreview({
code,
tabs,
scope,
codePreview
codePreview,
previewClassName
Comment thread
paanSinghCoder marked this conversation as resolved.
Outdated
}: DemoPreviewProps) {
const [activeTab, setActiveTab] = useState(0);
const [activeCodePreviewTab, setActiveCodePreviewTab] = useState(0);
Expand All @@ -39,8 +40,19 @@ export default function DemoPreview({
))}
</div>
)}
<div className={styles.preview}>
<Preview />
<div
className={cx(
styles.preview,
previewClassName && styles[previewClassName as keyof typeof styles]
)}
>
<Preview
className={
previewClassName === 'previewTop'
? styles.previewContentTop
: undefined
}
/>
</div>

{Array.isArray(codePreview) ? (
Expand Down
33 changes: 33 additions & 0 deletions apps/www/src/components/demo/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,39 @@
border-bottom: 0.5px solid var(--rs-color-border-base-primary);
}

.previewTop {
align-items: flex-start;
justify-content: flex-start;
padding: 0;
min-height: unset;
}

.previewContentTop {
padding: 0 !important;
align-items: flex-start;
justify-content: flex-start;
flex-direction: column;
}

.previewContentTop > * {
margin-bottom: var(--rs-space-8);
}

.previewContentTop::after {
content: "";
display: block;
width: calc(100% - 2 * var(--rs-space-8));
min-height: 200px;
margin: 0 var(--rs-space-8) var(--rs-space-8);
border: 2px dashed var(--rs-color-border-base-secondary);
box-sizing: border-box;
}

/* Hide dashed box for sticky demo - it has its own scrollable box inside */
.previewContentTop:has(> :global(.navbar-sticky-demo-scroll))::after {
display: none;
}

.previewReset {
position: absolute;
bottom: 8px;
Expand Down
1 change: 1 addition & 0 deletions apps/www/src/components/demo/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export type DemoPreviewProps = {
tabs?: { name: string; code: string }[];
scope?: ScopeType;
codePreview?: string | TabProps[];
previewClassName?: string;
};

export type DemoPlaygroundProps = {
Expand Down
86 changes: 77 additions & 9 deletions apps/www/src/content/docs/components/navbar/demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

export const preview = {
type: 'code',
previewClassName: 'previewTop',
code: `
<Navbar>
<Navbar.Start>
Expand All @@ -21,6 +22,7 @@ export const preview = {

export const stickyDemo = {
type: 'code',
previewClassName: 'previewTop',
tabs: [
{
name: 'Default',
Expand All @@ -39,25 +41,79 @@ export const stickyDemo = {
{
name: 'Sticky',
code: `
<Navbar sticky>
<div className="navbar-sticky-demo-scroll" style={{ width: '100%', alignSelf: 'stretch', overflow: 'auto', height: 300 }}>
<Navbar sticky>
<Navbar.Start>
<Text size="regular" weight="medium">Navigation</Text>
</Navbar.Start>
<Navbar.End>
<Button variant="ghost" size="small">Home</Button>
<Button variant="ghost" size="small">About</Button>
<Button variant="ghost" size="small">Contact</Button>
</Navbar.End>
</Navbar>
<div style={{ margin: 'var(--rs-space-8)', width: 'calc(100% - 2 * var(--rs-space-8))', minHeight: 400, border: '2px dashed var(--rs-color-border-base-secondary)', boxSizing: 'border-box' }} />
</div>`
}
]
};

export const shadowDemo = {
type: 'code',
previewClassName: 'previewTop',
tabs: [
{
name: 'With shadow (default)',
code: `
<Navbar shadow>
<Navbar.Start>
<Text size="regular" weight="medium">Navigation</Text>
<Text size="regular" weight="medium">Brand</Text>
</Navbar.Start>
<Navbar.End>
<Button variant="ghost" size="small">Home</Button>
<Button variant="ghost" size="small">About</Button>
<Button variant="ghost" size="small">Contact</Button>
<Button size="small">Action</Button>
</Navbar.End>
</Navbar>`
},
{
name: 'Without shadow',
code: `
<Navbar shadow={false}>
<Navbar.Start>
<Text size="regular" weight="medium">Brand</Text>
</Navbar.Start>
<Navbar.End>
<Button size="small">Action</Button>
</Navbar.End>
</Navbar>`
}
]
};

export const hideOnScrollDemo = {
type: 'code',
previewClassName: 'previewTop',
code: `
<div className="navbar-sticky-demo-scroll" style={{ width: '100%', alignSelf: 'stretch', overflow: 'auto', height: 300 }}>
<Navbar sticky hideOnScroll>
<Navbar.Start>
<Text size="regular" weight="medium">Navigation</Text>
</Navbar.Start>
<Navbar.End>
<Button variant="ghost" size="small">Home</Button>
<Button variant="ghost" size="small">About</Button>
<Button variant="ghost" size="small">Contact</Button>
</Navbar.End>
</Navbar>
<div style={{ margin: 'var(--rs-space-8)', width: 'calc(100% - 2 * var(--rs-space-8))', minHeight: 400, border: '2px dashed var(--rs-color-border-base-secondary)', boxSizing: 'border-box' }} />
</div>`
};

export const sectionsDemo = {
type: 'code',
previewClassName: 'previewTop',
tabs: [
{
name: 'Start Only',
name: 'Start',
code: `
<Navbar>
<Navbar.Start>
Expand All @@ -66,7 +122,16 @@ export const sectionsDemo = {
</Navbar>`
},
{
name: 'End Only',
name: 'Center',
code: `
<Navbar>
<Navbar.Center>
<Text size="regular" weight="medium">Centered Title</Text>
</Navbar.Center>
</Navbar>`
},
{
name: 'End',
code: `
<Navbar>
<Navbar.End>
Expand All @@ -76,14 +141,16 @@ export const sectionsDemo = {
</Navbar>`
},
{
name: 'Both Sections',
name: 'Start, Center and End',
code: `
<Navbar>
<Navbar.Start>
<Text size="regular" weight="medium">Explore</Text>
</Navbar.Start>
<Navbar.Center>
<Text size="regular" weight="medium">Brand</Text>
</Navbar.Center>
<Navbar.End>
<Search placeholder="Search..." size="small" style={{ width: '200px' }} />
<Button variant="outline" size="small">Action</Button>
</Navbar.End>
</Navbar>`
Expand All @@ -93,6 +160,7 @@ export const sectionsDemo = {

export const accessibilityDemo = {
type: 'code',
previewClassName: 'previewTop',
tabs: [
{
name: 'Custom aria-label',
Expand Down
31 changes: 26 additions & 5 deletions apps/www/src/content/docs/components/navbar/index.mdx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
---
title: Navbar
description: A horizontal navigation bar component with flexible left and right sections for building site headers and navigation.
description: A horizontal navigation bar component with flexible start, center, and end sections for building site headers and navigation.
source: packages/raystack/components/navbar
tag: new
---

import {
preview,
stickyDemo,
shadowDemo,
sectionsDemo,
hideOnScrollDemo,
accessibilityDemo,
} from "./demo.ts";

Expand All @@ -23,6 +25,7 @@ import { Navbar } from "@raystack/apsara";

<Navbar>
<Navbar.Start />
<Navbar.Center />
<Navbar.End />
</Navbar>
```
Expand All @@ -37,13 +40,19 @@ Renders the navigation bar container.

### Start

The start section is a container component that accepts all `div` props. It's commonly used for brand logos, primary navigation links, or page titles.
The start section is a container that accepts Flex props (`align`, `gap`, `direction`, etc.) and section props. It's commonly used for brand logos, primary navigation links, or page titles.

<auto-type-table path="./props.ts" name="NavbarStartProps" />

### Center

The center section sits in the middle of the navbar in a fixed position—it stays absolutely centered regardless of the width of Start or End content. The navbar uses a 3-column grid (Start | Center | End) so the center does not shift when left or right content changes. It accepts Flex props and section props.

<auto-type-table path="./props.ts" name="NavbarCenterProps" />
Comment on lines +47 to +51
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.

⚠️ Potential issue | 🟡 Minor

Navbar.Center's API table is out of sync with the implementation.

The prose says Navbar.Center accepts Flex props, but the generated table comes from apps/www/src/content/docs/components/navbar/props.ts:43-48, where NavbarCenterProps only documents aria-label. In packages/raystack/components/navbar/navbar-sections.tsx:8-40, the real type extends ComponentPropsWithoutRef<typeof Flex>, so align, gap, direction, etc. are currently missing from the reference.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/src/content/docs/components/navbar/index.mdx` around lines 48 - 52,
The docs table for Navbar.Center is wrong — update the props generator/source so
NavbarCenterProps reflects the actual component props: change the type exported
in apps/www/src/content/docs/components/navbar/props.ts from the current minimal
shape (only aria-label) to match the implementation which extends
ComponentPropsWithoutRef<typeof Flex> in
packages/raystack/components/navbar/navbar-sections.tsx; ensure the generated
table includes Flex props such as align, gap, direction, etc., by importing or
deriving the correct type used by Navbar.Center (or re-exporting the real
NavbarCenterProps type) so the MDX <auto-type-table name="NavbarCenterProps" />
shows the complete API.


### End

The end section is a container component that accepts all `div` props. It's commonly used for search inputs, action buttons, user menus, or secondary navigation.
The end section is a container that accepts Flex props and section props. It's commonly used for search inputs, action buttons, user menus, or secondary navigation.

<auto-type-table path="./props.ts" name="NavbarEndProps" />

Expand All @@ -55,12 +64,24 @@ The Navbar can be made sticky to remain visible at the top of the viewport when

<Demo data={stickyDemo} />

### Shadow

Use the `shadow` prop to show or hide the bottom shadow. Default is `true`.

<Demo data={shadowDemo} />

### Section Layouts

You can use either or both sections depending on your needs. The sections automatically position themselves with proper spacing.
You can use any combination of Start, Center, and End. The navbar uses a 3-column grid so each section renders in its own space; the center remains absolutely centered.

<Demo data={sectionsDemo} />

### Hide on scroll

Set `hideOnScroll` to hide the navbar when the user scrolls down and show it when they scroll up. It works with both window scroll and scroll containers (e.g. ScrollArea). The navbar slides out of view with a transition. The slide animation is only visible when the navbar is sticky (or fixed); without it, the navbar simply scrolls away with the content.

<Demo data={hideOnScrollDemo} />

### Accessibility

The Navbar supports custom ARIA labels for better screen reader support. You can provide descriptive labels for the entire navbar or individual sections.
Expand All @@ -73,7 +94,7 @@ The Navbar implements the following accessibility features:

- Proper ARIA roles and attributes
- `role="navigation"` for the main navbar
- `role="group"` for Start and End sections when `aria-label` is provided
- `role="group"` for Start, Center, and End sections when `aria-label` is provided
- Customizable `aria-label` and `aria-labelledby` support

- Semantic HTML
Expand Down
20 changes: 20 additions & 0 deletions apps/www/src/content/docs/components/navbar/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@ export interface NavbarRootProps {
*/
sticky?: boolean;

/**
* Show the bottom shadow.
* @default true
*/
shadow?: boolean;

/**
* Hide the navbar when the user scrolls down, show it when they scroll up.
* Uses the navbar's scroll parent (e.g. ScrollArea) or window.
* @default false
*/
hideOnScroll?: boolean;

/**
* Accessible label for the navigation.
* Use this to provide a description of the navbar's purpose.
Expand All @@ -27,6 +40,13 @@ export interface NavbarStartProps {
'aria-label'?: string;
}

export interface NavbarCenterProps {
/**
* Accessible label for the center section. When provided, the section will have `role="group"`.
*/
'aria-label'?: string;
}

export interface NavbarEndProps {
/**
* Accessible label for the end section. Use this to describe the purpose
Expand Down
Loading