From 9de8091bfb3391699d0b900932b91525a86d490e Mon Sep 17 00:00:00 2001
From: Norbert Preining <norbert@preining.info>
Date: Wed, 3 Feb 2021 03:00:54 +0000
Subject: CTAN sync 202102030300

---
 macros/latex/contrib/cellprops/cellprops.dtx | 451 +++++++++++++++++++++------
 1 file changed, 364 insertions(+), 87 deletions(-)

(limited to 'macros/latex/contrib/cellprops/cellprops.dtx')

diff --git a/macros/latex/contrib/cellprops/cellprops.dtx b/macros/latex/contrib/cellprops/cellprops.dtx
index 86c1d8ef2c..264361e91e 100644
--- a/macros/latex/contrib/cellprops/cellprops.dtx
+++ b/macros/latex/contrib/cellprops/cellprops.dtx
@@ -1,7 +1,7 @@
 % \iffalse meta-comment
 % vim: tw=80 spl=en
 %
-%% File: cellprops.dtx (C) Copyright 2016-2020 RIVAUD Julien
+%% File: cellprops.dtx (C) Copyright 2016-2021 RIVAUD Julien
 %%
 %% It may be distributed and/or modified under the conditions of the
 %% General Public License (GPL), either version 3 of this
@@ -14,8 +14,8 @@
 \RequirePackage{expl3}[2018/06/19]
 \def\ExplFileName{cellprops}
 \def\ExplFileDescription{CSS-like cell and table properties}
-\def\ExplFileDate{2020/06/07}
-\def\ExplFileVersion{1.7a}
+\def\ExplFileDate{2021/01/30}
+\def\ExplFileVersion{2.0}
 %</driver|package>
 %<*driver>
 \documentclass[full]{l3doc}
@@ -66,13 +66,13 @@
 %
 % I recommend to add globally:
 %\begin{verbatim}
-%\cellprops{ td { padding: 1ex; min-height: 0pt; min-depth: 0pt; } }
+% \cellprops{ td { padding: 1ex; min-height: 0pt; min-depth: 0pt; } }
 %\end{verbatim}
 % so that you get better-looking tables by default.
 %
 % \subsection{Examples}
 %
-% To produce:
+% To produce the (arguably ugly):
 % \[
 %   \cellprops{
 %       td {
@@ -102,6 +102,15 @@
 %           math-mode: text;
 %           text-align: left;
 %       }
+%       tr:nth-child(6) td:nth-child(1) {
+%           vertical-align: top; % see remark in usage guide
+%       }
+%       tr:nth-child(6) td:nth-child(2) {
+%           vertical-align: middle;
+%       }
+%       tr:nth-child(6) td:nth-child(3) {
+%           vertical-align: bottom; % see remark in usage guide
+%       }
 %   }
 %   \begin{array}{nnnp{5em}}
 %       This is text & A_2 & A_3 & A_4 \\
@@ -109,6 +118,16 @@
 %       C1 &  C_2  &  X  & Y \\
 %       D1 &  D_2  &  DX & v \\
 %       &  F  &  \int_a^b f(t) dt & v \\
+%       \fbox{Top} & \fbox{Middle} & \fbox{Bottom} & \fbox{Baseline}%
+%       \textcolor{red}{%
+%          \kern -9cm\relax
+%          \vrule height 0.1pt depth 0.1pt width 10cm
+%          \kern -1cm\relax
+%       }\textcolor{blue}{%
+%          \kern -9cm\relax
+%          \vrule height 0.52ex depth -0.48ex width 10cm
+%          \kern -1cm\relax
+%       } \\
 %   \end{array}
 % \]
 % you can use:
@@ -142,13 +161,32 @@
 %           math-mode: text;
 %           text-align: left;
 %       }
+%       tr:nth-child(6) td:nth-child(1) {
+%           vertical-align: top; % see remark in usage guide
+%       }
+%       tr:nth-child(6) td:nth-child(2) {
+%           vertical-align: middle;
+%       }
+%       tr:nth-child(6) td:nth-child(3) {
+%           vertical-align: bottom; % see remark in usage guide
+%       }
 %   }
-%   \begin{array}{nnnn}
+%   \begin{array}{nnnp{5em}}
 %       This is text & A_2 & A_3 & A_4 \\
 %       B1 & This is maths & B_3 & \\
 %       C1 &  C_2  &  X  & Y \\
 %       D1 &  D_2  &  DX & v \\
