# Automatically set description list labelwidth based on widest label?

How do I create a description list where the item text is automatically indented to the width of the widest label?

This is the same question as Description list with aligned descriptions but I would like the width of the widest label to be automatically calculated.

For example, I'd like the following output:

to be produced by the following input:

\documentclass{article}
\usepackage{calc}
\usepackage{enumitem}
\usepackage{lipsum}
\usepackage[margin=1em,papersize={4in,2.2in}]{geometry}

\newenvironment{mydescription}{%
\begin{description}[
leftmargin=!,
labelwidth=\magicgoeshere,
]%
}{%
\end{description}%
}

\newcommand{\text}{long long long long long long long long long long
long long long long long long long long long long long long long
long long long long long long long text}

\begin{document}
\begin{mydescription}
\item[The longest label] text
\item[Short] \text
\end{mydescription}
\hrule
\begin{mydescription}
\item[Medium label] text
\item[Short] \text
\end{mydescription}
\end{document}


# Solution

Here is a solution that uses the eqparbox package to measure item label widths. It avoids some of the limitations of the solution described in Gonzalo Medina's answer (see the bottom of this post) because it doesn't typeset the entire list twice. The trade-off is that it requires an additional compilation run because widths measured during one run will only become available for the next one.

You can declare a version of the description environment that does what you want by adding the following lines to your preamble. (Only works for enumitem v3.7+, see below.)

