# Sidebar layout

Header + left rail + main - the curriculum chrome shape.

Preview: https://design.freecodecamp.org/playground#sidebar-layout

## Add to your project

Use React and TypeScript. Required packages: `react@>=18 <20`. No freeCodeCamp package is needed.

Copy the files below to the indicated paths, relative to your project root. If you change the layout, update relative imports too.

Import the CSS once from your application entry. For an entry in src/:

```ts
import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/sidebar-layout/sidebar-layout.css';
import './ui/sidebar/sidebar.css';
import './ui/navbar/navbar.css';
```

The theme is shared: reuse it if already installed. Fonts use /fonts/ URLs on your host. Download the font files listed in https://design.freecodecamp.org/registry/starter.md or change those URLs in your copied tokens.css.

## Example

```tsx
import { SidebarLayout } from './ui/sidebar-layout/SidebarLayout';
import { Sidebar } from './ui/sidebar/Sidebar';
import { Navbar } from './ui/navbar/Navbar';

export function Example() {
  return (
    <SidebarLayout
      header={<Navbar start='freeCodeCamp' />}
      sidebar={
        <Sidebar aria-label='Curriculum'>
          <a href='https://www.freecodecamp.org/learn/'>Learn</a>
        </Sidebar>
      }
    >
      <h1>Curriculum</h1>
    </SidebarLayout>
  );
}
```

## Interaction guidance

Review https://www.w3.org/WAI/ARIA/apg/patterns/landmarks/ and test keyboard operation in your project.

## Component source

### src/ui/sidebar-layout/SidebarLayout.tsx

Source: https://design.freecodecamp.org/registry/sidebar-layout/SidebarLayout.tsx

```tsx
import React, { forwardRef } from 'react';

export interface SidebarLayoutProps extends React.HTMLAttributes<HTMLDivElement> {
  header?: React.ReactNode;
  sidebar?: React.ReactNode;
}

export const SidebarLayout = forwardRef<HTMLDivElement, SidebarLayoutProps>(
  ({ header, sidebar, className = '', children, ...rest }, ref) => {
    const classes = ['sidebar-layout', className].filter(Boolean).join(' ');
    return (
      <div ref={ref} className={classes} {...rest}>
        {header !== undefined && (
          <div className='sidebar-layout__header'>{header}</div>
        )}
        <div className='sidebar-layout__body'>
          {sidebar !== undefined && (
            <div className='sidebar-layout__aside'>{sidebar}</div>
          )}
          <main className='sidebar-layout__main'>{children}</main>
        </div>
      </div>
    );
  }
);
SidebarLayout.displayName = 'SidebarLayout';
```

### src/ui/sidebar-layout/sidebar-layout.css

Source: https://design.freecodecamp.org/registry/sidebar-layout/sidebar-layout.css

```css
.sidebar-layout {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  background: var(--background-primary);
  color: var(--foreground-primary);
}
.sidebar-layout__header {
  flex: 0 0 auto;
}
.sidebar-layout__body {
  display: grid;
  grid-template-columns: 220px 1fr;
  flex: 1 1 auto;
  min-height: 0;
}
.sidebar-layout__aside {
  min-height: 0;
  overflow-y: auto;
}
.sidebar-layout__main {
  padding: 24px;
  min-width: 0;
  overflow-x: hidden;
}
@media (max-width: 768px) {
  .sidebar-layout__body {
    grid-template-columns: 1fr;
  }
  .sidebar-layout__aside {
    display: none;
  }
}
```

## Shared source: Theme

### src/ui/theme/tokens.css

Source: https://design.freecodecamp.org/registry/theme/tokens.css