-%       E &  F  &  \int_a^b f(t) dt & v \\
+%       &  F  &  \int_a^b f(t) dt & v \\
+%       \fbox{Top} & \fbox{Middle} & \fbox{Bottom} & \fbox{Baseline}%
+%       \textcolor{red}{%
+%          \kern -9cm\relax
+%          \vrule height 0.1pt depth 0.1pt width 10cm
+%          \kern -1cm\relax
+%       }\textcolor{blue}{%
+%          \kern -9cm\relax
+%          \vrule height 0.52ex depth -0.48ex width 10cm
+%          \kern -1cm\relax
+%       } \\
 %   \end{array}
 % \]
 %\end{verbatim}
@@ -200,7 +238,7 @@
 %
 % This table has been produced by:
 %\begin{verbatim}
-%\cellprops{
+% \cellprops{
 %   td { border: thin solid black; }
 %   tr:nth-child(-n+3) { background-color: black!10; }
 %   tr:nth-child(n+8) { background-color: blue!10; }
@@ -210,12 +248,12 @@
 %   tr:nth-child(4n+3) td:nth-child(4) {
 %       border: thick solid red;
 %   }
-%}
-%\begin{longtable}{nnnn}
+% }
+% \begin{longtable}{nnnn}
 %   aaaaa & baaaa & caaaaa & dbbbb \\
 %   ...
 %   aaaaa & baaaa & caaaaa & dbbbb \\
-%\end{longtable}
+% \end{longtable}
 %\end{verbatim}
 %
 % \subsection{Usage guide}
@@ -229,15 +267,16 @@
 %                   ]
 %    \item[usage] "'\cellprops{'" [ \<selectors> "'{'" \<properties> "'}'" ]* "'}'"
 %    \item[selectors] \<selector> ["," \<selectors> ]
-%    \item[selector] [\<environment> "' '"] \<element1>
-%    \item[element1]
-%           "'table'" \OR
-%           "'tr'"[\<pseudo-class>] ["' '" \<element2>] \OR
-%           \<element2>
-%    \item[element2] "'td'"[\<pseudo-class>] ["' '" \<parbox>] \OR \<parbox>
-%    \item[parbox] "'p'"
-%    \item[pseudo-class] "':nth-child('"\<nth>"')"
-%    \item[nth] \<number> \OR "'odd'" \OR "'even'" \OR \<number>"'n+'"\<number>
+%    \item[selector] [\<table> "' '"] [\<tr> "' '"] [\<td> "' '"] [\<parbox>]
+%    \item[table] "'table'" ["'.'" \<class>]* ["':where(.'"\<class>"')'"]*
+%                   \OR "':where('"\<table>"')'"
+%    \item[tr] "'tr'" [\<pseudoclass>]* \OR "':where('"\<tr>"')'"
+%    \item[td] "'td'" [\<pseudoclass>]* \OR "':where('"\<td>"')'"
+%    \item[parbox] "'p'" \OR "':where('"\<p>"')'"
+%    \item[pseudoclass] "':where('"[\<pseudoclass>]*"')'" \OR "':nth-child('"\<nth>"')"
+%    \item[nth]
+%       "'odd'" \OR "'even'" \OR \<number> \OR
+%       \<number>"'n+'"\<number> \OR \<number>"'n-'"\<number>
 %    \item[properties] [ \<property> "';'" ]*
 %    \item[property]
 %       "'padding: '" ( \<dimension> ) \{1,4\} \OR \\
@@ -249,6 +288,7 @@
 %       "'min-depth: '" \<dimension> \OR \\
 %       "'min-width: '" \<dimension> \OR \\
 %       "'text-align: '" ( "'left'" \OR "'right'" \OR "'center'" ) \OR \\
+%       "'vertical-align: '" ( "'top'" \OR "'middle'" \OR "'baseline'" \OR "'bottom'" ) \OR \\
 %       "'math-mode: '" ( "'text'" \OR "'math'" \OR "'auto'" ) \OR \\
 %       "'color: '" \<color> \OR \\
 %       "'background-color: '" ( \<color> \OR "'transparent'" ) \OR \\
@@ -323,6 +363,29 @@
 %        ":nth-child("$B$")" is supported, with arbitrary $A$~and~$B$, including
 %        nothing for~$A$ (standing for~$A = 1$) or just a minus sign (standing
 %        for~$A = -1$).
