El aspecto de un título de LaTeX se decide en un lugar sorprendentemente pequeño. Cuatro líneas que empiezan en la línea 302 de article.cls —una sola llamada que pasa seis argumentos a un comando llamado \@startsection— fijan la tipografía, la numeración y el espacio alrededor de cada \section. Lo que hace el paquete titlesec (de Javier Bezos) es volver a leer esos seis argumentos en la clase y servirlos como los legibles \titleformat y \titlespacing. Por eso cargarlo sin más no cambia ni un punto en la página. Esta página abre primero el \@startsection de la clase para ver cómo funciona de verdad un título, y luego cubre cómo escribir \titleformat y \titlespacing, qué significa realmente la estrella y por qué el paquete choca con KOMA-Script y memoir, todo medido.
\@startsection: los seis argumentos con que una clase define un título
Imprima la definición de \section y saldrá una única llamada con seis argumentos. Todos los títulos de las clases estándar salvo \part tienen esta forma, y los argumentos son, por orden, el nombre del título, su número de nivel, la sangría, el espacio superior, el espacio inferior y el formato. Las líneas 302 a 305 de article.cls son exactamente eso.
% article.cls, lines 302-305 (TeX Live 2024)
\newcommand\section{\@startsection {section}{1}{\z@}%
{-3.5ex \@plus -1ex \@minus -.2ex}%
{2.3ex \@plus.2ex}%
{\normalfont\Large\bfseries}}
% \paragraph, from the same file - note the NEGATIVE after-skip
\newcommand\paragraph{\@startsection{paragraph}{4}{\z@}%
{3.25ex \@plus1ex \@minus.2ex}%
{-1em}%
{\normalfont\normalsize\bfseries}}Lo interesante es que el signo del cuarto y del quinto argumento significa algo. Un cuarto argumento negativo (el espacio superior) quiere decir «no sangres el párrafo que sigue a este título»; un quinto negativo (el espacio inferior) quiere decir «no cortes la línea tras el título, que el texto siga», es decir, un título run-in. \section lleva -3.5ex arriba, así que el párrafo posterior a una sección no se sangra. \paragraph lleva -1em abajo, así que \paragraph es un título run-in. Conmutar un comportamiento con el signo de una longitud es diseño TeX de los años ochenta en estado puro. La prueba está en el código: la rutina con la que titlesec vuelve a leer una definición de clase elige el \titlespacing* con estrella cuando el cuarto es negativo y la forma runin cuando lo es el quinto, invirtiendo el signo en cada caso.
| Comando de sección | Nivel en article | Nivel en book / report |
|---|---|---|
\part | 0 | −1 |
\chapter | (no existe) | 0 |
\section | 1 | 1 |
\subsection | 2 | 2 |
\subsubsection | 3 | 3 |
\paragraph | 4 (run-in por defecto) | 4 |
\subparagraph | 5 (ídem) | 5 |
Hay, pues, seis niveles en article (de 0 a 5) y siete en book y report (de −1 a 5), siendo el extra \chapter. La profundidad de la numeración la guarda \c@secnumdepth: medida, es 3 en article y 2 en book y report. Por eso article numera hasta \subsubsection mientras que book se detiene en \subsection. Escriba \setcounter{secnumdepth}{4} y \paragraph también recibirá número. Cuán hondo llega el índice es una cuenta aparte, guardada en tocdepth.
Qué ocurre realmente al cargar titlesec
En la página no cambia nada. Lo que cambia es la definición de \section: la llamada a \@startsection queda reemplazada por la maquinaria propia de titlesec. Medido, antes de cargar \section es \@startsection {section}{1}{\z@}{-3.5ex ...}{2.3ex ...}{\normalfont \Large \bfseries} y después es el único símbolo \ttl@straightclass {section}. En el proceso, los seis argumentos pasan al almacén interno de titlesec, donde el espacio superior se convierte en la longitud concreta 15.0694pt plus 4.30554pt minus 0.86108pt y el inferior en 9.90276pt plus 0.86108pt. Son simplemente 3.5ex y 2.3ex evaluados, ya que 1ex = 4.30554pt en Computer Modern de 10pt (las partes plus y minus son el estiramiento y el encogimiento que LaTeX puede usar para nivelar el pie de una página). En resumen, una transformación equivalente: no se pierde ni una brizna de significado.
% Measured in a 10pt article. These two lines reproduce exactly what the
% class already does - they are what titlesec extracted on load.
\titleformat{\section}[hang]
{\normalfont\Large\bfseries}{\thesection}{1em}{}
\titlespacing*{\section}{0pt}
{15.0694pt plus 4.30554pt minus 0.86108pt} % = 3.5ex plus 1ex minus .2ex
{9.90276pt plus 0.86108pt} % = 2.3ex plus .2exEscribir \titleformat: siete argumentos y seis formas
\titleformat es el centro del rediseño. Toma muchos argumentos, pero se corresponden con los seis de \@startsection, así que no hay nada que temer. Si solo quiere otra tipografía, la forma con estrella \titleformat* es la vía más corta —dos argumentos, como \titleformat*{\section}{\large\bfseries\sffamily}— y la numeración y el espaciado quedan como los fijó la clase. Aún más corto: hay una configuración sencilla que solo usa opciones del paquete; \usepackage[sf,bf]{titlesec} pone todos los títulos en sans serif negrita. Las opciones forman tres familias —rm sf tt md bf up it sl sc; big medium small tiny; raggedleft center raggedright— más compact, que reduce el espacio superior e inferior. Para cambiar solo cómo se imprime el número, use \titlelabel, por ejemplo \titlelabel{\thetitle.\quad}, donde \thetitle representa el número de ese título.
\titleformat{⟨command⟩}[⟨shape⟩]{⟨format⟩}{⟨label⟩}{⟨sep⟩}{⟨before-code⟩}[⟨after-code⟩]⟨command⟩— el comando de sección que va a rediseñar:\part,\chapter,\section,\subsection,\subsubsection,\paragrapho\subparagraph.[⟨shape⟩](opcional) — la «forma» del título:hang,block,display,runin,leftmargin,rightmargin,frame,drop,wrap. Por defecto,hang.{⟨format⟩}— formato aplicado tanto a la etiqueta como al título. Aquí se fijan tipografía y tamaño; la alineación, con\filcenter,\filright,\filleft,\fillast, o\filinner/\filoutera doble cara.{⟨label⟩}— cómo se imprime el número; ponga\thesection, o\@seccntformat{section}para conservar el formato de la clase. Dejarlo vacío suprime el número también en el índice.{⟨sep⟩}— separación entre etiqueta y título. Debe ser una longitud y no puede quedar vacía (horizontal enhang/block, vertical endisplay, distancia título-marco enframe).{⟨before-code⟩}— código ejecutado justo antes del título. Aquí va la decoración:\titlerulepara una regla,\titlelinepara componer material arbitrario en una línea propia.[⟨after-code⟩](opcional) — código ejecutado justo después del título (modo vertical enhang/block/display, horizontal enrunin/leftmargin).
| shape | Efecto |
|---|---|
hang | Predeterminado: etiqueta colgante con el título alineado a su lado (el \section estándar) |
block | Compone etiqueta y título como un único bloque (párrafo); lo indicado para títulos centrados |
display | Pone la etiqueta en su propia línea sobre el título (el \chapter estándar) |
runin | Título run-in: sin salto, el texto sigue en la misma línea (el \paragraph estándar) |
leftmargin | Coloca el título dentro del margen izquierdo; rightmargin es la versión derecha |
frame | Como display, pero el título queda enmarcado |
drop | Hace que el texto rodee al título; wrap ajusta la caja del título a su contenido |
\titlespacing y el significado de la estrella: por qué casi siempre quiere \titlespacing*
La estrella significa suprimir la sangría del párrafo que sigue al título. La forma más fácil de recordarlo es verla como traducción directa de la convención de \@startsection de la sección anterior: un espacio superior negativo quiere decir «sin sangría». El \section estándar lleva -3.5ex arriba, así que para conservar el aspecto de la clase se usa la forma con estrella; de ahí el consejo de «casi siempre \titlespacing*». Sus argumentos son el margen izquierdo adicional, el espacio sobre el título, la separación entre título y texto y un margen derecho adicional opcional. En las formas leftmargin, el primer argumento es el ancho del título; en runin, la sangría inmediatamente anterior. En drop, wrap y runin no hay sangría alguna que suprimir, así que ahí la estrella no surte efecto.
\titlespacing*{⟨command⟩}{⟨left⟩}{⟨before-sep⟩}{⟨after-sep⟩}[⟨right⟩]
% All arguments must be LENGTHS. A value containing a command such as
% \stretch, or an empty {}, produces:
% ! Missing number, treated as zero.
% ! Illegal unit of measure (pt inserted).
%
% Shorthand: *4 means 4 units of \beforetitleunit (or \aftertitleunit)
\titlespacing*{\section}{0pt}{*4}{*2}Todos los argumentos deben ser longitudes (dimensiones). Pase un valor que contenga un comando, como \stretch{1}, y obtendrá ! Missing number, treated as zero. seguido de ! Illegal unit of measure (pt inserted). Un {} vacío produce el mismo par de errores. Si escribir los valores completos de skip resulta pesado, abrevie con * y un número, como *4; las unidades de esa notación viven en \beforetitleunit y \aftertitleunit.
Un comportamiento que merece una medición, porque mucha gente cae en él. \titlespacing no hace nada con \chapter ni \part a menos que defina también su formato con \titleformat. En la clase report, escribiendo solo \titlespacing*{\chapter}{0pt}{0pt}{10pt} y midiendo dónde arranca el texto tras un capítulo se obtienen 305,1077pt, ni un punto escalado de diferencia respecto a no escribirlo. Añada \titleformat{\chapter}[display]{...} en el mismo archivo y sube a 229,2701pt. No se emite aviso ni error, así que lo incómodo es no darse cuenta. \part no está implementado con \@startsection de entrada, de modo que ni siquiera la configuración sencilla lo toca. Para ambos niveles, empiece por \titleformat.
Construir un título con regla y un título run-in
\titleformat y \titlespacing* se escriben normalmente en pareja. El preámbulo siguiente convierte \section en un título con una regla fina encima y \subsection en un título run-in que acaba en punto y deja seguir el texto. Para la regla, lo habitual es poner \titlerule en el {⟨before-code⟩} de la forma [display]. La opción [explicit] impide que el texto del título aparezca por sí solo, de modo que hay que colocarlo explícitamente con #1: justo el mecanismo que hace falta cuando se quiere decoración a ambos lados del título.
\documentclass{article}
\usepackage[explicit]{titlesec}
% A rule above the title, sans-serif bold below it
\titleformat{\section}[display]
{\normalfont\Large\bfseries\sffamily} % applies to label and title
{\thesection} % the number
{0pt} % label-to-title gap (vertical here)
{\titlerule\vspace{2pt}#1} % before-code, then the title itself
\titlespacing*{\section}{0pt}
{3.5ex plus 1ex minus .2ex}{2.3ex plus .2ex}
% A run-in subsection: "1.1 Beta. Text continues here."
\titleformat{\subsection}[runin]
{\normalfont\bfseries}{\thesubsection}{.5em}{#1.}
\titlespacing*{\subsection}{0pt}{2ex plus .5ex}{.5em}
\begin{document}
\section{Alpha}
The paragraph after a starred \titlespacing is not indented.
\subsection{Beta}
This text runs on from the heading, on the same line.
\end{document}Igualar el índice y el título de página: titletoc y titleps
Una vez rediseñados los títulos, normalmente se quiere que el índice y el título de página concuerden. titlesec incluye en el mismo paquete dos compañeros, utilizables por separado: titletoc para el índice y titleps para los estilos de página. El núcleo de titletoc son \titlecontents, que define por completo una entrada del índice, y \dottedcontents, un ajuste rápido con líderes de puntos. La única trampa es que el primer argumento ⟨section⟩ es el nombre sin barra invertida (section, chapter, figure, …). titleps define un estilo de página entero con \newpagestyle y rellena sus casillas izquierda, central y derecha con \sethead y \setfoot. Qué nombre de título llega al título de página lo controla \settitlemarks, y \bottitlemarks, \toptitlemarks y \firsttitlemarks permiten elegir cuál de los títulos de la página usar. Construir encabezados y pies como tales —incluido fancyhdr— corresponde a otra página.
\usepackage{titletoc}
% Dotted leaders for section entries:
% indent 1.5em, no numbered-entry format, label width 2.3em, leader 1pc
\dottedcontents{section}[1.5em]{}{2.3em}{1pc}
\usepackage{titleps}
\newpagestyle{main}{\sethead{\thesection}{}{\thepage}}
\pagestyle{main}Por qué no debe usar titlesec con KOMA-Script ni memoir
Ambas familias construyen sus títulos con maquinaria propia, así que un titlesec que intente interceptar \@startsection por encima no encaja. Eso sí, fallan de maneras muy distintas. KOMA-Script protesta. Cargue titlesec en scrartcl y obtendrá primero Class scrartcl Warning: Usage of package titlesec together with a KOMA-Script class is not recommended., y después un mensaje por título: Package titlesec Warning: Non standard sectioning command \section detected. Using default spacing and no format. Ese segundo mensaje es el fondo del asunto: el \section de KOMA-Script llama a \scr@startsection y no a \@startsection, de modo que titlesec no consigue leer nada del diseño de la clase y recompone el título con sus propios valores por omisión. Y no siempre se queda en un aviso. Combínelo con una opción de títulos del lado de la clase, como \documentclass[headings=big]{scrartcl}, y el resultado medido es ! Package titlesec Error: No format for this command. seguido de ! Missing number, treated as zero. e ! Illegal unit of measure (pt inserted).: la composición se derrumba ahí mismo. Con KOMA-Script, retoque los títulos con la interfaz de la clase: \setkomafont y \RedeclareSectionCommand.
memoir, en cambio, no dice nada en absoluto. Probado en TeX Live 2024, la combinación compila sin aviso ni error alguno y \titleformat funciona como se espera. La razón es que el \section de memoir tiene la forma \sechook \@startsection {section}{1}{\secindent}{\beforesecskip}{\aftersecskip}{\normalfont \secheadstyle}, que titlesec reconoce como estándar y puede releer. Ahora bien, al mirar lo releído, \secheadstyle y \secindent se conservan como nombres, mientras que los espacios superior e inferior quedaron congelados en los valores vigentes al cargar, 15.0694pt y 9.90276pt. Es decir, \setsecheadstyle sobrevive pero \setbeforesecskip y \setaftersecskip dejan de tener efecto: el paquete rompe la mitad de la interfaz, en silencio. memoir trae un mecanismo completo propio para diseñar títulos, y usar ese es el camino más seguro.
Dos cosas más alrededor de titlesec que conviene saber. \titleclass reasigna la clase de un nivel, como en \titleclass{\subsection}{straight}; los nombres de clase válidos son top, page y straight, y cualquier otro provoca Package titlesec Error: Unknown sectioning class. \assignpagestyle{\chapter}{plain} fija el estilo de la página en que arranca un título dado. Y una nota sobre el autor: Javier Bezos es además el mantenedor actual del paquete multilingüe babel y el autor de enumitem, que controla el formato de las listas; así que pasar un día en LaTeX ajustando títulos, listas e idiomas es cruzarse tres veces con el código de la misma persona.