```css
@font-face {
  font-family: 'Lato';
  src: url('/fonts/Lato-Light.woff') format('woff');
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Lato';
  src: url('/fonts/Lato-Regular.woff') format('woff');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Lato';
  src: url('/fonts/Lato-Italic.woff') format('woff');
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: 'Lato';
  src: url('/fonts/Lato-Bold.woff') format('woff');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Lato';
  src: url('/fonts/Lato-BoldItalic.woff') format('woff');
  font-weight: 700;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: 'Lato';
  src: url('/fonts/Lato-Black.woff') format('woff');
  font-weight: 900;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Hack-ZeroSlash';
  src: url('/fonts/Hack-ZeroSlash-Regular.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Hack-ZeroSlash';
  src: url('/fonts/Hack-ZeroSlash-Bold.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Hack-ZeroSlash';
  src: url('/fonts/Hack-ZeroSlash-Italic.woff2') format('woff2');
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: 'Hack-ZeroSlash';
  src: url('/fonts/Hack-ZeroSlash-BoldItalic.woff2') format('woff2');
  font-weight: 700;
  font-style: italic;
  font-display: swap;
}

:root {
  --gray-00: #ffffff;
  --gray-00-translucent: rgba(255, 255, 255, 0.85);
  --gray-05: #f5f6f7;
  --gray-10: #dfdfe2;
  --gray-15: #d0d0d5;
  --gray-45: #858591;
  --gray-75: #3b3b4f;
  --gray-80: #2a2a40;
  --gray-85: #1b1b32;
  --gray-90: #0a0a23;
  --gray-90-translucent: rgba(10, 10, 35, 0.85);

  --purple-light: #dbb8ff;
  --purple-mid: #9400d3;
  --purple-dark: #5a01a7;
  --yellow-light: #ffc300;
  --yellow-gold: #ffbf00;
  --yellow-style: #f1be32;
  --yellow-dark: #4d3800;
  --blue-light: #99c9ff;
  --blue-light-translucent: rgba(153, 201, 255, 0.3);
  --blue-mid: #198eee;
  --blue-dark: #002ead;
  --blue-dark-translucent: rgba(0, 46, 173, 0.3);
  --green-light: #acd157;
  --green-dark: #00471b;
  --red-light: #ffadad;
  --red-dark: #850000;
  --love-light: #f8577c;
  --love-dark: #f82153;
  --orange: #eda971;

  --editor-background-light: #fffffe;
  --editor-background-dark: #2a2b40;

  --syntax-keyword: #dbb8ff;
  --syntax-fn: #99c9ff;
  --syntax-string: #acd157;
  --syntax-class: #f1be32;
  --syntax-number: #f78c6c;
  --syntax-tag: #f07178;
  --syntax-operator: #89ddff;
  --syntax-invalid: #ff5370;
  --syntax-comment: #858591;
  --syntax-plain: #eeffff;

  --font-sans:
    'Lato', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-mono: 'Hack-ZeroSlash', 'Fira Mono', Menlo, Consolas, monospace;

  --fs-base: 18px;
  --fs-sm: 16px;
  --fs-md: 18px;
  --fs-lg: 24px;
  --fs-xl: 32px;
  --fs-2xl: 42px;
  --fs-3xl: 56px;
  --fs-display: clamp(2.5rem, 5vw, 3.75rem);

  --lh-tight: 1.2;
  --lh-snug: 1.33;
  --lh-base: 1.42857143;
  --lh-loose: 1.6;

  --fw-light: 300;
  --fw-regular: 400;
  --fw-bold: 700;
  --fw-black: 900;

  --space-0: 0;
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;
  --space-8: 64px;

  --border-width-hair: 1px;
  --border-width-default: 2px;
  --border-width-thick: 3px;
  --radius-none: 0;
  --radius-sm: 2px;

  --focus-outline-color: var(--blue-mid);
  --focus-outline-width: 3px;

  --z-breadcrumbs: 100;
  --z-flash: 150;
  --z-site-header: 200;
  --z-modal: 1050;

  --ease-snap: cubic-bezier(0.2, 0.8, 0.2, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --dur-fast: 120ms;
  --dur-base: 180ms;
  --dur-slow: 260ms;

  --header-height: 48px;
  --breadcrumbs-height: 32px;
  --sidebar-width: 288px;
  --content-max: 1040px;

  color-scheme: dark;
}

.dark-palette,
:root {
  color-scheme: dark;
  --foreground-primary: var(--gray-00);
  --foreground-secondary: var(--gray-05);
  --foreground-tertiary: var(--gray-10);
  --foreground-quaternary: var(--gray-15);
  --foreground-muted: #b0b0bd;

  --background-primary: var(--gray-90);
  --background-primary-translucent: var(--gray-90-translucent);
  --background-secondary: var(--gray-85);
  --background-tertiary: #33334f;
  --background-quaternary: #4b4b66;

  --highlight-color: var(--blue-light);
  --highlight-background: var(--blue-dark);
  --selection-color: var(--blue-light-translucent);

  --success-color: var(--green-light);
  --success-background: var(--green-dark);
  --danger-color: var(--red-light);
  --danger-background: var(--red-dark);
  --warning-color: var(--yellow-light);
  --warning-background: var(--yellow-dark);
  --purple-color: var(--purple-light);
  --purple-background: var(--purple-dark);
  --love-color: var(--love-light);

  --editor-background: var(--editor-background-dark);

  --cta-background: var(--yellow-gold);
  --cta-foreground: var(--gray-90);

  --surface-elevation-1: rgba(255, 255, 255, 0.045);
  --surface-elevation-2: rgba(255, 255, 255, 0.09);
  --border-soft: var(--background-tertiary);
  --border-strong: var(--background-quaternary);
}

.light-palette {
  --foreground-primary: var(--gray-90);
  --foreground-secondary: var(--gray-85);
  --foreground-tertiary: var(--gray-80);
  --foreground-quaternary: var(--gray-75);
  --foreground-muted: #5a5a68;

  --background-primary: var(--gray-00);
  --background-primary-translucent: var(--gray-00-translucent);
  --background-secondary: var(--gray-05);
  --background-tertiary: #c5c5cc;
  --background-quaternary: #a8a8b4;

  --highlight-color: var(--blue-dark);
  --highlight-background: var(--blue-light);
  --selection-color: var(--blue-dark-translucent);

  --success-color: var(--green-dark);
  --success-background: var(--green-light);
  --danger-color: var(--red-dark);
  --danger-background: var(--red-light);
  --warning-color: var(--yellow-dark);
  --warning-background: var(--yellow-light);
  --purple-color: var(--purple-dark);
  --purple-background: var(--purple-light);
  --love-color: var(--love-dark);

  --editor-background: var(--editor-background-dark);

  --cta-background: var(--yellow-gold);
  --cta-foreground: var(--gray-90);

  --surface-elevation-1: rgba(10, 10, 35, 0.05);
  --surface-elevation-2: rgba(10, 10, 35, 0.09);
  --border-soft: var(--background-tertiary);
  --border-strong: var(--background-quaternary);

  color-scheme: light;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  font-size: var(--fs-md);
  font-family: var(--font-sans);
  line-height: var(--lh-base);
  color: var(--foreground-primary);
  background: var(--background-primary);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-height) + 24px);
}

body {
  margin: 0;
  font-family: var(--font-sans);
  color: var(--foreground-primary);
  background: var(--background-primary);
}

::selection {
  background: var(--selection-color);
}

h1,
h2,
h3,
h4,
h5,
h6 {
  font-family: var(--font-sans);
  font-weight: var(--fw-bold);
  color: var(--foreground-primary);
  line-height: var(--lh-snug);
  margin: 0 0 12px 0;
}
h1 {
  font-size: var(--fs-3xl);
  line-height: var(--lh-tight);
  letter-spacing: -0.01em;
}
h2 {
  font-size: var(--fs-2xl);
  letter-spacing: -0.005em;
}
h3 {
  font-size: var(--fs-xl);
}
h4 {
  font-size: var(--fs-lg);
}
h5 {
  font-size: var(--fs-md);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
h6 {
  font-size: var(--fs-sm);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--foreground-muted);
  font-family: var(--font-mono);
}

p {
  margin: 0 0 12px 0;
}

a {
  color: var(--highlight-color);
  text-decoration: underline;
  text-underline-position: under;
  text-underline-offset: 0.1em;
}
a:hover {
  color: var(--foreground-primary);
}

code,
pre,
kbd,
samp {
  font-family: var(--font-mono);
  font-size: 16px;
}
code {
  background: var(--background-tertiary);
  color: var(--foreground-tertiary);
}
:not(pre) > code {
  border: 1px solid var(--background-quaternary);
  padding: 1px 4px;
  overflow-wrap: anywhere;
  word-break: break-word;
}
pre {
  background: var(--editor-background);
  color: var(--foreground-tertiary);
  padding: 14px 16px;
  font-size: 14px;
  line-height: var(--lh-base);
  max-width: 100%;
  overflow-x: auto;
  margin: 0;
}
pre code {
  display: block;
  width: max-content;
  min-width: 100%;
  background: transparent;
  border: 0;
  padding: 0;
}

:focus-visible {
  outline: var(--focus-outline-width) solid var(--focus-outline-color);
  outline-offset: 0;
}

hr {
  border: 0;
  border-top: 1px solid var(--background-quaternary);
  margin: 24px 0;
}

::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}
::-webkit-scrollbar-track {
  background: var(--background-primary);
}
::-webkit-scrollbar-thumb {
  background: var(--background-quaternary);
  border: 2px solid var(--background-primary);
}
::-webkit-scrollbar-thumb:hover {
  background: var(--foreground-muted);
}
```

