Ir al contenido

Table of contents

Navegación compacta por encabezados con ampliación al pasar el puntero y seguimiento del desplazamiento.

Navegadores
Chrome 123+Firefox 121+Safari 17.5+
Nivel
2
Tamaño comprimido
4.14 kB
Presupuesto de diseño forzado
0 ms
Presupuesto de axe
0
Compatible con SSR
Sí
Contrato publicado
Entorno de pruebas

Zona de pruebas

Overview

A table of contents gives readers a quick map of a long page. Follow a heading to jump directly to its section, or keep reading and let the current position update as you scroll.

Installation

Import the client entry to enable scroll tracking and pointer interaction. The native button and heading links are already usable before the page finishes loading.

Configuration

Match every line and link to a unique heading ID. Choose a scroll container and a content boundary when the article lives inside another page.

Examples

Hover a line to see its heading in the contents list. Click the line to jump there. On a touch screen, open the list and select the heading you want to read.

Accessibility

The contents are ordinary navigation links. Use the keyboard to open the list, move through its headings, and dismiss it with Escape. Scrolling never moves keyboard focus.

Styling

Adjust line length, spacing and the panel width with CSS variables. The current section uses bolder text; the heading under the pointer gets a separate hover background.

Conclusion

The same component works beside a document or inside a reader. Its compact rail stays available while the article scrolls, without taking a separate content column.

import { TableOfContents } from "@gunkin/uiify/components/table-of-contents/client";

