\documentclass[unknownoption]{article} compila sin problemas. Una opción de clase mal escrita no es un error: LaTeX entierra LaTeX Warning: Unused global option(s): en el registro y te entrega el PDF igualmente, así que rara vez se nota. Escribe en cambio \usepackage[unknownoption]{color} y la compilación se detiene en seco. Esa asimetría no es un capricho: procede de un diseño deliberado en el que una opción no reconocida tiene un destino por omisión distinto en una clase que en un paquete. Partiendo de ese mecanismo, esta página construye \DeclareOption y \ProcessOptions, el más reciente \DeclareKeys y, por último, \LoadClass, que permite asentar tu propia clase sobre una existente.
Por qué una opción de clase mal escrita no detiene la compilación
La respuesta está escrita en el clsguide oficial. Si un archivo de clase no contiene ningún \DeclareOption*, todas las opciones que no declaró se pasan en silencio a todos los paquetes. Si un archivo de paquete no contiene ningún \DeclareOption*, cada opción no declarada produce un error. Así, una opción de clase se va llevando por si alguien todavía la quiere, y solo cuando nadie la ha reclamado al llegar a \begin{document} informa LaTeX de LaTeX Warning: Unused global option(s): seguido de los nombres sobrantes entre corchetes. Una opción de paquete, en cambio, no tiene otro destino, así que un nombre desconocido se convierte de inmediato en ! LaTeX Error: Unknown option 'unknownoption' for package 'color'.
El diseño tiene sentido: las opciones que la clase misma no conoce pero que un paquete cargado después reclamará —las opciones globales, como en \documentclass[dvipsnames]{article}— se usan a diario. El precio es que las erratas guardan silencio. Dos hábitos compensan en la práctica. Primero, buscar Unused global option en el registro tras cada compilación. Segundo, poner \listfiles al principio del preámbulo para que el final del registro liste todos los archivos cargados con su versión. Por cierto, llamar a \OptionNotUsed dentro del código de una opción la envía a propósito a esa misma lista de opciones sin usar.
La diferencia entre una clase (.cls) y un paquete (.sty)
El clsguide enuncia la prueba en una línea: si las órdenes pueden usarse con cualquier clase de documento, hazlas paquete; si no, hazlas clase. Una clase define el tipo de documento y se carga exactamente una vez, con \documentclass. Un paquete se carga con \usepackage, tantos como quieras, y añade funciones independientes del tipo de documento. El ejemplo de la propia guía lo aclara: la clase que una empresa escribe para componer cartas en su papel con membrete se apoya en letter pero no sirve con ninguna otra clase, de ahí ownlet.cls; el paquete graphics, que incluye imágenes, funciona con todas las clases, de ahí graphics.sty.
También hay dos tipos de clase: las autónomas, como article, report y letter, y las que son extensiones o variantes de otra clase; el clsguide cita proc, construida sobre article. Cualquier clase que escribas tú será casi con seguridad del segundo tipo, porque montar una maqueta desde cero rara vez compensa. Las convenciones de autoría de un .cls y un .sty son casi idénticas; las órdenes simplemente vienen en parejas Class y Package (\ProvidesClass ↔ \ProvidesPackage, \LoadClass ↔ \RequirePackage, \PassOptionsToClass ↔ \PassOptionsToPackage).
Las opciones estándar que tu clase debería aceptar
Los usuarios pasarán opciones a tu clase igual que a una estándar, así que como mínimo conviene tener el elenco habitual: 10pt / 11pt / 12pt para el tamaño base del cuerpo, a4paper / letterpaper para el papel, onecolumn / twocolumn para las columnas, oneside / twoside para una o dos caras, y draft, que marca las líneas desbordadas con una barra negra (su opuesto es final). No hace falta que implementes ninguna: como veremos, lo normal es reenviarlas a la clase base.
| Opción | Significado | Predeterminado |
|---|---|---|
10pt / 11pt / 12pt | tamaño base del cuerpo | 10pt |
a4paper / letterpaper | tamaño del papel (también b5paper, legalpaper, …) | letterpaper |
onecolumn / twocolumn | una columna / dos columnas | onecolumn |
oneside / twoside | diseño a una cara / doble cara | oneside (pero twoside en book) |
draft / final | si las líneas desbordadas reciben una barra negra | final |
Si quieres que la clase decida qué ocurre cuando el usuario no especifica nada, escribe \ExecuteOptions{a4paper,11pt} antes de \ProcessOptions. Declara «ejecuta primero el código de estas opciones», y el clsguide presenta justamente esta forma para dar a una clase su diseño por omisión. Usar esas opciones desde el \documentclass[...] del documento, y las propias de book como openright, corresponde a la página sobre la clase de documento y el preámbulo. De aquí en adelante nos concentramos en escribir la clase que las recibe.
Identificar el archivo al principio: \NeedsTeXFormat y \ProvidesClass
Las dos primeras líneas de un archivo de clase (myclass.cls) son casi boilerplate. \NeedsTeXFormat{LaTeX2e} declara que el archivo está pensado para LaTeX2e. Después, \ProvidesClass{myclass}[2026/01/01 v1.0 My example class] anuncia el nombre de la clase, la fecha, la versión y una breve descripción. Donde esa línea se gana el sueldo es en el registro: al compilar aparece Document Class: myclass 2026/01/01 v1.0 My example class. Cuando un coautor dice que el documento no compila, pedirle solo esa línea revela al instante si tiene un .cls antiguo.
La parte entre corchetes es opcional, pero incluirla permite a los usuarios exigir una versión mínima mediante la fecha en formato YYYY/MM/DD, como \documentclass{myclass}[2026/01/01]. Si escribes un paquete, el equivalente es \ProvidesPackage{mypackage}[2026/01/01 v1.0 ...]; \NeedsTeXFormat es común a ambos. Regla firme: el nombre en \ProvidesClass debe coincidir con el nombre real del archivo; dentro de myclass.cls, escribe siempre \ProvidesClass{myclass}.
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{myclass}[2026/01/01 v1.0 My example class]Declarar opciones: \DeclareOption y \CurrentOption
Cada opción que acepta tu clase se declara con \DeclareOption{option}{code}. Cuando el usuario la especifica, su code se ejecuta al llegar a \ProcessOptions (véase más abajo). El código puede ser cualquier construcción válida de LaTeX, pero en la práctica suele ser una sola línea que activa una bandera booleana creada con \newif; dejar el trabajo pesado para después, consultando la bandera, evita accidentes de orden.
El receptáculo de las opciones no declaradas es la forma con estrella \DeclareOption*{code}, dentro de la cual \CurrentOption se expande al nombre de la opción que se procesa. La línea más habitual de una clase propia la usa para reenviar cualquier opción desconocida tal cual a la clase base. Gracias a ella, los usuarios pueden pasar 10pt o a4paper con toda naturalidad aunque nunca las declararas: has redirigido hacia un destino elegido por ti el comportamiento por omisión que vimos al principio, «una clase la pasa en silencio».
% pass anything we do not handle ourselves on to article
\DeclareOption*{\PassOptionsToClass{\CurrentOption}{article}}Procesar las opciones y cargar la clase base: \ProcessOptions y \LoadClass
Declarar no basta: el código de las opciones elegidas solo se ejecuta al llamar a \ProcessOptions. En la práctica casi siempre se escribe \ProcessOptions\relax. Como también existe un \ProcessOptions* con estrella, el \relax final selecciona con seguridad la forma sin estrella y evita una lectura anticipada innecesaria y mensajes de error confusos; el clsguide lo recomienda explícitamente. La forma sin estrella procesa las opciones en el orden en que las declaraste; la forma con estrella, en el orden en que las listó quien llama.
Montar una maqueta desde cero rara vez compensa, así que la mayoría de clases propias se apoyan en una existente mediante \LoadClass[options]{article}, que carga todas las órdenes y el estilo de article.cls. Esta orden solo puede usarse dentro de un archivo de clase, y como mucho una vez por archivo. El orden importa: para que las opciones que el usuario dio en \documentclass[...] lleguen a la clase base, pon \LoadClass después del procesamiento de opciones (\ProcessOptions): declara el reenvío, deja que \ProcessOptions reparta y luego carga la base. Si solo quieres entregar exactamente las opciones que recibió tu clase, \LoadClassWithOptions{article} es el atajo; en un paquete, \RequirePackage sustituye a \LoadClass, y \RequirePackageWithOptions cubre el caso de pasarlo todo.
Todo lo que sigue a \LoadClass es donde por fin aparece el carácter de tu clase: redefinir encabezados con \renewcommand, ajustar márgenes con \setlength, definir órdenes y entornos nuevos con \newcommand / \newenvironment. Los paquetes adicionales que necesites se cargan aquí con \RequirePackage. Dicho al revés: recuerda que antes de \LoadClass solo van la declaración y el procesamiento de opciones, y las dudas de orden dejan de existir.
Ejemplo completo: una clase mínima que extiende article
Al reunir todo lo anterior sale este .cls mínimo. Se apoya en article, añade su propia opción draft, reenvía las opciones desconocidas a article, aporta a4paper como valor por omisión y, por último, fija los márgenes y la numeración de secciones a su gusto. Guárdalo como myclass.cls junto al manuscrito y úsalo con \documentclass[11pt,a4paper,draft]{myclass}.
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{myclass}[2026/01/01 v1.0 My example class]
% --- declare options ---
\newif\if@my@draft \@my@draftfalse
\DeclareOption{draft}{\@my@drafttrue}
% forward everything else to article
\DeclareOption*{\PassOptionsToClass{\CurrentOption}{article}}
% --- defaults, then execute, then load the base class ---
\ExecuteOptions{a4paper}
\ProcessOptions\relax
\LoadClass{article}
% --- this class's own character ---
\RequirePackage[margin=25mm]{geometry}
\setlength{\parindent}{0pt}
\renewcommand{\thesection}{\Alph{section}}
\if@my@draft
\AtBeginDocument{\typeout{myclass: DRAFT MODE}}
\fi
\endinputEl \endinput final le dice a LaTeX que el archivo termina ahí; se incluye por convención, y las notas o ejemplos escritos después nunca se leen. Para convertirlo en paquete, cambia \ProvidesClass por \ProvidesPackage y \LoadClass por \RequirePackage: el mismo esqueleto pasa a ser un .sty.
La forma moderna: \DeclareKeys y \ProcessKeyOptions
\DeclareOption sigue siendo del todo válido, pero está pensado para interruptores presente/ausente; una opción con valor, como logo=acme.pdf, te dejaría analizarla a mano. Por eso el kernel de LaTeX ofrece ya su propia interfaz clave-valor: declarar claves con \DeclareKeys y procesarlas con \ProcessKeyOptions. Cada nombre de clave lleva una «propiedad» que decide su comportamiento; las básicas son .code (ejecutar código arbitrario), .if / .ifnot (activar un booleano de TeX), .store (guardar el valor en una macro) y .usage (si la opción solo puede darse al cargar, en cualquier punto del preámbulo, o sin restricción). Las claves desconocidas van a \DeclareUnknownKeyHandler, y una vez llamas a \ProcessKeyOptions no hace falta llamar además a \ProcessOptions.
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{keyclass}[2026/01/01 v1.0 Key-value demo class]
\DeclareKeys[keyclass]{
draft.if = @keyclass@draft,
logo.store = \@keyclass@logo,
logo.usage = load
}
% anything that is not one of our keys goes to article
\DeclareUnknownKeyHandler[keyclass]{%
\PassOptionsToClass{\CurrentOption}{article}}
\ProcessKeyOptions[keyclass] % no \ProcessOptions needed
\LoadClass{article}
\endinputEste mecanismo venía originalmente del paquete l3keys2e, cuyo núcleo se incorporó después al kernel de LaTeX2ε (el kernel que trae TeX Live 2024 es LaTeX2e 2023-11-01 y ofrece \DeclareKeys, \ProcessKeyOptions y \SetKeys). Algunos paquetes existentes todavía cargan l3keys2e: jlreq.cls, por ejemplo, ejecuta \RequirePackage{l3keys2e} cerca del principio. La elección es sencilla: si aunque sea una opción toma un valor, usa \DeclareKeys; si todas son interruptores, \DeclareOption basta. Para cambiar ajustes después de cargar, usa \SetKeys.
La prueba mínima antes de compartir
Una clase afecta a todo el documento en cuanto se carga, así que asienta su comportamiento con un documento de prueba diminuto antes de escribir contenido real. Solo hay dos cosas que comprobar: que opciones estándar como 11pt o twocolumn llegan a la clase base, y que solo tus opciones propias las maneja tu código. Si aquí no se comporta como esperas, la causa es casi con seguridad la posición de \ProcessOptions, el reenvío en \DeclareOption* o el orden de \LoadClass.
\listfiles % log every file and version that is loaded
\documentclass[11pt,a4paper,draft]{myclass}
\begin{document}
\section{Smoke test}
Check the body size, the paper, the draft switch,
the heading style and the margins.
\end{document}- ¿El registro identifica la clase? Confirma que la línea
Document Class: myclass ..., con la fecha y versión que escribiste en\ProvidesClass, aparece en el.log. Una discrepancia entre nombre de archivo y nombre de clase acabará confundiendo a alguien. - ¿Conservaste las opciones estándar? Si
11ptotwocolumnse ignoran, revisa el reenvío de\DeclareOption*o la posición de\LoadClass. - Escribe mal una opción a propósito. Compila
\documentclass[nosuchoption]{myclass}y comprueba que apareceUnused global option(s)en el registro. Si no aparece, algún paquete al que reenvías se la está tragando en silencio. - Deja vacío todo lo que sigue a
\endinput. Las notas o ejemplos dejados al final se leerán como entrada en cuanto ese marcador desaparezca.
Piensa también en la forma de distribución
Una clase propia se pone realmente a prueba no cuando funciona por primera vez en tu máquina, sino cuando otra persona la carga en otro entorno. Como mínimo, guarda en un mismo directorio el .cls, un documento de ejemplo corto, un README y un changelog, y verifica que el ejemplo compila tal cual. En el README, separa las opciones que reenvías a la clase base de las que maneja tu propia clase, para que se pueda rastrear dónde surte efecto 11pt. Cuando el proyecto crezca, pasa a doc y docstrip, propios de LaTeX: fuente y comentario juntos en un .dtx y el .cls generado desde un .ins, con lo que distribución y documentación van por la misma vía.
myclass/
myclass.cls
sample.tex
README.md
CHANGELOG.mdPor último, pon \listfiles en el ejemplo. El final del registro listará entonces todos los archivos cargados con su versión, de modo que se distingue de un vistazo si un usuario arrastra un myclass.cls local antiguo o si los paquetes esperados se cargan de verdad. Una clase es el cimiento de todo el documento: a la larga, un orden de carga, un procesamiento de opciones y una información de registro cuidados rinden mucho más que una macro más para el cuerpo.