+%    \item "vertical-align" values "baseline" and "middle" are following the CSS
+%        specification. On the other hand, "top" (resp.~"bottom") align the top
+%        (resp.~bottom) of the cell with the row baseline which gives a
+%        different result than CSS if there is mixed alignment in the row.
+%        The common case where all cells have "vertical-align: top" or
+%        "vertical-align: bottom" behaves as expected from the CSS
+%        specification.
+%
+%        As fas as I can tell, obeying CSS in all cases would require
+%        typesetting each row in two passes.
+%    \item Class names are matched against the classes defined with
+%        \cs{cellpropsclass}, which takes a space-separated list of classes. By
+%        default, \cs{cellpropsclass} also add as a class the name of the
+%        environment that created the tabular-like structure (that is, "tabular"
+%        or "array" or "matrix" or similar). If you do not want that behavior,
+%        you can use \cs{cellpropsclass*}. The class list is
+%        redefined locally, and \pkg{cellprops} initially calls
+%        "\cellpropsclass{}".
+%    \item The ":where()" pseudo-class is the same as in CSS selectors level 4,
+%        except that it does not accept spaces nor commas. Its only effect is to
+%        neutering the specificity of the selectors within while still keeping
+%        their matching conditions. This is mainly useful to write default yet
+%        specific rules whose specificity would otherwise dwarf simple rules.
 % \end{itemize}
 %
 % \subsection{Compatibility}