const sections = ["Overview", "Installation", "Configuration", "Examples", "Accessibility", "Styling", "Conclusion"];
export default function Example() {
  return (
    <div className="toc-reader" id="toc-reader" tabIndex={0} role="region" aria-label="Example article">
      <TableOfContents.Root
        className=""
        aria-label="Contents"
        scrollTarget="toc-reader"
        contentTarget="toc-article"
        offset={24}
      >
        <TableOfContents.Trigger target="toc-panel" aria-label="Contents">
          <TableOfContents.Rail>
            {sections.map((label, index) => (
              <TableOfContents.Indicator key={label} value={`toc-section-${index}`} />
            ))}
          </TableOfContents.Rail>
        </TableOfContents.Trigger>
        <TableOfContents.Content id="toc-panel" side="right" align="center">
          <TableOfContents.List>
            {sections.map((label, index) => (
              <TableOfContents.Link key={label} href={`#toc-section-${index}`}>
                {label}
              </TableOfContents.Link>
            ))}
          </TableOfContents.List>
        </TableOfContents.Content>
      </TableOfContents.Root>
      <article id="toc-article">
        {sections.map((label, index) => (
          <section id={`toc-section-${index}`} key={label}>
            <h2>{label}</h2>
            <p>Read this section, or choose another heading from the contents.</p>
          </section>
        ))}
      </article>
    </div>
  );
}
@layer components {
  [data-uiify-table-of-contents] {
    --uiify-toc-line-size: 12px;
    --uiify-toc-line-max-size: 48px;
    --uiify-toc-line-thickness: 2px;
    --uiify-toc-row-size: 20px;
    --uiify-toc-magnify-radius: 2.5;
    --uiify-toc-indent: 1rem;
    --uiify-toc-panel-size: 18rem;
    --uiify-toc-duration: 140ms;

    color: var(--foreground);
  }
  [data-uiify-table-of-contents] [data-part="trigger"] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-inline-size: 44px;
    min-block-size: 44px;
    padding: 0.75rem;
    border: 0;
    border-radius: var(--radius-2);
    background: transparent;
    color: inherit;
    font: inherit;
    cursor: pointer;
  }
  [data-uiify-table-of-contents] [data-part="rail"] {
    display: grid;
    grid-auto-rows: var(--uiify-toc-row-size);
    align-items: center;
    inline-size: var(--uiify-toc-line-max-size);
    max-block-size: calc(100dvb - 8rem);
    overflow: auto;
  }
  [data-uiify-table-of-contents] [data-part="indicator"] {
    display: block;

    --uiify-toc-distance: max(
      calc(var(--uiify-toc-index, 0) - var(--uiify-toc-pointer, -1000)),
      calc(var(--uiify-toc-pointer, -1000) - var(--uiify-toc-index, 0))
    );
    --uiify-toc-proximity: clamp(
      0,
      calc(1 - var(--uiify-toc-distance) / max(0.001, var(--uiify-toc-magnify-radius))),
      1
    );

    inline-size: calc(
      var(--uiify-toc-line-size) + (var(--uiify-toc-line-max-size) - var(--uiify-toc-line-size)) *
        var(--uiify-toc-proximity)
    );
    transition:
      inline-size var(--uiify-toc-duration) ease,
      opacity var(--uiify-toc-duration) ease;
    block-size: var(--uiify-toc-line-thickness);
    border-radius: var(--radius-full);
    background: var(--muted-foreground);
    opacity: 0.5;
  }
  [data-uiify-table-of-contents] [data-part="indicator"][data-selected] {
    background: var(--foreground);
    opacity: 1;
  }
  [data-uiify-table-of-contents] [data-part="indicator"][data-highlighted]:not([data-selected]) {
    background: var(--primary);
    opacity: 1;
  }
  [data-uiify-table-of-contents] [data-part="content"] {
    --uiify-anchor-offset: 0;

    inline-size: min(var(--uiify-toc-panel-size), calc(100dvi - 2rem));
    max-block-size: calc(100dvb - 2rem);
    overflow: auto;
    overscroll-behavior: contain;
    padding: 0.5rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-3);
    background: var(--popover);
    color: var(--popover-foreground);
  }
  [data-uiify-table-of-contents] [data-part="list"] {
    display: grid;
  }
  [data-uiify-table-of-contents] [data-part="link"] {
    display: block;
    padding-block: 0.625rem;
    padding-inline: calc(0.75rem + max(0, var(--uiify-toc-level, 2) - 2) * var(--uiify-toc-indent))
      0.75rem;
    border-radius: var(--radius-2);
    color: var(--muted-foreground);
    text-decoration: none;
    overflow-wrap: anywhere;
    font-size: 0.875rem;
    line-height: 1.4;
  }
  [data-uiify-table-of-contents] [data-part="link"][data-selected] {
    color: var(--foreground);
    font-weight: var(--weight-semibold);
  }
  [data-uiify-table-of-contents] [data-part="link"]:is(:hover, [data-highlighted]) {
    background: var(--accent);
    color: var(--accent-foreground);
  }
  [data-uiify-table-of-contents] :is([data-part="trigger"], [data-part="link"]):focus-visible {
    outline: 2px solid var(--ring);
    outline-offset: 2px;
  }

  @media (prefers-reduced-motion: reduce) {
    [data-uiify-table-of-contents] [data-part="indicator"] {
      transition: none;
    }
  }
}
.toc-reader { display: grid; grid-template-columns: 3rem minmax(0, 1fr); align-items: start; block-size: 28rem; overflow: auto; }
.toc-reader > nav { grid-column: 1; grid-row: 1; position: sticky; inset-block-start: 50%; translate: 0 -50%; --uiify-toc-line-size: 6px; --uiify-toc-line-max-size: 24px; }
.toc-reader > article { grid-column: 2; grid-row: 1; min-inline-size: 0; }
.toc-reader section { min-block-size: 18rem; padding: 1rem; scroll-margin-block-start: 24px; }
import { TableOfContents } from "@gunkin/uiify/components/table-of-contents/client";