### src/ui/theme/base.css

Source: https://design.freecodecamp.org/registry/theme/base.css

```css
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
```

## Shared source: sidebar

### src/ui/sidebar/Sidebar.tsx

Source: https://design.freecodecamp.org/registry/sidebar/Sidebar.tsx

```tsx
import React, { forwardRef } from 'react';

export interface SidebarProps extends React.HTMLAttributes<HTMLElement> {}

export const Sidebar = forwardRef<HTMLElement, SidebarProps>(
  ({ className = '', children, ...rest }, ref) => {
    const classes = ['sidebar', className].filter(Boolean).join(' ');
    return (
      <aside ref={ref} role='navigation' className={classes} {...rest}>
        {children}
      </aside>
    );
  }
);
Sidebar.displayName = 'Sidebar';

export interface SidebarSectionProps extends React.HTMLAttributes<HTMLElement> {
  label?: React.ReactNode;
  /**
   * When true, renders the section as a `<details><summary>` pair so users can
   * fold/unfold. Default: false (emits the original `<section>`).
   */
  collapsible?: boolean;
  defaultOpen?: boolean;
}

export const SidebarSection = forwardRef<HTMLElement, SidebarSectionProps>(
  (
    {
      label,
      className = '',
      children,
      collapsible = false,
      defaultOpen = true,
      ...rest
    },
    ref
  ) => {
    if (collapsible) {
      const classes = [
        'sidebar__section',
        'sidebar__section--collapsible',
        className
      ]
        .filter(Boolean)
        .join(' ');
      return (
        <details
          ref={ref as unknown as React.Ref<HTMLDetailsElement>}
          className={classes}
          open={defaultOpen}
          {...(rest as React.HTMLAttributes<HTMLDetailsElement>)}
        >
          <summary className='sidebar__section__summary'>
            {label !== undefined && (
              <span className='sidebar__eyebrow'>{label}</span>
            )}
            <span className='sidebar__section__caret' aria-hidden='true'>
              ▸
            </span>
          </summary>
          {children}
        </details>
      );
    }
    const classes = ['sidebar__section', className].filter(Boolean).join(' ');
    return (
      <section ref={ref} className={classes} {...rest}>
        {label !== undefined && <div className='sidebar__eyebrow'>{label}</div>}
        {children}
      </section>
    );
  }
);
SidebarSection.displayName = 'SidebarSection';

type AnchorProps = React.AnchorHTMLAttributes<HTMLAnchorElement>;
type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement>;

export interface SidebarItemCommonProps {
  active?: boolean;
  icon?: React.ReactNode;
}

export type SidebarItemProps = SidebarItemCommonProps &
  Omit<AnchorProps & ButtonProps, 'type'>;

export const SidebarItem = forwardRef<
  HTMLAnchorElement | HTMLButtonElement,
  SidebarItemProps
>((props, ref) => {
  const { active, icon, className = '', children, ...rest } = props;
  const classes = ['sidebar__item', className].filter(Boolean).join(' ');
  const extras = {
    'aria-current': active ? 'page' : undefined,
    'data-active': active ? 'true' : undefined
  } as const;
  const inner = (
    <>
      {icon !== undefined && <span className='sidebar__icon'>{icon}</span>}
      <span className='sidebar__label'>{children}</span>
    </>
  );
  const href = (rest as AnchorProps).href;
  if (href !== undefined) {
    return (
      <a
        ref={ref as React.Ref<HTMLAnchorElement>}
        className={classes}
        {...extras}
        {...(rest as AnchorProps)}
      >
        {inner}
      </a>
    );
  }
  return (
    <button
      ref={ref as React.Ref<HTMLButtonElement>}
      type='button'
      className={classes}
      {...extras}
      {...(rest as ButtonProps)}
    >
      {inner}
    </button>
  );
});
SidebarItem.displayName = 'SidebarItem';

/**
 * Normalise a path by stripping trailing slashes. Root (`/`) is preserved.
 */
function normalisePath(value: string): string {
  if (value === '/') return '/';
  return value.replace(/\/+$/, '') || '/';
}

export interface IsActiveHrefOptions {
  /** When true (default), require exact match. When false, descendant hrefs match. */
  exact?: boolean;
}

export function isActiveHref(
  path: string,
  href: string,
  options?: IsActiveHrefOptions
): boolean {
  const exact = options?.exact ?? true;
  const p = normalisePath(path);
  const h = normalisePath(href);
  if (p === h) return true;
  if (exact) return false;
  if (h === '/') return true;
  return p.startsWith(h + '/');
}

export function isActiveHrefWithHash(
  currentPath: string,
  currentHash: string,
  href: string
): boolean {
  const hashIdx = href.indexOf('#');
  if (hashIdx === -1) return isActiveHref(currentPath, href);

  const targetPath = hashIdx === 0 ? currentPath : href.slice(0, hashIdx);
  const targetHash = '#' + href.slice(hashIdx + 1);

  // A bare `#frag` href is treated as same-route fragment.
  const path = normalisePath(targetPath);
  const cur = normalisePath(currentPath);
  if (path !== cur) return false;
  return currentHash === targetHash;
}
```

### src/ui/sidebar/sidebar.css

Source: https://design.freecodecamp.org/registry/sidebar/sidebar.css

```css
.sidebar {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 16px 8px;
  background: var(--background-primary);
  border-right: var(--border-width-thin) solid var(--foreground-secondary);
  font-family: var(--font-sans);
  font-size: var(--fs-md);
  color: var(--foreground-primary);
  min-width: 220px;
  position: sticky;
  top: var(--sidebar-top, 0);
  align-self: start;
  max-height: calc(100vh - var(--sidebar-top, 0px));
  overflow-y: auto;
}
.sidebar__intro {
  padding: 0 12px 12px;
  border-bottom: var(--border-width-thin) solid var(--background-tertiary);
  margin-bottom: 12px;
}
.sidebar__intro-kicker {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--foreground-secondary);
  margin: 0 0 4px;
}
.sidebar__intro-title {
  font-family: var(--font-sans);
  font-size: var(--fs-md);
  font-weight: var(--fw-bold);
  color: var(--foreground-primary);
  margin: 0;
}
.sidebar__hint {
  padding: 12px 16px;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--foreground-secondary);
  line-height: 1.5;
}
.sidebar__section {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px 0;
}
.sidebar__section + .sidebar__section {
  border-top: var(--border-width-thin) dashed var(--background-tertiary);
}
.sidebar__eyebrow {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--foreground-secondary);
  padding: 4px 10px;
}
.sidebar__item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  color: var(--foreground-secondary);
  background: transparent;
  border: none;
  font: inherit;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  transition:
    background-color 120ms,
    color 120ms;
}
.sidebar__item:hover {
  background: var(--background-secondary);
  color: var(--foreground-primary);
}
.sidebar__item {
  border-left: 3px solid transparent;
}
.sidebar__item[data-active='true'] {
  color: var(--foreground-primary);
  background: var(--background-secondary);
  border-left-color: var(--cta-background);
  font-weight: var(--fw-bold);
}
.sidebar__item:focus-visible {
  outline: var(--border-width-thin) solid var(--foreground-primary);
  outline-offset: -2px;
}
.sidebar__icon {
  display: inline-flex;
  align-items: center;
  width: 1em;
  height: 1em;
}
.sidebar__label {
  flex: 1 1 auto;
}