@@ -465,20 +528,16 @@
     \seq_map_inline:Nn \l_tmpa_seq {
         \tl_if_empty:nF {##1} {
             \exp_args:NNV \seq_set_split:Nnn \l_tmpb_seq \c_colon_str {##1}
-            \int_compare:nNnTF {\seq_count:N \l_tmpb_seq} = { 2 } {
+            \int_compare:nNnT {\seq_count:N \l_tmpb_seq} = { 2 } {
                 \seq_get_left:NN \l_tmpb_seq \l_tmpa_tl
                 \exp_args:NNV \str_set:Nn \l_tmpa_str \l_tmpa_tl
                 \seq_get_right:NN \l_tmpb_seq \l_tmpa_tl
-                \cs_if_exist:cTF { _@@_property_type_\l_tmpa_str :nn } {
+                \cs_if_exist:cT { _@@_property_type_\l_tmpa_str :nn } {
                     \tl_put_right:Nx #1 {
                         \exp_args:NVV \@@_delegate_setter:nn
                             \l_tmpa_str \l_tmpa_tl
                     }
-                }{
-                % TODO: ERROR-no property with that name
                 }
-            }{
-            % TODO: ERROR-too many : or none at all
             }
         }
     }
@@ -615,7 +674,7 @@
             }
         }{
             \quark_if_no_value:nTF {#3} {
-                %% TODO: Error, one no-style value, ambiguous
+                %% One no-style value, ambiguous. Ignore the property
             }{
                 \@@_border_setter_isstyle:nTF {#3} {
                     \@@_delegate_setter:nn {#1-width} {#2}
@@ -676,6 +735,7 @@
     \@@_generic_setter:nnn \tl_to_str:n
 }{
     text-align,
+    vertical-align,
     math-mode,
 }
 %    \end{macrocode}
@@ -748,6 +808,17 @@
 
 \cs_new_protected:Nn \@@_parse_css:n {
     \@@_parse_css:w #1 \q_mark {\q_nil} \q_stop
+%    \end{macrocode}
+%
+%   Ensure the already seen specificities is in order without duplicates.
+%
+%    \begin{macrocode}
+    \seq_remove_duplicates:N \l_@@_specificities_seq
+    \seq_sort:Nn \l_@@_specificities_seq {
+        \int_compare:nNnTF { ##1 } > { ##2 }
+            { \sort_return_swapped: }
+            { \sort_return_same: }
+    }
 }
 %    \end{macrocode}
 %
@@ -761,7 +832,7 @@
     \quark_if_nil:nF {#2} {
         \@@_parse_properties:Nn \l_@@_parse_properties_tl {#2}
         \clist_map_inline:nn {#1} {
-            \@@_parse_css_addprops:n {##1}
+            \tl_if_empty:nF {##1} { \@@_parse_css_addprops:n {##1} }
         }
         \@@_parse_css:w #3 \q_stop
     }
@@ -777,16 +848,19 @@
 \tl_new:N \l_@@_current_selector_check_tl
 \cs_new_protected:Nn \@@_parse_css_addprops:n {
     \@@_parse_selector:n {#1}
-    \tl_set:Nx \l_tmpa_tl { l_@@_property_group_\l_@@_current_selector_tl _tl }
-    \tl_if_exist:cF { \l_tmpa_tl } { \tl_clear:c { \l_tmpa_tl } }
-    \tl_if_empty:NTF \l_@@_current_selector_check_tl {
-        \tl_put_right:cV { \l_tmpa_tl } \l_@@_parse_properties_tl
-    }{
-        \tl_put_right:cx { \l_tmpa_tl } {
-            \exp_not:N \bool_if:nT {
-                \exp_not:V \l_@@_current_selector_check_tl
-            }{
-                \exp_not:V \l_@@_parse_properties_tl
+    \tl_if_empty:NF \l_@@_current_selector_tl {
+        \tl_set:Nx \l_tmpa_tl
+                { l_@@_property_group_\l_@@_current_selector_tl _tl }
+        \tl_if_exist:cF { \l_tmpa_tl } { \tl_clear:c { \l_tmpa_tl } }
+        \tl_if_empty:NTF \l_@@_current_selector_check_tl {
+            \tl_put_right:cV { \l_tmpa_tl } \l_@@_parse_properties_tl
+        }{
+            \tl_put_right:cx { \l_tmpa_tl } {
+                \exp_not:N \bool_if:nT {
+                    \exp_not:V \l_@@_current_selector_check_tl
+                }{
+                    \exp_not:V \l_@@_parse_properties_tl
+                }
             }
         }
     }
@@ -822,12 +896,17 @@
 %    \end{macrocode}
 %
 %   Now that we can sanitize pseudo-classes, parsing the selector is safe. The
-%   only construct to protect is currently ":nth-child()".
+%   constructs to protect are ":nth-child()" and ":where()" since other supported
+%   pseudo-classes have no argument.
 %
 %    \begin{macrocode}
 \seq_new:N \l_@@_current_selector_seq
 \seq_new:N \l_@@_pseudoclasses_seq
 \tl_new:N \l_@@_current_element_tl
+\tl_new:N \l_@@_current_tableclass_tl
+\int_new:N \l_@@_current_level_int
+\int_new:N \l_@@_current_specificity_int
+\seq_new:N \l_@@_specificities_seq
 \cs_new_protected:Nn \@@_parse_selector:n {
     \tl_set:Nx \l_@@_current_selector_tl { \tl_to_str:n {#1} }
 %    \end{macrocode}
@@ -839,23 +918,141 @@
     \exp_args:NNV \tl_replace_all:Nnn
         \l_@@_current_selector_tl \c_colon_str {:}
     \@@_parse_selector_sanitize:n {nth-child}
+    \@@_parse_selector_sanitize:n {where}
     \seq_set_split:NnV \l_@@_current_selector_seq {~} \l_@@_current_selector_tl
     \tl_clear:N \l_@@_current_selector_tl
     \tl_clear:N \l_@@_current_selector_check_tl
+    \tl_clear:N \l_@@_current_tableclass_tl
+    \int_set:Nn \l_@@_current_level_int {-1}
+    \int_zero:N \l_@@_current_specificity_int
     \seq_map_inline:Nn \l_@@_current_selector_seq {
+        \tl_clear:N \l_@@_current_element_tl
+        \@@_parse_simple_selector:n {##1}
+    }
+%    \end{macrocode}
+%
+%   Remember the current selector as "<last element>~<specificity>".
+%
+%    \begin{macrocode}
+    \tl_if_empty:NF \l_@@_current_element_tl {
+        \tl_if_empty:NF \l_@@_current_tableclass_tl {
+            \tl_set:Nx \l_@@_current_tableclass_tl {
+                l_@@_active_classes_\l_@@_current_tableclass_tl _bool
+            }
+            \bool_if_exist:cF { \l_@@_current_tableclass_tl } {
+                \bool_new:c { \l_@@_current_tableclass_tl }
+            }
+            \@@_add_check:x {
+                \exp_not:n { \bool_if_p:N }
+                {
+                    \exp_not:c { \l_@@_current_tableclass_tl }
+                }
+            }
+        }
+        \tl_set:Nx \l_@@_current_selector_tl {
+            \exp_not:V \l_@@_current_element_tl
+            \exp_not:n {~}
+            \int_use:N \l_@@_current_specificity_int
+        }
+        \seq_put_right:Nx \l_@@_specificities_seq {
+            \int_use:N \l_@@_current_specificity_int
+        }
+    }
+}
+
+\tl_new:N \l_@@_maybe_element_tl
+
+\cs_new_protected:Nn \@@_parse_simple_selector:n {
+%    \end{macrocode}
+%
+%   First we replace "." with ":." so that class selectors can be handled with
+%   the same code as pseudo-classes. Then we split the current selector item on
+%   ":" to get the base element and the pseudo-classes.
+%
+%    \begin{macrocode}
+    \tl_set:Nn \l_tmpa_tl { #1 }
+    \tl_replace_all:Nnn \l_tmpa_tl {.} {:.}
+    \seq_set_split:NnV \l_@@_pseudoclasses_seq {:} \l_tmpa_tl
+    \seq_pop_left:NN \l_@@_pseudoclasses_seq \l_@@_maybe_element_tl
+%    \end{macrocode}
+%
+%   Known type selectors increase the specificity by one.
+%
+%    \begin{macrocode}
+    \str_case:VnTF \l_@@_maybe_element_tl {
+        { table } { \int_set:Nn \l_tmpa_int { 1 } }
+        { tr }    { \int_set:Nn \l_tmpa_int { 2 } }
+        { td }    { \int_set:Nn \l_tmpa_int { 3 } }
+        { p }     { \int_set:Nn \l_tmpa_int { 4 } }
+    }{
+        \int_add:Nn \l_@@_current_specificity_int { 1 }
+    }{
+%    \end{macrocode}
+%
+%   An empty type selector is accepted to account for ":where()" pseudo-classes.
+%   They will be handled in the loop below, but of course there should be a type
+%   selector eventually. Map that case to a big positive next level so that we can
+%   detect that and keep the current level as-is.
+%
+%   Unknown type selectors are mapped to big negative levels
+%   so that the descendant check will refuse them, unless we are at the very
+%   first type selector in which case we map the deprecated environment
+%   selector to a class selector.
+%
+%    \begin{macrocode}
+        \tl_if_empty:NTF \l_@@_maybe_element_tl {
+            \int_set:Nn \l_tmpa_int { 10 }
+        }{
+            \int_compare:nNnTF \l_@@_current_level_int = { -1 } {
+                \tl_set_eq:NN
+                    \l_@@_current_tableclass_tl
+                    \l_@@_maybe_element_tl
+                \tl_set:Nn \l_@@_maybe_element_tl { table }
+                \int_set:Nn \l_tmpa_int { 1 }
+                \int_add:Nn \l_@@_current_specificity_int { 11 }
+            }{
+                \int_set:Nn \l_tmpa_int { -10 }
+            }
+        }
+    }
+%    \end{macrocode}
+%
+%   If no type selector has been found yet, record the one we may have found
+%   now. Else, refuse to have two in the same selector.
+%
+%    \begin{macrocode}
+    \tl_if_empty:NTF \l_@@_current_element_tl {
+        \tl_set_eq:NN \l_@@_current_element_tl \l_@@_maybe_element_tl
+    }{
+        \tl_if_empty:NF \l_@@_maybe_element_tl {
+            \int_set:Nn \l_tmpa_int { -10 }
+        }
+    }
 %    \end{macrocode}
 %
-%   Split the current selector item on ":" to get the base element and the
-%   pseudo-classes.
+%   If the detected element is a descendent of the previous one, the selector
+%   can match; count the element selector specificity and parse its
+%   pseudo-classes. If not, the whole selector cannot match and we discard it
+%   early.
 %
 %    \begin{macrocode}
-        \seq_set_split:Nnn \l_@@_pseudoclasses_seq {:} {##1}
-        \seq_pop_left:NN \l_@@_pseudoclasses_seq \l_@@_current_element_tl
-        \tl_put_right:Nn \l_@@_current_selector_tl {~}
-        \tl_put_right:NV \l_@@_current_selector_tl \l_@@_current_element_tl
+    \int_compare:nNnTF \l_tmpa_int > \l_@@_current_level_int {
+        \int_compare:nNnT \l_tmpa_int < 10 {
+            \int_set_eq:NN \l_@@_current_level_int \l_tmpa_int
+        }
         \seq_map_inline:Nn \l_@@_pseudoclasses_seq {
-            \@@_parse_pseudoclass:w ####1{}\q_stop
+            \@@_parse_pseudoclass:w ##1{}\q_stop
         }
+    }{
+        \tl_clear:N \l_@@_current_element_tl
+    }
+%    \end{macrocode}
+%
+%   At the end of the selector parsing, the current element should not be empty.
+%
+%    \begin{macrocode}
+    \tl_if_empty:NT \l_@@_current_element_tl {
+        \seq_map_break:
     }
 }
 %    \end{macrocode}
@@ -864,11 +1061,25 @@
 %   brace (if any), and the second argument is the braced content (if any).
 %   The third argument gobbles any trailing garbage.
 %
+%   If the pseudo-class starts with a "." this is a class selector. Else, this
+%   is an unknown pseudo-class and we reject the complete selector.
+%
 %    \begin{macrocode}
 \NewDocumentCommand \@@_parse_pseudoclass:w { lmu{\q_stop} } {
-    \exp_args:Nx \str_case:nn { #1 } {
+    \str_case:nnF { #1 } {
         {first-child} { \@@_parse_selector_nth:n {1} }
-        {nth-child} { \@@_parse_selector_nth:n {#2} }
+        {nth-child}   { \@@_parse_selector_nth:n {#2} }
+        {where}       { \@@_parse_where:n {#2} }
+    }{
+        \str_if_eq:eeTF { \str_head:n { #1 } } {.} {
+            \tl_set:Nx \l_@@_current_tableclass_tl {
+                \str_tail:n { #1 }
+            }
+            \int_add:Nn \l_@@_current_specificity_int { 10 }
+        }{
+            \tl_clear:N \l_@@_current_element_tl
+            \seq_map_break:n { \seq_map_break: }
+        }
     }
 }
 
@@ -878,11 +1089,10 @@
 \cs_new_protected:Nn \@@_parse_selector_nth:n {
 %    \end{macrocode}
 %
-%   Put something in the selector token list, for specificity ordering
-%   of declarations.
+%   Count the pseudo-class in the specificity.
 %
 %    \begin{macrocode}
-    \tl_put_right:Nn \l_@@_current_selector_tl { :nth-child }
+    \int_add:Nn \l_@@_current_specificity_int { 10 }
 %    \end{macrocode}
 %
 %   Now parse the nth-child argument:
@@ -920,7 +1130,7 @@
 %   At last, generate the condition code.
 %
 %    \begin{macrocode}
-    \exp_args:NV \str_case:nn \l_@@_current_element_tl {
+    \str_case:Vn \l_@@_current_element_tl {
         {tr} { \@@_generate_check_nth:n {\g_@@_row_int} }
         {td} { \@@_generate_check_nth:n {\g_@@_col_int} }
     }
@@ -928,9 +1138,11 @@
 
 \cs_new_protected_nopar:Nn \@@_generate_check_nth:n {
     \int_compare:nNnTF \l_@@_nth_coeff_int = { 0 } {
-        \tl_set:Nx \l_tmpa_tl {
+        \@@_add_check:x {
             \exp_not:n { \int_compare_p:nNn #1 = }
-            \exp_not:V \l_@@_nth_offset_int
+            {
+                \exp_not:V \l_@@_nth_offset_int
+            }
         }
     }{
         \tl_set:Nx \l_tmpb_tl {
@@ -941,7 +1153,7 @@
                 \exp_not:V \l_@@_nth_coeff_int
             }
         }
-        \tl_set:Nx \l_tmpa_tl {
+        \@@_add_check:x {
             \exp_not:N \bool_lazy_and_p:nn {
                 \exp_not:n { \int_compare_p:nNn 0 = }
                 {
@@ -958,26 +1170,48 @@
             }
         }
     }
+}
+
+\cs_new_protected:Nn \@@_parse_where:n {
+    \use:x {
+        \exp_not:n {
+            \@@_parse_simple_selector:n { #1 }
+            \int_set:Nn \l_@@_current_specificity_int
+        }
+        {
+            \int_use:N \l_@@_current_specificity_int
+        }
+    }
+}
+
+\cs_new_protected:Nn \@@_add_check:n {
     \tl_if_empty:NTF \l_@@_current_selector_check_tl {
-        \tl_set_eq:NN \l_@@_current_selector_check_tl \l_tmpa_tl
+        \tl_set:Nn \l_@@_current_selector_check_tl { #1 }
     }{
         \tl_set:Nx \l_@@_current_selector_check_tl {
             \exp_not:N \bool_lazy_and_p:nn {
                 \exp_not:V \l_@@_current_selector_check_tl
             }{
-                \exp_not:V \l_tmpa_tl
+                \exp_not:n { #1 }
             }
         }
     }
 }
+\cs_generate_variant:Nn \@@_add_check:n {x}
 
-\cs_set_protected:Nn \@@_recall_properties:n {
-    \tl_if_exist:cT { l_@@_property_group_~#1_tl } {
-        \tl_use:c { l_@@_property_group_~#1_tl }
+\seq_new:N \l_@@_classes_seq
+\NewDocumentCommand \cellpropsclass { sm } {
+    \seq_set_split:Nnn \l_@@_classes_seq {~} { #2 }
+    \IfBooleanF {#1} {
+        \seq_put_right:Nn \l_@@_classes_seq { \@currenvir }
     }
-    \clist_map_inline:nn { \@currenvir } {
-        \tl_if_exist:cT { l_@@_property_group_~##1~#1_tl } {
-            \tl_use:c { l_@@_property_group_~##1~#1_tl }
+}
+\cellpropsclass{}
+
+\cs_set_protected:Nn \@@_recall_properties:n {
+    \seq_map_inline:Nn \l_@@_specificities_seq {
+        \tl_if_exist:cT { l_@@_property_group_#1~##1_tl } {
+            \tl_use:c { l_@@_property_group_#1~##1_tl }
         }
     }
 }
@@ -988,22 +1222,23 @@
 
 \ExplSyntaxOff
 \cellprops{
-    td {
+    :where(td) {
         padding: 0pt \csname l_@@_colsep_dim\endcsname;
         min-height: \csname l_@@_strut_ht_dim\endcsname;
         min-depth: \csname l_@@_strut_dp_dim\endcsname;
         min-width: 0pt;
         text-align: left;
+        vertical-align: baseline;
         math-mode: auto;
         color: inherit;
         background-color: transparent;
         border: thin none inherit;
     }
-    tr {
+    :where(tr) {
         color: inherit;
         background-color: transparent;
     }
-    table {
+    :where(table) {
         padding: 0pt; % No change at load time
         color: inherit;
         background-color: transparent;
@@ -1030,7 +1265,8 @@
 \tl_new:N  \l_@@_color_tl
 \tl_new:N  \l_@@_bgcolor_tl
 
-% To count rows and columns
+\seq_new:N \l_@@_classes_at_start_seq
+
 \cs_new_protected:Nn \@@_array_init: {
     \tl_set:Nx \l_@@_restore_tl {
         \bool_if:NTF \g_@@_inrow_bool {
@@ -1049,6 +1285,23 @@
         \exp_not:n { \tl_gset:Nn \g_@@_borders_tl }
             { \exp_not:V \g_@@_borders_tl }
     }
+%    \end{macrocode}
+%
+% Unset all previous active classes, and set current ones as active.
+%
+%    \begin{macrocode}
+    \seq_map_inline:Nn \l_@@_classes_at_start_seq {
+        \bool_set_false:c { l_@@_active_classes_##1_bool }
+    }
+    \seq_set_eq:NN \l_@@_classes_at_start_seq \l_@@_classes_seq
+    \seq_map_inline:Nn \l_@@_classes_at_start_seq {
+        \bool_set_true:c { l_@@_active_classes_##1_bool }
+    }
+%    \end{macrocode}
+%
+% To count rows and columns.
+%
+%    \begin{macrocode}
     \int_gzero:N \g_@@_row_int
     \bool_gset_false:N \g_@@_inrow_bool
     \tl_gclear:N \g_@@_borders_tl
@@ -1080,7 +1333,6 @@
     \dim_set_eq:NN \l_@@_tablepadding_bottom_dim \g_tmpb_dim
     \tl_set_eq:NN \l_@@_color_tl \g_tmpa_tl
     \tl_set_eq:NN \l_@@_bgcolor_tl \g_tmpb_tl
-    \@@_recall_properties:n {tr}
     \dim_set:Nn \l_@@_strut_ht_dim { \box_ht:N \@arstrutbox }
     \dim_set:Nn \l_@@_strut_dp_dim { \box_dp:N \@arstrutbox }
     \box_clear:N \@arstrutbox
@@ -1360,13 +1612,7 @@
             \global\@minipagefalse
             \everypar{}
         }
-        \@@_recall_properties:n {td~p}
-        \@@_recall_properties:n {tr~td~p}
-        \@@_recall_properties:n {tr:nth-child~p}
-        \@@_recall_properties:n {td:nth-child~p}
-        \@@_recall_properties:n {tr:nth-child~td~p}
-        \@@_recall_properties:n {tr~td:nth-child~p}
-        \@@_recall_properties:n {tr:nth-child~td:nth-child~p}
+        \@@_recall_properties:n {p}
     }
 }
 \cs_new_protected_nopar:Nn \@@_end_par_cell:n {
@@ -1381,14 +1627,9 @@
 
 \cs_new_protected_nopar:Nn \@@_begin_raw_cell:n {
     \group_begin:
-    \@@_recall_properties:n {tr:nth-child}
+    \@@_recall_properties:n {tr}
     \@@_update_colors:
     \@@_recall_properties:n {td}
-    \@@_recall_properties:n {tr~td}
-    \@@_recall_properties:n {td:nth-child}
-    \@@_recall_properties:n {tr:nth-child~td}
-    \@@_recall_properties:n {tr~td:nth-child}
-    \@@_recall_properties:n {tr:nth-child~td:nth-child}
     \@@_update_colors:
     % Additional init code
     #1
@@ -1476,12 +1717,50 @@
         \tl_if_empty:NF \g_@@_borders_tl { \exp_not:n {&} }
         \exp_not:n { \omit \kern \c_zero_dim }
     }
-    % Handle padding-top, min-height and border-top
-    \@@_get_border_info:n {top}
+    % Handle min-height, min-depth and vertical-align
+    % wrong values are treated as |baseline|.
     \box_set_ht:Nn \l_@@_cell_box {
         \dim_max:nn
             {\box_ht:N \l_@@_cell_box}
             {\@@_get_property:n {min-height}}
+    }
+    \box_set_dp:Nn \l_@@_cell_box {
+        \dim_max:nn
+            {\box_dp:N \l_@@_cell_box}
+            {\@@_get_property:n {min-depth}}
+    }
+    \str_case_e:nn {\@@_get_property:n {vertical-align}} {
+        { top } {
+            \hbox_set:Nn \l_@@_cell_box {
+                \vbox_top:n {
+                    \kern 0pt\relax
+                    \box_use_drop:N \l_@@_cell_box
+                }
+            }
+        }
+        { bottom } {
+            \hbox_set:Nn \l_@@_cell_box {
+                \vbox:n {
+                    \box_use_drop:N \l_@@_cell_box
+                    \kern 0pt\relax
+                }
+            }
+        }
+        { middle } {
+            \hbox_set:Nn \l_@@_cell_box {
+                \dim_set:Nn \l_tmpa_dim {
+                    (\box_dp:N \l_@@_cell_box
+                    - \box_ht:N \l_@@_cell_box
+                    + 1ex) / 2
+                }
+                \raisebox{\l_tmpa_dim}{\box_use_drop:N \l_@@_cell_box}
+            }
+        }
+    }
+    % Handle padding-top and border-top
+    \@@_get_border_info:n {top}
+    \box_set_ht:Nn \l_@@_cell_box {
+        \box_ht:N \l_@@_cell_box
         + (\@@_get_property:n {padding-top})
         + \l_@@_border_width_dim
     }
@@ -1497,12 +1776,10 @@
                 { \exp_not:V \l_@@_border_color_tl }
         }
     }
-    % Handle padding-bottom, min-depth and border-bottom
+    % Handle padding-bottom and border-bottom
     \@@_get_border_info:n {bottom}
     \box_set_dp:Nn \l_@@_cell_box {
-        \dim_max:nn
-            {\box_dp:N \l_@@_cell_box}
-            {\@@_get_property:n {min-depth}}
+        \box_dp:N \l_@@_cell_box
         + (\@@_get_property:n {padding-bottom})
         + \l_@@_border_width_dim
     }
-- 
cgit v1.2.3