const sections = ["Overview", "Installation", "Configuration", "Examples", "Accessibility", "Styling", "Conclusion"];
export default function Example() {
  return (
    <div
      className="grid grid-cols-[3rem_minmax(0,1fr)] [block-size:28rem] overflow-auto [&>article]:[min-inline-size:0] [&_section]:[min-block-size:18rem] [&_section]:p-4"
      id="toc-reader"
      tabIndex={0}
      role="region"
      aria-label="Example article"
    >
      <TableOfContents.Root
        className={
          "[--uiify-toc-line-size:12px] [--uiify-toc-line-max-size:48px] [--uiify-toc-line-thickness:2px] [--uiify-t" +
          "oc-row-size:20px] [--uiify-toc-magnify-radius:2.5] [--uiify-toc-indent:1rem] [--uiify-toc-panel-size:18re" +
          "m] [--uiify-toc-duration:140ms] text-foreground [&_[data-part=trigger]]:inline-flex [&_[data-part=trigger" +
          "]]:items-center [&_[data-part=trigger]]:justify-center [&_[data-part=trigger]]:[min-inline-size:44px] [&_" +
          "[data-part=trigger]]:[min-block-size:44px] [&_[data-part=trigger]]:[padding:0.75rem] [&_[data-part=trigge" +
          "r]]:border-0 [&_[data-part=trigger]]:border-current [&_[data-part=trigger]]:border-none [&_[data-part=tri" +
          "gger]]:[border-radius:var(--radius-2)] [&_[data-part=trigger]]:bg-transparent [&_[data-part=trigger]]:tex" +
          "t-inherit [&_[data-part=trigger]]:[font-style:inherit] [&_[data-part=trigger]]:[font-variant:inherit] [&_" +
          "[data-part=trigger]]:[font-weight:inherit] [&_[data-part=trigger]]:[font-stretch:inherit] [&_[data-part=t" +
          "rigger]]:text-[length:inherit] [&_[data-part=trigger]]:leading-[inherit] [&_[data-part=trigger]]:font-[fa" +
          "mily-name:inherit] [&_[data-part=trigger]]:cursor-pointer [&_[data-part=rail]]:grid [&_[data-part=rail]]:" +
          "[grid-auto-rows:var(--uiify-toc-row-size)] [&_[data-part=rail]]:items-center [&_[data-part=rail]]:[inline" +
          "-size:var(--uiify-toc-line-max-size)] [&_[data-part=rail]]:[max-block-size:calc(100dvb_-_8rem)] [&_[data-" +
          "part=rail]]:overflow-auto [&_[data-part=indicator]]:block [&_[data-part=indicator]]:[--uiify-toc-distance" +
          ":max(_calc(var(--uiify-toc-index,_0)_-_var(--uiify-toc-pointer,_-1000)),_calc(var(--uiify-toc-pointer,_-1" +
          "000)_-_var(--uiify-toc-index,_0))_)] [&_[data-part=indicator]]:[--uiify-toc-proximity:clamp(_0,_calc(1_-_" +
          "var(--uiify-toc-distance)_/_max(0.001,_var(--uiify-toc-magnify-radius))),_1_)] [&_[data-part=indicator]]:" +
          "[inline-size:calc(_var(--uiify-toc-line-size)_+_(var(--uiify-toc-line-max-size)_-_var(--uiify-toc-line-si" +
          "ze))_*_var(--uiify-toc-proximity)_)] [&_[data-part=indicator]]:[transition:inline-size_var(--uiify-toc-du" +
          "ration)_ease,_opacity_var(--uiify-toc-duration)_ease] [&_[data-part=indicator]]:[block-size:var(--uiify-t" +
          "oc-line-thickness)] [&_[data-part=indicator]]:rounded-[var(--radius-full)] [&_[data-part=indicator]]:bg-m" +
          "uted-foreground [&_[data-part=indicator]]:opacity-50 [&_[data-part=indicator][data-selected]]:bg-foregrou" +
          "nd [&_[data-part=indicator][data-selected]]:opacity-100 [&_[data-part=indicator][data-highlighted]:not([d" +
          "ata-selected])]:bg-primary [&_[data-part=indicator][data-highlighted]:not([data-selected])]:opacity-100 [" +
          "&_[data-part=content]]:[--uiify-anchor-offset:0] [&_[data-part=content]]:[inline-size:min(var(--uiify-toc" +
          "-panel-size),_calc(100dvi_-_2rem))] [&_[data-part=content]]:[max-block-size:calc(100dvb_-_2rem)] [&_[data" +
          "-part=content]]:overflow-auto [&_[data-part=content]]:overscroll-contain [&_[data-part=content]]:[padding" +
          ":0.5rem] [&_[data-part=content]]:[border-width:1px] [&_[data-part=content]]:border-solid [&_[data-part=co" +
          "ntent]]:border-border [&_[data-part=content]]:[border-radius:var(--radius-3)] [&_[data-part=content]]:bg-" +
          "popover [&_[data-part=content]]:text-popover-foreground [&_[data-part=list]]:grid [&_[data-part=link]]:bl" +
          "ock [&_[data-part=link]]:[padding-block:0.625rem] [&_[data-part=link]]:[padding-inline:calc(0.75rem_+_max" +
          "(0,_var(--uiify-toc-level,_2)_-_2)_*_var(--uiify-toc-indent))_0.75rem] [&_[data-part=link]]:[border-radiu" +
          "s:var(--radius-2)] [&_[data-part=link]]:text-muted-foreground [&_[data-part=link]]:no-underline [&_[data-" +
          "part=link]]:[overflow-wrap:anywhere] [&_[data-part=link]]:[font-size:0.875rem] [&_[data-part=link]]:[line" +
          "-height:1.4] [&_[data-part=link][data-selected]]:text-foreground [&_[data-part=link][data-selected]]:font" +
          "-[number:var(--weight-semibold)] [&_[data-part=link]:is(:hover,[data-highlighted])]:bg-accent [&_[data-pa" +
          "rt=link]:is(:hover,[data-highlighted])]:text-accent-foreground [&_:is([data-part=trigger],[data-part=link" +
          "]):focus-visible]:[outline:2px_solid_var(--ring)] [&_:is([data-part=trigger],[data-part=link]):focus-visi" +
          "ble]:[outline-offset:2px] motion-reduce:[&_[data-part=indicator]]:[transition:none]"
        }
        aria-label="Contents"
        scrollTarget="toc-reader"
        contentTarget="toc-article"
        offset={24}
      >
        <TableOfContents.Trigger target="toc-panel" aria-label="Contents">
          <TableOfContents.Rail>
            {sections.map((label, index) => (
              <TableOfContents.Indicator key={label} value={`toc-section-${index}`} />
            ))}
          </TableOfContents.Rail>
        </TableOfContents.Trigger>
        <TableOfContents.Content id="toc-panel" side="right" align="center">
          <TableOfContents.List>
            {sections.map((label, index) => (
              <TableOfContents.Link key={label} href={`#toc-section-${index}`}>
                {label}
              </TableOfContents.Link>
            ))}
          </TableOfContents.List>
        </TableOfContents.Content>
      </TableOfContents.Root>
      <article id="toc-article">
        {sections.map((label, index) => (
          <section id={`toc-section-${index}`} key={label}>
            <h2>{label}</h2>
            <p>Read this section, or choose another heading from the contents.</p>
          </section>
        ))}
      </article>
    </div>
  );
}
/* app.css */