%% Requires enumitem v3.7+
\newlist{mydescription}{description}{1} %% <- pick a larger number if you want to nest these
\setlist[mydescription]{
labelwidth=\eqboxwidth{listlabel@\EnumitemId},
leftmargin=!,
format=\mydescriptionlabel,
}
\newcommand\mydescriptionlabel[2][l]{\eqmakebox[listlabel@\EnumitemId][#1]{#2}}


What this does is put the description item labels in in \eqmakeboxes so that their widths are written to the aux file. From the second run onwards, all such boxes in the same list will be given the same width (largest among them) and this width will be used as the list's labelwidth.

If you're using a version of enumitem that is older than v3.7 (which was released on 2019-01-04), you can still make this work by adding the following lines:

%% Only required for enumitem v3.6-
\usepackage{etoolbox} %% <- for \AtBeginEnvironment
\newcounter{mydescription}
\AtBeginEnvironment{mydescription}{%
\stepcounter{mydescription}%
\edef\EnumitemId{\arabic{mydescription}}%
}


(N.B. Using the before key for this wouldn't work because the code would be run too late.)

## Application to your MWE

Here is my solution applied to your MWE (version for enumitem v3.7+).

\documentclass{article}
\usepackage{eqparbox}
\usepackage{enumitem}
\usepackage[margin=1em,papersize={4in,2.2in}]{geometry}

\newlist{mydescription}{description}{1} %% <- pick a larger number if you want to nest these
\setlist[mydescription]{
labelwidth=\eqboxwidth{listlabel@\EnumitemId},
leftmargin=!,
format=\mydescriptionlabel,
}
\newcommand\mydescriptionlabel[2][l]{\eqmakebox[listlabel@\EnumitemId][#1]{#2}}

\newcommand{\text}{long long long long long long long long long long
long long long long long long long long long long long long long
long long long long long long long text}

\begin{document}
\begin{mydescription}
\item[The longest label] text
\item[Short] \text
\end{mydescription}
\hrule
\begin{mydescription}
\item[Medium label] text
\item[Short] \text
\end{mydescription}
\end{document}


# How this works

• I used \newlist instead of \newenvironment to define the mydescription environment and \setlist to customise it. You could of course also forego the \newlist and modify the normal description environment instead.
• enumitems format key is used to put every item label in your mydescription environment in an \eqmakebox. All \eqmakeboxes with the same tag will be given the same dimensions (after the second run). They're centred by default, but I provided [l] as an option to make them left-aligned.
• The tag used by this \eqmakebox includes \EnumitemId, which is a unique identifier for the current list environment and is provided by enumitem.
• The width of the widest such box for a given list is retrieved using \eqboxwidth and used as the labelwidth for that list.
• The calculated horizontal length is leftmargin, as in your MWE.

The documentation for enumitem and eqparbox can be found here and here respectively

### Notes

• If you want all such list environments in your document to use the same labelwidth you can simply remove both instances of \EnumitemId in my code snippet.
• These can be nested safely if declared using \newlist{mydescription}{description}{n} for with n greater than 1.
• To right-align, centre or stretch (multi-word only) your the labels, \mydescriptionlabel can be replaced by \mydescriptionlabel[r], \mydescriptionlabel[c] or \mydescriptionlabel[s] respectively.
• You can combine \mydescriptionlabel with other text formatting commands, but they should precede \mydescriptionlabel and none of them should take an argument (so \itshape is okay, but \textit is not).
• The item labels themselves are still typeset twice and assignments made within them will thus still be performed twice, which I feel is unlikely to be a problem. This could be avoided, but not without making the preamble significantly longer.

• The labels themselves are still typeset twice, and definitions in the labels will thus still be applied twice as well. This can be avoided but this is less likely to be a problem than typesetting the entire list twice.

• The labels are not allowed to contain anything you wouldn't be allowed to put in a tabular environment, but I don't think that's much of a restriction since I can't think anything not allowed in a tabular that is allowed in an item label.

# Comparison to the other solution

Gonzalo Medina's answer will work well in most circumstances, but it's a bit heavy-handed and has a few limitations:

• \NewEnviron has a few subtle problems and restrictions;
• display equations don't work inside lists defined this way;
• counters incremented from anywhere in the list will be incremented twice and the same goes for other global assignments made within the list environment;
• it won't work if the labels don't use boldface formatting;
• multiple such environments can't be nested.

Downsides of my answer:

• My solution requires an additional compilation run because the label widths measured during the first run are only available during the second.
• @RichardHansen: I had overlooked that my answer wouldn't work with versions of enumitem older than v3.7. I've added the required code to still make it work and improved the answer while I was at it. Jun 8, 2020 at 18:40
• Terribly underrated answer! I was surprised to find out that this kind of formating doesn't seem to be provided out of the box, eg. by some package. I'd suggest to incorporate this as an option into enumitem. For whoever might want to tweak the formating like me: changes of labelwidth need to be written somehow like labelwidth={\dimexpr \eqboxwidth{listlabel@\EnumitemId} + 1em} Jul 22, 2021 at 19:48
• Thank you :). If you're changing the value of labelwidth to change the distance between the label and the item I would recommend using the labelsep key instead (or labelsep* if you prefer, see the enumitem documentation). I expect setting labelsep=\dimexpr\labelsep+1em (or labelsep=1.5em in a 10pt article with no further customisation) will have the exact same effect. Jul 22, 2021 at 21:48

A variation on egreg's answer to Working with margins

\documentclass{article}
\usepackage{enumitem}
\usepackage{environ}

\newlength\widest
\makeatletter
\NewEnviron{ldescription}{%
\vbox{%
\global\setlength\widest{0pt}%
\def\item[##1]{%
\settowidth\@tempdima{\textbf{##1}}%
\ifdim\@tempdima>\widest\global\setlength\widest{\@tempdima}\fi%
}%
\setbox0=\hbox{\BODY}%
}
\begin{description}[
leftmargin=\dimexpr\widest+0.5em\relax,
labelindent=0pt,
labelwidth=\widest]
\BODY
\end{description}%
}
\makeatother

\begin{document}

\begin{ldescription}
\item[Short] text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text
\item[A really really long label] text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text
\end{ldescription}

\begin{ldescription}
\item[Short] text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text
\item[A medium label] text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text
\end{ldescription}

\end{document}


In the first part, the width of the longest label is calculated: initially, \widest is set to 0pt; for each \item, the length of its optional argument is measured and the length stored in \@tempdima; if \@tempdima is greater than \widest, (this is always true for the first \item) then \widest is updated to be \@tempdima. This is done for all \items; \BODY is set inside a box that is never used. Then, a description environment is used, setting \labelwidth to the previously calculated value for \widest.

Answers to follow-up questions:

• Q: What is the point of \setbox0=\hbox{\BODY}?
A: This just boxes \BODY without typesetting it.
• Q: Since \BODY is used twice, what happens if \BODY contains something that has side effects (writes to the .aux file, contains \newcommand, contains \footnote, etc.)?
A: There's no problem with what \BODY contains (as long as it's reasonable for a standard description, so, for example, sectional unit commands won't be allowed, but they don't make sense in a description anyways).
• Q: Why is that stuff wrapped in a \vbox{}?
A: The \vbox is used to prevent the dreadful "Something's wrong--perhaps a missing \item." error at the beginning of a list (\hbox or \mbox could have also been used instead).
• Thanks! Can you explain how this works? I thought I would have to somehow save the widest length to the .aux file and reuse it in a second pass, but your example shows that this is not necessary. I'm not sure how you do it without requiring a second pass. Aug 25, 2013 at 23:47
• @RichardHansen I added a short explanation at the end of my updated answer. Aug 26, 2013 at 0:39
• Thanks for updating it, but I still have some questions: Why is that stuff wrapped in a \vbox{}? What is the point of \setbox0=\hbox{\BODY}? Doesn't redefining \item affect spacing and \label? Since \BODY is used twice, what happens if \BODY contains something that has side effects (writes to the .aux file, contains \newcommand, contains \footnote, etc.)? Aug 26, 2013 at 4:40
• @RichardHansen For some reason I didn't receive the notification of your last comment, so I just casually saw it. \setbox0=\hbox{\BODY} just boxes \BODY without typesetting it. There's no problem with what \BODY contains (as long as it's reasonable for a standerd descrition, so, for example, sectional unit commands won't be allowed, but they don't make sense in a description anyways). The \vbox is used to prevent the dreadful "Something's wrong--perhaps a missing \item." error at the beginning of a list (\hbox or \mbox could have also been used instead). Aug 27, 2013 at 0:51
• @RichardHansen Just a Hint : It would be advisable to ask GonzaloMedina to add his explanation from comments to answer by pinging him so that he would make the answer flow continuous and smooth. Aug 27, 2013 at 4:30