.sidebar__section--collapsible {
  padding: 0;
}
.sidebar__section--collapsible > .sidebar__section__summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 4px 10px;
  cursor: pointer;
  list-style: none;
  user-select: none;
}
.sidebar__section--collapsible
  > .sidebar__section__summary::-webkit-details-marker {
  display: none;
}
.sidebar__section--collapsible
  > .sidebar__section__summary
  > .sidebar__eyebrow {
  padding: 0;
}
.sidebar__section__caret {
  display: inline-block;
  font-size: var(--fs-xs);
  color: var(--foreground-secondary);
  transition: transform 160ms ease;
}
.sidebar__section--collapsible[open]
  > .sidebar__section__summary
  > .sidebar__section__caret {
  transform: rotate(90deg);
}
```

## Shared source: navbar

### src/ui/navbar/Navbar.tsx

Source: https://design.freecodecamp.org/registry/navbar/Navbar.tsx

```tsx
import React, { forwardRef } from 'react';

export interface NavbarProps extends React.HTMLAttributes<HTMLElement> {
  start?: React.ReactNode;
  center?: React.ReactNode;
  end?: React.ReactNode;
}

export const Navbar = forwardRef<HTMLElement, NavbarProps>(
  ({ start, center, end, className = '', children, ...rest }, ref) => {
    const classes = ['navbar', className].filter(Boolean).join(' ');
    return (
      <header ref={ref} role='banner' className={classes} {...rest}>
        {start !== undefined && <div className='navbar__start'>{start}</div>}
        {center !== undefined && <div className='navbar__center'>{center}</div>}
        {end !== undefined && <div className='navbar__end'>{end}</div>}
        {children}
      </header>
    );
  }
);
Navbar.displayName = 'Navbar';
```

### src/ui/navbar/navbar.css

Source: https://design.freecodecamp.org/registry/navbar/navbar.css

```css
.navbar {
  display: flex;
  align-items: center;
  gap: 16px;
  min-height: 56px;
  padding: 8px 16px;
  background: var(--background-primary);
  border-bottom: var(--border-width-thin) solid var(--foreground-secondary);
  color: var(--foreground-primary);
  font-family: var(--font-sans);
  font-size: var(--fs-md);
}
.navbar__start {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 0 0 auto;
}
.navbar__center {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  flex: 1 1 auto;
}
.navbar__end {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 0 0 auto;
  margin-left: auto;
}
@media (max-width: 768px) {
  .navbar {
    flex-wrap: wrap;
  }
  .navbar__center {
    order: 3;
    flex-basis: 100%;
    justify-content: flex-start;
  }
  .navbar__end {
    margin-left: auto;
  }
}
```

## Adapting this component

Keep the component's semantics and keyboard behavior. Use the CSS variables to change its appearance. Check the result in your project; copied source does not receive automatic updates.

Source revision: 37aae52 (2026-09-08). Component source: BSD-3-Clause. Preserve the license notice: https://design.freecodecamp.org/license.txt.

Design rules: https://design.freecodecamp.org/handbook.md