@layer reset, ui, app, tokens, base, demo, components, utilities, theme;

@import "@gunkin/uiify/styles/reset";

@import "@gunkin/uiify/components/behavior.css";

@import "@gunkin/uiify/styles/tokens";

@import "@gunkin/uiify/styles/tailwind";

@import "tailwindcss/utilities.css";

@theme { --spacing: 0.25rem; --font-weight-normal: 400; --font-weight-medium: 500; --font-weight-semibold: 600; --font-weight-bold: 700; }

@source inline('[--uiify-toc-line-size:12px] [--uiify-toc-line-max-size:48px] [--uiify-toc-line-thickness:2px] [--uiify-toc-row-size:20px] [--uiify-toc-magnify-radius:2.5] [--uiify-toc-indent:1rem] [--uiify-toc-panel-size:18rem] [--uiify-toc-duration:140ms] text-foreground [&_[data-part=trigger]]:inline-flex [&_[data-part=trigger]]:items-center [&_[data-part=trigger]]:justify-center [&_[data-part=trigger]]:[min-inline-size:44px] [&_[data-part=trigger]]:[min-block-size:44px] [&_[data-part=trigger]]:[padding:0.75rem] [&_[data-part=trigger]]:border-0 [&_[data-part=trigger]]:border-current [&_[data-part=trigger]]:border-none [&_[data-part=trigger]]:[border-radius:var(--radius-2)] [&_[data-part=trigger]]:bg-transparent [&_[data-part=trigger]]:text-inherit [&_[data-part=trigger]]:[font-style:inherit] [&_[data-part=trigger]]:[font-variant:inherit] [&_[data-part=trigger]]:[font-weight:inherit] [&_[data-part=trigger]]:[font-stretch:inherit] [&_[data-part=trigger]]:text-[length:inherit] [&_[data-part=trigger]]:leading-[inherit] [&_[data-part=trigger]]:font-[family-name:inherit] [&_[data-part=trigger]]:cursor-pointer [&_[data-part=rail]]:grid [&_[data-part=rail]]:[grid-auto-rows:var(--uiify-toc-row-size)] [&_[data-part=rail]]:items-center [&_[data-part=rail]]:[inline-size:var(--uiify-toc-line-max-size)] [&_[data-part=rail]]:[max-block-size:calc(100dvb_-_8rem)] [&_[data-part=rail]]:overflow-auto [&_[data-part=indicator]]:block [&_[data-part=indicator]]:[--uiify-toc-distance:max(_calc(var(--uiify-toc-index,_0)_-_var(--uiify-toc-pointer,_-1000)),_calc(var(--uiify-toc-pointer,_-1000)_-_var(--uiify-toc-index,_0))_)] [&_[data-part=indicator]]:[--uiify-toc-proximity:clamp(_0,_calc(1_-_var(--uiify-toc-distance)_/_max(0.001,_var(--uiify-toc-magnify-radius))),_1_)] [&_[data-part=indicator]]:[inline-size:calc(_var(--uiify-toc-line-size)_+_(var(--uiify-toc-line-max-size)_-_var(--uiify-toc-line-size))_*_var(--uiify-toc-proximity)_)] [&_[data-part=indicator]]:[transition:inline-size_var(--uiify-toc-duration)_ease,_opacity_var(--uiify-toc-duration)_ease] [&_[data-part=indicator]]:[block-size:var(--uiify-toc-line-thickness)] [&_[data-part=indicator]]:rounded-[var(--radius-full)] [&_[data-part=indicator]]:bg-muted-foreground [&_[data-part=indicator]]:opacity-50 [&_[data-part=indicator][data-selected]]:bg-foreground [&_[data-part=indicator][data-selected]]:opacity-100 [&_[data-part=indicator][data-highlighted]:not([data-selected])]:bg-primary [&_[data-part=indicator][data-highlighted]:not([data-selected])]:opacity-100 [&_[data-part=content]]:[--uiify-anchor-offset:0] [&_[data-part=content]]:[inline-size:min(var(--uiify-toc-panel-size),_calc(100dvi_-_2rem))] [&_[data-part=content]]:[max-block-size:calc(100dvb_-_2rem)] [&_[data-part=content]]:overflow-auto [&_[data-part=content]]:overscroll-contain [&_[data-part=content]]:[padding:0.5rem] [&_[data-part=content]]:[border-width:1px] [&_[data-part=content]]:border-solid [&_[data-part=content]]:border-border [&_[data-part=content]]:[border-radius:var(--radius-3)] [&_[data-part=content]]:bg-popover [&_[data-part=content]]:text-popover-foreground [&_[data-part=list]]:grid [&_[data-part=link]]:block [&_[data-part=link]]:[padding-block:0.625rem] [&_[data-part=link]]:[padding-inline:calc(0.75rem_+_max(0,_var(--uiify-toc-level,_2)_-_2)_*_var(--uiify-toc-indent))_0.75rem] [&_[data-part=link]]:[border-radius:var(--radius-2)] [&_[data-part=link]]:text-muted-foreground [&_[data-part=link]]:no-underline [&_[data-part=link]]:[overflow-wrap:anywhere] [&_[data-part=link]]:[font-size:0.875rem] [&_[data-part=link]]:[line-height:1.4] [&_[data-part=link][data-selected]]:text-foreground [&_[data-part=link][data-selected]]:font-[number:var(--weight-semibold)] [&_[data-part=link]:is(:hover,[data-highlighted])]:bg-accent [&_[data-part=link]:is(:hover,[data-highlighted])]:text-accent-foreground [&_:is([data-part=trigger],[data-part=link]):focus-visible]:[outline:2px_solid_var(--ring)] [&_:is([data-part=trigger],[data-part=link]):focus-visible]:[outline-offset:2px] motion-reduce:[&_[data-part=indicator]]:[transition:none] grid grid-cols-[3rem_minmax(0,1fr)] [block-size:28rem] overflow-auto [&>article]:[min-inline-size:0] [&_section]:[min-block-size:18rem] [&_section]:p-4');

@layer base { :root { --uiify-control-text: var(--text-sm); } @media (pointer: coarse) { :root { --uiify-control-text: var(--text-base); } } }

/* Authored animation and registered properties used by the Tailwind skin.
 * Visual rules belong in utility recipes; only at-rule definitions live here. */
@layer components {
  @keyframes uiify-presence-in {
    from {
      opacity: 0;
      scale: var(--presence-scale);
    }
  }
}

@layer components {
  @keyframes uiify-presence-out {
    to {
      opacity: 0;
      scale: var(--presence-scale);
    }
  }
}

@property --uiify-spinner-tail {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

@property --uiify-spinner-length {
  syntax: "<angle>";
  inherits: false;
  initial-value: 36deg;
}

@layer components {
  @keyframes uiify-spinner-rotate {
    to {
      transform: rotate(360deg);
    }
  }
}

@layer components {
  @keyframes uiify-spinner-orbit {
    0% {
      --uiify-spinner-tail: 0deg;
      --uiify-spinner-length: 36deg;
    }

    50% {
      --uiify-spinner-tail: 90deg;
      --uiify-spinner-length: 324deg;
    }

    100% {
      --uiify-spinner-tail: 360deg;
      --uiify-spinner-length: 36deg;
    }
  }
}

@layer components {
  @keyframes uiify-skeleton-pulse {
    50% {
      opacity: 0.45;
    }
  }
}

@layer components {
  @keyframes uiify-skeleton-shimmer {
    to {
      background-position: -200% 0;
    }
  }
}

@layer components {
  @keyframes uiify-skeleton-reveal {
    from {
      opacity: 0;
    }
    to {
      opacity: 1;
    }
  }
}

@property --candle-body-height {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 0%;
}

@property --candle-body-top {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 50%;
}

@property --candle-slot {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 100%;
}

@property --candle-wick-height {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 0%;
}

@property --candle-wick-top {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 50%;
}

@property --candle-x {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 0%;
}

@property --candle-scale {
  syntax: "<number>";
  inherits: true;
  initial-value: 1;
}

@property --line-y {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 50%;
}

@property --line-prev-y {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 50%;
}

@property --line-count {
  syntax: "<integer>";
  inherits: false;
  initial-value: 1;
}

@property --line-scale {
  syntax: "<number>";
  inherits: true;
  initial-value: 1;
}

@layer components {
  @keyframes uiify-otp-input-caret-blink {
    50% {
      opacity: 0;
    }
  }
}

API

TableOfContents.Root (/client)

PropTipoPredeterminadoDescripción
scrollSpybooleantrueSigue los destinos de la misma página en el orden del documento después de la hidratación.
scrollTargetstring—ID del elemento desplazable. Omítelo para seguir la ventana a la que pertenece; si no se encuentra el ID, no se usa una alternativa.
contentTargetstring—ID del elemento de contenido; limita la búsqueda de destinos y determina el final del artículo.
offsetnumber0Píxeles desde el inicio del área visible de desplazamiento que se usan para seleccionar el encabezado actual.
valuestring | null—ID del destino actual controlado. null borra la selección.
defaultValuestring | nullnullID del destino actual inicial.
onValueChange(value: string | null) => void—Informa los cambios del seguimiento de desplazamiento sin mover el foco.
openboolean—Estado controlado de la ventana emergente.
defaultOpenbooleanfalseEstado de apertura inicial aplicado después de la hidratación.
onOpenChange(open: boolean) => void—Informa solicitudes de apertura al pasar el puntero, activación y cierre nativo.
openOnHoverbooleantrueAbre al pasar el mouse; la entrada táctil usa el botón nativo.
openDelaynumber100Espera de apertura al pasar el mouse, en milisegundos.
closeDelaynumber200Tiempo de gracia para moverse entre el activador y la lista.
renderReactElement | ((props, state) => ReactElement)—Reemplaza el elemento renderizado y conserva las conexiones de comportamiento y accesibilidad.

TableOfContents.Trigger

PropTipoPredeterminadoDescripción
target*string—ID único de Content, conectado mediante popovertarget nativo.
aria-labelstring—Nombre accesible obligatorio cuando el activador solo contiene la guía decorativa.

TableOfContents.Indicator

PropTipoPredeterminadoDescripción
value*string—ID de destino que coincide con un fragmento de Link; un indicador por sección.
level1 | 2 | 3 | 4 | 5 | 62Nivel del encabezado disponible mediante --uiify-toc-level.
selectedbooleanfalseSelección de la entrada de servidor; el cliente deriva la selección de Root.value.
renderReactElement | ((props, state) => ReactElement)—Reemplaza el elemento renderizado y conserva las conexiones de comportamiento y accesibilidad.

TableOfContents.Content

PropTipoPredeterminadoDescripción
id*string—ID único de ventana emergente que coincide con Trigger.target.
side"top" | "right" | "bottom" | "left""left"Lado del ancla CSS nativa; la disposición se invierte cuando hace falta.
align"start" | "center" | "end""start"Alineación junto al activador.
renderReactElement | ((props, state) => ReactElement)—Reemplaza el elemento renderizado y conserva las conexiones de comportamiento y accesibilidad.

TableOfContents.Link

TableOfContents.Rail / List

PropTipoPredeterminadoDescripción
renderReactElement | ((props, state) => ReactElement)—Reemplaza el elemento renderizado y conserva las conexiones de comportamiento y accesibilidad.

Teclado

TeclaAcción
Enter / SpaceAlternar la ventana emergente nativa del índice desde su activador
Tab / Shift+TabRecorrer los enlaces normales de los encabezados
EscapeCerrar la ventana emergente y devolver el foco de la lista al activador

Accesibilidad

Etiqueta la región de navegación y su activador de solo ícono. La entrada de servidor permite activar y cerrar la ventana emergente nativa y seguir enlaces de fragmento antes de la hidratación; la entrada /client añade apertura al pasar el puntero, fijación con clic, ampliación y seguimiento del desplazamiento. Los enlaces actuales exponen aria-current=location y los indicadores decorativos se ocultan a la tecnología de asistencia. El seguimiento del desplazamiento nunca mueve el foco ni anuncia el desplazamiento. Con movimiento reducido, la apariencia opcional desactiva las transiciones de línea.

Puntos de estilo

data-uiify-table-of-contentsdata-partdata-selecteddata-highlighteddata-statedata-sidedata-aligndata-positioning--uiify-toc-line-size--uiify-toc-line-max-size--uiify-toc-line-thickness--uiify-toc-row-size--uiify-toc-magnify-radius--uiify-toc-indent--uiify-toc-panel-size--uiify-toc-duration--uiify-toc-level--uiify-toc-index--uiify-toc-pointer