texlive[74569] Master/texmf-dist: tabularray (11mar25)

commits+karl at tug.org commits+karl at tug.org
Tue Mar 11 21:06:02 CET 2025


Revision: 74569
          https://tug.org/svn/texlive?view=revision&revision=74569
Author:   karl
Date:     2025-03-11 21:06:02 +0100 (Tue, 11 Mar 2025)
Log Message:
-----------
tabularray (11mar25)

Modified Paths:
--------------
    trunk/Master/texmf-dist/doc/latex/tabularray/tabularray.pdf
    trunk/Master/texmf-dist/doc/latex/tabularray/tabularray.tex
    trunk/Master/texmf-dist/tex/latex/tabularray/tabularray.sty

Added Paths:
-----------
    trunk/Master/texmf-dist/doc/latex/tabularray/CHANGELOG.md
    trunk/Master/texmf-dist/doc/latex/tabularray/README.md
    trunk/Master/texmf-dist/tex/latex/tabularray/tabularray-2024.sty

Removed Paths:
-------------
    trunk/Master/texmf-dist/doc/latex/tabularray/README.txt

Added: trunk/Master/texmf-dist/doc/latex/tabularray/CHANGELOG.md
===================================================================
--- trunk/Master/texmf-dist/doc/latex/tabularray/CHANGELOG.md	                        (rev 0)
+++ trunk/Master/texmf-dist/doc/latex/tabularray/CHANGELOG.md	2025-03-11 20:06:02 UTC (rev 74569)
@@ -0,0 +1,438 @@
+All notable changes to this package will be documented here.
+The format is based on [Keep a Changelog](https://keepachangelog.com).
+
+# [v2025A] - 2025-03-11
+
+## Added
+
+- Allow rollback to version 2024 by `\usepackage{tabularray}[=v2024]`
+- Load `tabularray` libraries in external files ([#532])
+- Evaluate inner specifications with `functional` library ([#270])
+- Add `tikz` library for drawing below or above tables ([#29], [#552])
+  - Create table node `table` for each table
+  - Create cell nodes `<i>-<j>` for each cell
+  - Create corner nodes `h<i>` for each hborder
+  - Create corner nodes `v<j>` for each vborder
+  - Create `tblrtikzbelow` and `tblrtikzabove` environments
+- Add `measure=vstore` option to `varwidth` library ([#549])
+- Add `\AddToTblrHook` and `\AddToTblrHookNext` commands ([#197])
+- Add `\DeclareTblrKeys` and `\SetTblrKeys` commands ([#547])
+- Add `\lTblrDefaultHruleWidthDim` and `\lTblrDefaultVruleWidthDim` ([#102], [#527])
+- Add `\lTblrDefaultHruleColorTl` and `\lTblrDefaultVruleColorTl` ([#172])
+- Add experimental public variable `\lTblrPortraitTypeTl` ([#29], [#197])
+- Add experimental public variable `\lTblrRowHeadInt` ([#29], [#197])
+- Add experimental public variable `\lTblrRowFootInt` ([#29], [#197])
+- Add experimental public variable `\lTblrTablePageInt` ([#29], [#197])
+- Add experimental public variable `\lTblrRowFirstInt` ([#29], [#197])
+- Add experimental public variable `\lTblrRowLastInt` ([#29], [#197])
+- Add experimental public variable `\lTblrCellBreakBool` ([#442])
+- Add `\NewTblrChildIndexer` for selecting a child index ([#578])
+- Allow mixing child indexes, indexers, and selectors ([#577])
+- Add support for selecting cells with two dimensional indexes ([#381])
+- Add support for two dimensional indexers and selectors ([#381])
+- Add `\lTblrChildIndexTl` for child indexers ([#578])
+- Add `\lTblrChildHtotalInt` and `\lTblrChildVtotalInt` for indexers and selectors ([#381])
+- Add `\SetChild` commands for setting ids and classes ([#381])
+- Add `\ExpTblrChildId` for expanding index from an id ([#381])
+- Add `\ExpTblrChildClass` for expanding indexes from a class ([#381])
+- Add child selector `every` for selecting indexes in an arithmetic sequence ([#576])
+- Add benchmark tests and publish results to `gh-pages` branch ([#480])
+- Add new chapter "Experimental Interfaces" in the manual
+- Document how to use color models with `functional` library ([#106])
+- Document that double blank lines around cells are not supported ([#282])
+- Document that math cells cannot include multiline math ([#491], [#492])
+
+## Changed
+
+- Support old TeX Live releases published within previous three years only
+- Use linked property lists to make `tabularray` run much faster ([#541])
+- Avoid using `l3regex` to make `tabularray` run much faster ([#553])
+- Recognize spaces between `\\` and `*` or `[<length>]` ([#526])
+- Keep braces when splitting table body to cells ([#501])
+- Keep braces when extracting table commands from cells ([#501])
+- Make `measure` setting apply to subtables in `varwidth` library
+- Set `measure=vbox` as default in `varwidth` library ([#540])
+- Make `hook` library depend on `varwidth` library ([#179])
+- Set `measure=vstore` as default in `hook` library ([#179])
+- Forbid page breaks in the middle of multirow cells ([#442])
+- Rename all key paths and add `tabularray` prefix to them ([#547])
+- Raise an error for using an undefined template in `\SetTblrTemplate` ([#517])
+- Replace `x`-type expansions with `e`-type expansions ([#560])
+- Optimize internal structure of child index lists ([#575])
+- Make local definitions for child selectors ([#381])
+- Make indexer `Z` accept an optional argument for a negative index ([#509], [#578])
+- Ensure the converted index of `U`/`V`/`W`/`X`/`Y`/`Z` is positive ([#385], [#578])
+- Load `ninecolors` with `package/xcolor/after` hook ([#490])
+- Rename `\l_tblr_childs_clist` as `\lTblrChildClist` ([#249], [#527])
+- Rename `\l_tblr_childs_total_tl` as `\lTblrChildTotalInt` ([#249], [#527])
+- Rename `\lTblrCellRowSpanTl` as `\lTblrCellRowSpanInt` ([#527])
+- Rename `\lTblrCellColSpanTl` as `\lTblrCellColSpanInt` ([#527])
+- Rename `\lTblrCellAboveBorderWidthTl` as `\lTblrCellAboveBorderWidthDim` ([#527])
+- Rename `\lTblrCellBelowBorderWidthTl` as `\lTblrCellBelowBorderWidthDim` ([#527])
+- Rename `\lTblrCellLeftBorderWidthTl` as `\lTblrCellLeftBorderWidthDim` ([#527])
+- Rename `\lTblrCellRightBorderWidthTl` as `\lTblrCellRightBorderWidthDim` ([#527])
+- Replace `\verb` commands with meaningful commands in the manual
+- Improve documentation for `booktabs` library ([#443])
+
+## Deprecated
+
+- Deprecate `\DefTblrTemplate` in favor of `\DeclareTblrTemplate` ([#563])
+- Deprecate `\NewColumnType` in favor of `\NewTblrColumnType` ([#421])
+- Deprecate `\NewRowType` in favor of `\NewTblrRowType` ([#421])
+- Deprecate `\NewColumnRowType` in favor of `\NewTblrColumnRowType` ([#421])
+- Deprecate `\NewTableCommand` in favor of `\NewTblrTableCommand` ([#421])
+- Deprecate `\NewContentCommand` in favor of `\NewTblrContentCommand` ([#421])
+- Deprecate `\NewDashStyle` in favor of `\NewTblrDashStyle` ([#421])
+- Deprecate `\NewChildSelector` in favor of `\NewTblrChildSelector` ([#579])
+- Deprecate `\tablewidth` in favor of `\lTblrTableWidthDim` ([#527])
+- Deprecate `\g_tblr_level_int` in favor of `\gTblrLevelInt` ([#527])
+
+## Fixed
+
+- Fix `bad register code` errors from huge tables ([#305])
+- Fix measuring phase of `\lTblrMeasuringBool` ([#179])
+- Fix expansion errors with border text ([#303])
+- Fix undeclared variables and inconsistent assignments ([#22])
+- Fix `X`-column errors from `\hfuzz=\maxdimen` setting ([#445])
+- Fix misspelled variable name of `\lTblrCellRightBorderColorTl` ([#476])
+- Fix missing `tblr` prefixes in some variable names ([#469])
+- Throw an error for an unknown inner key name ([#574])
+- Suppress "table-width-too-small" warning if table width is not set ([#497])
+
+## Removed
+
+- Drop support for TeX Live 2020 and 2021
+- Drop support for end index in `odd` and `even` selectors ([#580])
+- Remove deprecated inner key `verb` ([#530])
+- Remove undocumented `\rulewidth` ([#102], [#527])
+
+# [v2024A] - 2024-02-16
+
+## Added
+
+- Add experimental `hook` and `html` libraries (#197)
+- Expand multiple macros with outer key `expand` (#77)
+
+## Changed
+
+- Only run code of the same library once (#364)
+- Update rollback release 2021 to version 2021R
+
+## Fixed
+
+- Fix undefined props with latest l3kernel (#22, #474)
+- Fix incorrect `longtblr` pagebreaks before sectioning commands (#361)
+
+# [v2023A] - 2023-03-01
+
+## Added
+
+- Add experimental `\lTblrMeasuringBool` for measuring phase (#179)
+- Allow to evaluate all functions in the table body (#254)
+- Add `\empty` as a table command (#328)
+- Allow to define `\relax` as a table command (#328)
+- Extend from `X-Z` to `U-Z` for the last children (#353)
+- Add new chapter "Tips and Tricks" in manual
+
+## Deprecated
+
+- Mark inner key `verb` as obsolete for future removal (#104, #294, #358, #370)
+
+## Fixed
+
+- Fix vline positions when `belowpos=0` (#272)
+- Fix wrong `capcont` template when `label=none` (#335)
+
+# [v2022D] - 2022-11-01
+
+## Added
+
+- Add inner key `delimiter` and its `left` and `right` subkeys (#300)
+- Add `\tblrcontheadname` and `\tblrcontfootname` for `babel` translations (#167)
+- Allow setting multiple environments at the same time (#37)
+- Add `nameref` and `zref` libraries (#256)
+
+## Fixed
+
+- Fix wrong delimiter positions of delimited matrices in `amsmath` library (#300)
+- Fix a mistake in storing styles of elements (#286)
+
+# [v2022C] - 2022-07-01
+
+## Changed
+
+- Avoid adding struts before and after cell text (#265)
+- Make `\pagebreak` and `\nopagebreak` accept an optional argument (#259)
+- Restore table commands before building cells (#31)
+- Share `rownum` and `colnum` counters (#269, davidcarlisle/dpctex#33)
+- Replace TeXSE with TopAnswersTeX in support sites
+
+## Fixed
+
+- Fix wrong calculation of border sizes (#260)
+- Fix extra line breaks when cells begin or end with CJK punctuations (#145, #257)
+
+# [v2022B] - 2022-06-01
+
+**It is in Overleaf installation of [TeX Live 2022](https://www.overleaf.com/blog/tex-live-2022-now-available).**
+
+## Added
+
+- Provide `functional` library for doing functional programming in tables (#81)
+  + Add outer specification `evaluate` for file inputting and table building
+  + Add inner specification `process` for cell summing and conditional styling
+  + Add `\cellGetText`, `\cellSetText`, `\cellSetStyle`, `\rowSetStyle`, `\columnSetStyle`
+- Add `guard` option in `siunitx` library (#175)
+
+## Fixed
+
+- Make `tabularray` correctly nest in `align` environment (#143)
+- Fix wrong page breaks of long tables with positive `\parskip` (#203)
+- Fix missing first and last hlines in subtables (#227)
+
+# [v2022A] - 2022-03-01
+
+## Added
+
+- Allow rollback to version 2021 by `\usepackage{tabularray}[=v2021]` (#28)
+- Add `longtabs` and `talltabs` environments in `booktabs` library (#170)
+- Add `stretch=-1` option for removing vertical space around lists (#99)
+- Add an optional argument to `odd` and `even` selectors (#105)
+- Add new interfaces `hborder` and `vborder` (#183)
+- Add `pagebreak=yes/no/auto` options to `hborder` (#165)
+- Add `baseline` key for aligning table at any row (#72)
+- Update change log on wiki page of the project (#95)
+
+## Changed
+
+- Make justifying the default in table cells (#69)
+- Make justifying the default in table head/foot (#174)
+- Make \TblrNote overlap to the right side by default (#198)
+- Restore paragraph parameters in table head/foot (#181)
+- Don't add `headsep`/`footsep` if table head/foot is empty (#86)
+- Normalize `\NewSomeThing` and `\DefSomeThing` commands (#115)
+- Rename `README` as `README.txt` and add `CONTRIBUTING.txt` (#178)
+
+## Fixed
+
+- Fix multiline nonnumeric cells in `siunitx`'s `S` columns (#90)
+- Fix wrong cell widths when using `font=\boldmath` option (#137)
+- Fix `@` expression in math mode (#138)
+- Fix vlines that cross double hlines caused by span cells (#141)
+- Fix wrong counters in math mode for `counter` library (#154)
+- Fix wrong cell widths caused by `counter` library (#162)
+- Fix wrong widths of enumerate cells caused by `hyperref` (#196)
+
+## Removed
+
+- Remove `\firsthline` and `\lasthline` commands (#72)
+- Remove `\multicolumn` and `\multirow` commands (#28)
+
+# [v2021Q] - 2021-12-01
+
+## Added
+
+- Add missing `\therownum`, `\thecolnum`, `\therowcount`, `\thecolcount` (#129)
+
+## Changed
+
+- Use `\thetable` in `caption-lot` template (#126)
+
+## Fixed
+
+- Fix missing last row (#114)
+
+# [v2021P] - 2021-10-01
+
+## Added
+
+- Add `counter` library for manipulating LaTeX counters (#89)
+- Add `mode` option for cells/columns/rows (#45)
+- Allow to modify `fg` settings (#62)
+- Allow to modify `cmd` settings (#57)
+- Define `+array`, `+cases`, `+pmatrix`, etc with `amsmath` library (#87)
+- Update `endpos` and `expand` options in the manual (#92, #110)
+
+## Changed
+
+- Use `\tablename` in `caption-tag` templates (#103)
+
+## Fixed
+
+- Fix lost lines in some long tables in PDF readers (#88)
+- Fix an error caused by `verb` option (#112)
+
+# [v2021N] - 2021-09-01
+
+## Added
+
+- Add crossing and trimming options for hlines and vlines (#55)
+- Add trim options for `\cmidrule` command (#41)
+- Add vertical spaces below and above booktabs rules (#79)
+- Add `\morecmidrules`, `\specialrule` and `\addlinespace` for booktabs
+- Add `varwidth` library for using enumerate inside cell text (#36)
+- Add simple template for caption and capcont (#70)
+
+## Changed
+
+- Change pdfborder setting only inside `tblr` environment (#33)
+- Remove `\par` tokens at the beginning and at the end of cell text (#32)
+
+## Fixed
+
+- Fix bug in including `\linewidth` graphics in `X` columns (#80)
+- Fix disappeared borders around colored cells in Adobe Acrobat (#76)
+- Fix wrong span widths with `hspan=minimal` option (#71)
+- Fix bug with tabularray and shortverb `|` (#58)
+- Fix bug with extra space at the end of a multiline cell (#48)
+- Fix blank page in long tables when using `\newpage` command (#39)
+- Fix blank page after `\section` starts at the top of a page (#42)
+
+# [v2021M] - 2021-08-01
+
+## Added
+
+- Expand every occurrence of the specified macro once (#18, #23)
+- Provide some table commands (T-F-S/csvsimple#12)
+- Use `X`, `Y`, `Z` for the last three childs in child selectors (#24)
+- Support `\verb` command via `verb` option (#13)
+- Implement `s` column for `siunitx` library
+- Implement `\\*`, `\nopagebreak` and `\pagebreak` (#30)
+- Define `empty` template for every elements
+- Implement `\caption[]{text}` and `\caption*{text}` (#25)
+- Implement floatable tall tables (#26)
+
+## Fixed
+
+- Fix empty table pages with only one row (#38)
+
+# [v2021L] - 2021-07-01
+
+**It is in Overleaf installation of [TeX Live 2021](https://www.overleaf.com/blog/tex-live-2021-now-available).**
+
+## Added
+
+- Add full support for long tables with `longtblr` environment
+- Add templates for caption and continuation text and table footnotes
+- Add templates `firsthead`, `middlehead`, `lasthead`, `firstfoot`, `middlefoot`, `lastfoot` (#12)
+- Add `\DefTblrTemplate`, `\SetTblrTemplate`, `\UseTblrTemplate` and `\ExpTblrTemplate`
+- Add `\SetTblrStyle` for setting color, font, alignment, parindent and hangindent
+- Add `\NewTblrTheme` command for defining new themes for long tables
+- Add `\SetTblrOuter` command for setting outer specifications
+- Add `\NewTblrEnviron` command for defining new `tblr` environments
+- Add `\UseTblrLibrary` command and `booktabs`, `diagbox`, `siunitx` libraries
+- Provide `\leftsep`, `\rightsep`, `\abovesep` and `\belowsep` for cell text
+- Add options for distributing span sizes evenly to columns or rows (#3)
+- Add `preto`/`appto`/`cmd` options for cells/rows/columns (#18)
+
+## Changed
+
+- Replace `\SetTblrDefault` with `\SetTblrInner` for setting inner specifications
+- Update the manual and add three chapters
+
+## Fixed
+
+- Keep row height and column widths even if all cells in them are spanned (#15)
+- Fix font option for multiline cells (#17)
+- Fix problem with `\relax` after `\\` or `\hline` (#20)
+
+# [v2021K] - 2021-06-05
+
+## Added
+
+- Allow to change `rulesep`
+- Add `fg` and `font` options
+- Include old version as `tabularray-2021.sty`
+
+## Changed
+
+- Make it run much faster by using less property lists
+
+## Fixed
+
+- Fix child selectors with more than nine childs
+
+# [v2021J] - 2021-05-25
+
+## Added
+
+- Document `\SetTblrTracing` command
+
+## Changed
+
+- Compatible with TeX Live 2020
+
+## Fixed
+
+- Avoid "need 0.0pt more!" warnings
+
+# [v2021H] - 2021-05-13
+
+## Added
+
+- First public release
+
+[#22]: https://github.com/lvjr/tabularray/issues/22
+[#29]: https://github.com/lvjr/tabularray/issues/29
+[#102]: https://github.com/lvjr/tabularray/issues/102
+[#106]: https://github.com/lvjr/tabularray/issues/106
+[#172]: https://github.com/lvjr/tabularray/issues/172
+[#179]: https://github.com/lvjr/tabularray/issues/179
+[#197]: https://github.com/lvjr/tabularray/issues/197
+[#249]: https://github.com/lvjr/tabularray/issues/249
+[#270]: https://github.com/lvjr/tabularray/issues/270
+[#282]: https://github.com/lvjr/tabularray/issues/282
+[#303]: https://github.com/lvjr/tabularray/issues/303
+[#305]: https://github.com/lvjr/tabularray/issues/305
+[#381]: https://github.com/lvjr/tabularray/issues/381
+[#385]: https://github.com/lvjr/tabularray/pull/385
+[#421]: https://github.com/lvjr/tabularray/issues/421
+[#442]: https://github.com/lvjr/tabularray/issues/442
+[#443]: https://github.com/lvjr/tabularray/pull/443
+[#445]: https://github.com/lvjr/tabularray/issues/445
+[#469]: https://github.com/lvjr/tabularray/issues/469
+[#476]: https://github.com/lvjr/tabularray/issues/476
+[#480]: https://github.com/lvjr/tabularray/issues/480
+[#490]: https://github.com/lvjr/tabularray/issues/490
+[#491]: https://github.com/lvjr/tabularray/issues/491
+[#492]: https://github.com/lvjr/tabularray/issues/492
+[#497]: https://github.com/lvjr/tabularray/pull/497
+[#501]: https://github.com/lvjr/tabularray/issues/501
+[#509]: https://github.com/lvjr/tabularray/pull/509
+[#517]: https://github.com/lvjr/tabularray/pull/517
+[#526]: https://github.com/lvjr/tabularray/issues/526
+[#527]: https://github.com/lvjr/tabularray/issues/527
+[#530]: https://github.com/lvjr/tabularray/issues/530
+[#532]: https://github.com/lvjr/tabularray/issues/532
+[#540]: https://github.com/lvjr/tabularray/issues/540
+[#541]: https://github.com/lvjr/tabularray/issues/541
+[#547]: https://github.com/lvjr/tabularray/issues/547
+[#549]: https://github.com/lvjr/tabularray/issues/549
+[#552]: https://github.com/lvjr/tabularray/issues/552
+[#553]: https://github.com/lvjr/tabularray/issues/553
+[#560]: https://github.com/lvjr/tabularray/issues/560
+[#563]: https://github.com/lvjr/tabularray/issues/563
+[#574]: https://github.com/lvjr/tabularray/issues/574
+[#575]: https://github.com/lvjr/tabularray/issues/575
+[#576]: https://github.com/lvjr/tabularray/issues/576
+[#577]: https://github.com/lvjr/tabularray/issues/577
+[#578]: https://github.com/lvjr/tabularray/issues/578
+[#579]: https://github.com/lvjr/tabularray/issues/579
+[#580]: https://github.com/lvjr/tabularray/issues/580
+
+[Unreleased]: https://github.com/lvjr/tabularray/compare/2024A...HEAD
+[v2025A]: https://github.com/lvjr/tabularray/compare/2024A...2025A
+[v2024A]: https://github.com/lvjr/tabularray/compare/2023A...2024A
+[v2023A]: https://github.com/lvjr/tabularray/compare/2022D...2023A
+[v2022D]: https://github.com/lvjr/tabularray/compare/2022C...2022D
+[v2022C]: https://github.com/lvjr/tabularray/compare/2022B...2022C
+[v2022B]: https://github.com/lvjr/tabularray/compare/2022A...2022B
+[v2022A]: https://github.com/lvjr/tabularray/compare/2021Q...2022A
+[v2021Q]: https://github.com/lvjr/tabularray/compare/2021P...2021Q
+[v2021P]: https://github.com/lvjr/tabularray/compare/2021N...2021P
+[v2021N]: https://github.com/lvjr/tabularray/compare/2021M...2021N
+[v2021M]: https://github.com/lvjr/tabularray/compare/2021L...2021M
+[v2021L]: https://github.com/lvjr/tabularray/compare/2021K...2021L
+[v2021K]: https://github.com/lvjr/tabularray/compare/2021J...2021K
+[v2021J]: https://github.com/lvjr/tabularray/compare/2021H...2021J
+[v2021H]: https://github.com/lvjr/tabularray/compare/2021G...2021H


Property changes on: trunk/Master/texmf-dist/doc/latex/tabularray/CHANGELOG.md
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/doc/latex/tabularray/README.md
===================================================================
--- trunk/Master/texmf-dist/doc/latex/tabularray/README.md	                        (rev 0)
+++ trunk/Master/texmf-dist/doc/latex/tabularray/README.md	2025-03-11 20:06:02 UTC (rev 74569)
@@ -0,0 +1,43 @@
+# The LaTeX3 package `tabularray`
+
+```
+Caption     : Typeset tabulars and arrays with LaTeX3
+Author      : Jianrui Lyu <tolvjr at 163.com>
+Collaborator: Yukai Chou <muzimuzhi at gmail.com>
+CTAN Page   : https://ctan.org/pkg/tabularray
+Repository  : https://github.com/lvjr/tabularray
+Repository  : https://bitbucket.org/lvjr/tabularray
+License     : The LaTeX Project Public License 1.3c
+```
+
+## 1\. Introduction
+
+LaTeX tables are implemented using TeX commands such as `\halign`, `\noalign`, `\span`, and `\omit`.
+In order to implement new features, many macro packages have modified the inner table commands inside LaTeX.
+This makes package code complicated, difficult to maintain, and often conflicts with each other.
+
+At present, the LaTeX3 programming layer is basically mature.
+This `tabularray` package will discard the old `\halign` commands and directly use LaTeX3 functions to parse the table,
+and then typeset the entire table.
+Under the premise of being compatible with the basic syntax of LaTeX2 tables,
+this macro package will completely separate the contents and styles of the table,
+and the styles of the table can be completely set in key-value way.
+
+## 2\. Contributing
+
+To make the upcoming releases more stable, you are very welcome to test the latest package file in the repository.
+To test it, you only need to download [`tabularray.sty`](https://github.com/lvjr/tabularray/raw/main/tabularray.sty)
+and put it into the folder of your TeX documents.
+
+If you want to make a contribution to `tabularray` package, you can get necessary informations from [CONTRIBUTING.md](https://github.com/lvjr/tabularray/blob/main/CONTRIBUTING.md) file.
+The following are all contributors to the package:
+
+  - [dbitouze](https://github.com/lvjr/tabularray/commits?author=dbitouze) (Denis Bitouzé)
+  - [IsaacOscar](https://github.com/lvjr/tabularray/commits?author=IsaacOscar) (Isaac Oscar Gariano)
+  - [jasperhabicht](https://github.com/lvjr/tabularray/commits?author=jasperhabicht) (Jasper Habicht)
+  - [krlmlr](https://github.com/lvjr/tabularray/commits?author=krlmlr) (Kirill Müller)
+  - [mbertucci47](https://github.com/lvjr/tabularray/commits?author=mbertucci47) (Matthew Bertucci)
+  - [pkl97](https://github.com/lvjr/tabularray/commits?author=pkl97)
+  - [qinglee](https://github.com/lvjr/tabularray/commits?author=qinglee) (Qing Lee)
+  - [SainoNamkho](https://github.com/lvjr/tabularray/commits?author=SainoNamkho)
+  - [Yodude2002](https://github.com/lvjr/tabularray/commits?author=Yodude2002)


Property changes on: trunk/Master/texmf-dist/doc/latex/tabularray/README.md
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Deleted: trunk/Master/texmf-dist/doc/latex/tabularray/README.txt
===================================================================
--- trunk/Master/texmf-dist/doc/latex/tabularray/README.txt	2025-03-11 20:05:45 UTC (rev 74568)
+++ trunk/Master/texmf-dist/doc/latex/tabularray/README.txt	2025-03-11 20:06:02 UTC (rev 74569)
@@ -1,6 +0,0 @@
-Package: Typeset tabulars and arrays with LaTeX3
-Copyright: 2021-2024 (c) Jianrui Lyu <tolvjr at 163.com>
-CTAN: https://ctan.org/pkg/tabularray
-Repository: https://github.com/lvjr/tabularray
-Repository: https://bitbucket.org/lvjr/tabularray
-License: The LaTeX Project Public License 1.3c

Modified: trunk/Master/texmf-dist/doc/latex/tabularray/tabularray.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/tabularray/tabularray.tex
===================================================================
--- trunk/Master/texmf-dist/doc/latex/tabularray/tabularray.tex	2025-03-11 20:05:45 UTC (rev 74568)
+++ trunk/Master/texmf-dist/doc/latex/tabularray/tabularray.tex	2025-03-11 20:06:02 UTC (rev 74569)
@@ -1,7 +1,7 @@
 % -*- coding: utf-8 -*-
 % !TEX program = lualatex
 \documentclass[oneside]{book}
-\newcommand*{\myversion}{2024A}
+\newcommand*{\myversion}{2025A}
 \newcommand*{\mylpad}[1]{\ifnum#1<10 0\the#1\else\the#1\fi}
 
 \usepackage[a4paper,margin=2.5cm]{geometry}
@@ -9,8 +9,23 @@
 \setlength{\parindent}{0pt}
 \setlength{\parskip}{4pt plus 1pt minus 1pt}
 
+\makeatletter
+\ExplSyntaxOn
+\NewDocumentCommand\MyDebugSingle{m}{
+  \@ifpackageloaded{docmute}{#1}{
+    \str_if_eq:onTF{\jobname}{tabularray}{#1}{
+      \sys_ensure_backend:
+      \debug_on:n{check-declarations}
+      #1
+      \debug_off:n{check-declarations}
+    }
+  }
+}
+\ExplSyntaxOff
+\makeatother
+\MyDebugSingle{\usepackage{tabularray}}
+
 \usepackage{codehigh} % https://ctan.org/pkg/codehigh
-\usepackage{tabularray}
 \usepackage{array,multirow,amsmath}
 \usepackage{chemmacros,environ}
 \usepackage{enumitem}
@@ -18,7 +33,10 @@
 \usepackage[firstpage=true]{background}
 \backgroundsetup{contents={}}
 
-\UseTblrLibrary{amsmath,booktabs,counter,diagbox,functional,siunitx,varwidth}
+\UseTblrLibrary{
+  amsmath,booktabs,counter,diagbox,functional,siunitx,tikz,varwidth
+}
+\usetikzlibrary{patterns}
 
 \usepackage{hyperref}
 \hypersetup{
@@ -35,15 +53,34 @@
 
 \setcounter{tocdepth}{1}
 
-\newcommand*{\K}[1]{\texttt{#1}}
-\newcommand*{\V}[1]{\texttt{#1}}
-\newcommand*{\None}{$\times$}
+\NewDocumentCommand\None{}{{\boldmath$\times$}}
+\NewDocumentCommand\K{m}{\texttt{#1}} % key
+\NewDocumentCommand\V{m}{\texttt{#1}} % value
+\NewDocumentCommand\Q{m}{\texttt{#1}} % column/row type
 
+\NewDocumentCommand\KK{m}{\texttt{\fakeverb{#1}}} % key
+\NewDocumentCommand\VV{m}{\texttt{\fakeverb{#1}}} % value
+\NewDocumentCommand\KV{m}{\texttt{\fakeverb{#1}}} % key and value
+\NewDocumentCommand\CC{m}{\texttt{\fakeverb{#1}}} % command
+\NewDocumentCommand\EE{m}{\texttt{\fakeverb{#1}}} % environment
+\NewDocumentCommand\LL{m}{\texttt{\fakeverb{#1}}} % library
+\NewDocumentCommand\PP{m}{\texttt{\fakeverb{#1}}} % package
+\NewDocumentCommand\FF{m}{\texttt{\fakeverb{#1}}} % file
+\NewDocumentCommand\NN{m}{\texttt{\fakeverb{#1}}} % tikz node
+\NewDocumentCommand\KP{m}{\texttt{\fakeverb{#1}}} % key path
+\NewDocumentCommand\HP{m}{\texttt{\fakeverb{#1}}} % hook path
+\NewDocumentCommand\CI{m}{\texttt{\fakeverb{#1}}} % child indexer
+\NewDocumentCommand\CS{m}{\texttt{\fakeverb{#1}}} % child selector
+\NewDocumentCommand\CO{m}{\texttt{\fakeverb{#1}}} % counter
+\NewDocumentCommand\EN{m}{\texttt{\fakeverb{#1}}} % element name
+\NewDocumentCommand\TN{m}{\texttt{\fakeverb{#1}}} % template name
+\NewDocumentCommand\TT{m}{\texttt{\fakeverb{#1}}} % text
+
 \NewTblrEnviron{newtblr}
 \SetTblrOuter[newtblr]{long}
 \SetTblrInner[newtblr]{
-  hlines = {white}, column{1,2} = {co=1}, colsep = 5pt,
-  row{odd} = {brown8}, row{even} = {gray8},
+  hlines = {gray3}, column{1,2} = {co=1}, colsep = 5pt,
+  row{2-Z} = {brown8},
   row{1} = {fg=white, bg=purple2, font=\bfseries\sffamily},
 }
 
@@ -50,19 +87,18 @@
 \NewTblrEnviron{spectblr}
 \SetTblrOuter[spectblr]{long}
 \SetTblrInner[spectblr]{
-  hlines = {white}, column{2} = {co=1}, colsep = 5pt,
-  row{odd} = {brown8}, row{even} = {gray8},
+  hlines = {gray3}, column{2} = {co=1}, colsep = 5pt,
+  row{2-Z} = {brown8},
   row{1} = {fg=white, bg=purple2, font=\bfseries\sffamily},
   rowhead = 1,
 }
 
+\renewcommand\emph[1]{\textit{\color{red3}#1}}
+
 \newcommand{\mywarning}[1]{%
   \begin{tcolorbox}
-  The interfaces in this #1 should be seen as
-  \textcolor{red3}{\bfseries experimental}
-  and are likely to change in future releases, if necessary.
-  Don’t use them in important documents.
-  \end{tcolorbox}
+  #1
+  \end{tcolorbox}%
 }
 
 %\renewcommand*{\thefootnote}{*}
@@ -156,18 +192,18 @@
 
 \chapter{Overview of Features}
 
-Before using \verb!tabularray! package, it is better to know how to typeset simple text and
-math tables with traditional \verb!tabular!, \verb!tabularx! and \verb!array! environments,
-because we will compare \verb!tblr! environment from \verb!tabularray! package with these
+Before using \PP{tabularray} package, it is better to know how to typeset simple text and
+math tables with traditional \EE{tabular}, \EE{tabularx} and \EE{array} environments,
+because we will compare \EE{tblr} environment from \PP{tabularray} package with these
 environments. You may read web pages on LaTeX tables on
 \href{https://www.learnlatex.org/en/lesson-08}{LearnLaTeX} and
 \href{https://www.overleaf.com/learn/latex/Tables}{Overleaf} first.
 
-\section{Vertical Space}
+\section{Vertical space}
 
-After loading \verb!tabularray! package in the preamble,
-we can use \verb!tblr! environments to typeset tabulars and arrays.
-The name \verb!tblr! is short for \verb!tabularray! or \verb!top-bottom-left-right!.
+After loading \PP{tabularray} package in the preamble,
+we can use \EE{tblr} environments to typeset tabulars and arrays.
+The name \EE{tblr} is short for \PP{tabularray} or \VV{top-bottom-left-right}.
 The following is our first example:
 
 \begin{demo}
@@ -194,9 +230,9 @@
 \end{tblr}
 \end{demohigh}
 
-You may notice that there is extra space above and below the table rows with \verb!tblr! environment.
+You may notice that there is extra space above and below the table rows with \EE{tblr} environment.
 This space makes the table look better.
-If you don't like it, you could use \verb!\SetTblrInner! command:
+If you don't like it, you could use \CC{\SetTblrInner} command:
 
 \begin{demohigh}
 \SetTblrInner{rowsep=0pt}
@@ -209,9 +245,9 @@
  Iota    & Kappa & Lambda & Mu    \\
 \hline
 \end{tblr}
-\end{demohigh} 
+\end{demohigh}
 
-But in many cases, this \verb!rowsep! is useful:
+But in many cases, this \KK{rowsep} is useful:
 
 \begin{demo}
 $\begin{array}{rrr}
@@ -233,12 +269,12 @@
 \end{tblr}$
 \end{demohigh}
 
-Note that you can use \verb!tblr! in both text and math modes.
+Note that you can use \EE{tblr} in both text and math modes.
 
-\section{Multiline Cells}
+\section{Multiline cells}
 
-It's quite easy to write multiline cells without fixing the column width in \verb!tblr! environments:
-just enclose the cell text with braces and use \verb!\\! to break lines:
+It's quite easy to write multiline cells without fixing the column width in \EE{tblr} environments:
+just enclose the cell text with braces and use \CC{\\\\} to break lines:
 
 \begin{demohigh}
 \begin{tblr}{|l|c|r|}
@@ -248,15 +284,15 @@
  {L \\ Left} & {C \\ Cent \\ Center} & R \\
 \hline
 \end{tblr}
-\end{demohigh} 
+\end{demohigh}
 
-\section{Cell Alignment}
+\section{Cell alignment}
 
 From time to time,
 you may want to specify the horizontal and vertical alignment of cells at the same time.
-\verb!Tabularray! package provides a \verb!Q! column for this
-(In fact, \verb!Q! column is the only primitive column,
-other columns are defined as \verb!Q! columns with some options):
+\PP{Tabularray} package provides a \Q{Q} column for this
+(In fact, \Q{Q} column is the only primitive column,
+other columns are defined as \Q{Q} columns with some options):
 
 \begin{demohigh}
 \begin{tblr}{|Q[l,t]|Q[c,m]|Q[r,b]|}
@@ -264,12 +300,12 @@
  {Top Baseline \\ Left Left} & Middle Center & {Right Right \\ Bottom Baseline} \\
 \hline
 \end{tblr}
-\end{demohigh} 
+\end{demohigh}
 
-Note that you can use more meaningful \verb!t! instead of \verb!p! for top baseline alignment.
+Note that you can use more meaningful \K{t} instead of \K{p} for top baseline alignment.
 For some users who are familiar with word processors,
-these \verb!t! and \verb!b! columns are counter-intuitive.
-In \verb!tabularray! package, there are another two column types \verb!h! and \verb!f!,
+these \K{t} and \K{b} columns are counter-intuitive.
+In \PP{tabularray} package, there are another two column types \K{h} and \K{f},
 which will align cell text at the head and the foot, respectively:
 
 \begin{demohigh}
@@ -282,10 +318,10 @@
 \end{tblr}
 \end{demohigh}
 
-\section{Multirow Cells}
+\section{Multirow cells}
 
-The above \verb!h! and \verb!f! alignments are necessary
-when we write multirow cells with \verb!\SetCell! command in \verb!tabularray!.
+The above \K{h} and \K{f} alignments are necessary
+when we write multirow cells with \CC{\SetCell} command in \PP{tabularray}.
 
 \begin{demo}
 \begin{tabular}{|l|l|l|l|}
@@ -311,9 +347,9 @@
 \end{tblr}
 \end{demohigh}
 
-Note that you don't need to load \verb!multirow! package first,
-since \verb!tabularray! doesn't depend on it.
-Furthermore, \verb!tabularray! will always typeset decent multirow cells.
+Note that you don't need to load \PP{multirow} package first,
+since \PP{tabularray} doesn't depend on it.
+Furthermore, \PP{tabularray} will always typeset decent multirow cells.
 First, it will set correct vertical middle alignment,
 even though some rows have large height:
 
@@ -363,9 +399,9 @@
 \end{demohigh}
 
 If you want to distribute extra vertical space evenly to two rows,
-you may use \verb!vspan! option described in Chapter \ref{chap:extra}.
+you may use \K{vspan} option described in Chapter \ref{chap:extra}.
 
-\section{Multi Rows and Columns}
+\section{Multi rows and columns}
 
 It was a hard job to typeset cells with multiple rows and multiple columns. For example:
 
@@ -383,9 +419,9 @@
 \end{tabular}
 \end{demo}
 
-With \verb!tabularray! package, you can set spanned cells with \verb!\SetCell! command:
-within the optional argument of \verb!\SetCell! command,
-option \verb!r! is for rowspan number, and \verb!c! for colspan number;
+With \PP{tabularray} package, you can set spanned cells with \CC{\SetCell} command:
+within the optional argument of \CC{\SetCell} command,
+option \K{r} is for rowspan number, and \K{c} for colspan number;
 within the mandatory argument of it, horizontal and vertical alignment options are accepted.
 Therefore it's much simpler to typeset spanned cells:
 
@@ -403,13 +439,13 @@
 \end{tblr}
 \end{demohigh}
 
-Using \verb!\multicolumn! command, the omitted cells \textcolor{red3}{must} be removed.
+Using \CC{\multicolumn} command, the omitted cells \emph{must} be removed.
 On the contrary,
-using \verb!\multirow! command, the omitted cells \textcolor{red3}{must not} be removed.
-\verb!\SetCell! command behaves the same as \verb!\multirow! command in this aspect.
+using \CC{\multirow} command, the omitted cells \emph{must not} be removed.
+\CC{\SetCell} command behaves the same as \CC{\multirow} command in this aspect.
 
-With \verb!tblr! environment, any \verb!\hline! segments inside a spanned cell will be ignored,
-therefore we're free to use \verb!\hline! in the above example.
+With \EE{tblr} environment, any \CC{\hline} segments inside a spanned cell will be ignored,
+therefore we're free to use \CC{\hline} in the above example.
 Also, any omitted cell will definitely be ignored when typesetting,
 no matter it's empty or not.
 With this feature, we could put row and column numbers into the omitted cells,
@@ -418,7 +454,7 @@
 \begin{demohigh}
 \begin{tblr}{|ll|c|rr|}
 \hline
- \SetCell[r=3,c=2]{h} r=3 c=2 & 1-2 & \SetCell[r=2,c=3]{r} r=2 c=3 & 1-4 & 1-5 \\ 
+ \SetCell[r=3,c=2]{h} r=3 c=2 & 1-2 & \SetCell[r=2,c=3]{r} r=2 c=3 & 1-4 & 1-5 \\
  2-1 & 2-2 & 2-3 & 2-4 & 2-5 \\
 \hline
  3-1 & 3-2 & MIDDLE & \SetCell[r=3,c=2]{f} r=3 c=2 & 3-5 \\
@@ -429,11 +465,11 @@
 \end{tblr}
 \end{demohigh}
 
-\section{Column Types}
+\section{Column types}
 
-\verb!Tabularray! package supports all normal column types, as well as
-the extendable \verb!X! column type,
-which first occurred in \verb!tabularx! package and was largely improved by \verb!tabu! package:
+\PP{Tabularray} package supports all normal column types, as well as
+the extendable \Q{X} column type,
+which first occurred in \PP{tabularx} package and was largely improved by \PP{tabu} package:
 
 \begin{demohigh}
 \begin{tblr}{|X[2,l]|X[3,l]|X[1,r]|X[r]|}
@@ -443,7 +479,7 @@
 \end{tblr}
 \end{demohigh}
 
-Also, \verb!X! columns with negative coefficients are possible:
+Also, \Q{X} columns with negative coefficients are possible:
 
 \begin{demohigh}
 \begin{tblr}{|X[2,l]|X[3,l]|X[-1,r]|X[r]|}
@@ -453,9 +489,9 @@
 \end{tblr}
 \end{demohigh}
 
-We need the width to typeset a table with \verb!X! columns.
-If unset, the default is \verb!\linewidth!.
-To change the width, we have to first put all column specifications into \verb!colspec={...}!:
+We need the width to typeset a table with \Q{X} columns.
+If unset, the default is \CC{\linewidth}.
+To change the width, we have to first put all column specifications into \KV{colspec={...}}:
 
 \begin{demohigh}
 \begin{tblr}{width=0.8\linewidth,colspec={|X[2,l]|X[3,l]|X[-1,r]|X[r]|}}
@@ -465,18 +501,18 @@
 \end{tblr}
 \end{demohigh}
 
-You can define new column types with \verb!\NewColumnType! command.
-For example, in \verb!tabularray! package,
-\verb!b! and \verb!X! columns are defined as special \verb!Q! columns:
+You can define new column types with \CC{\NewTblrColumnType} command.
+For example, in \PP{tabularray} package,
+\Q{b} and \Q{X} columns are defined as special \Q{Q} columns:
 \begin{codehigh}
-\NewColumnType{b}[1]{Q[b,wd=#1]}
-\NewColumnType{X}[1][]{Q[co=1,#1]}
+\NewTblrColumnType{b}[1]{Q[b,wd=#1]}
+\NewTblrColumnType{X}[1][]{Q[co=1,#1]}
 \end{codehigh}
 
-\section{Row Types}
+\section{Row types}
 
-Now that we have column types and \verb!colspec! option,
-you may ask for row types and \verb!rowspec! option.
+Now that we have column types and \K{colspec} option,
+you may ask for row types and \K{rowspec} option.
 Yes, they are here:
 
 \begin{demohigh}
@@ -487,15 +523,15 @@
 \end{tblr}
 \end{demohigh}
 
-Same as column types, \verb!Q! is the only primitive row type,
-and other row types are defined as \verb!Q! types with different options.
-It's better to specify horizontal alignment in \verb!colspec!,
-and vertical alignment in \verb!rowspec!, respectively.
+Same as column types, \Q{Q} is the only primitive row type,
+and other row types are defined as \Q{Q} types with different options.
+It's better to specify horizontal alignment in \K{colspec},
+and vertical alignment in \K{rowspec}, respectively.
 
-Inside \verb!rowspec!, \verb!|! is the hline type.
-Therefore we need not to write \verb!\hline! command, which makes table code cleaner.
+Inside \K{rowspec}, \Q{|} is the hline type.
+Therefore we need not to write \CC{\hline} command, which makes table code cleaner.
 
-\section{Hlines and Vlines}
+\section{Hlines and vlines}
 
 Hlines and vlines have been improved too. You can specify the widths and styles of them:
 
@@ -511,12 +547,12 @@
 \end{tblr}
 \end{demohigh}
 
-\section{Colorful Tables}
+\section{Colorful tables}
 
-To add colors to your tables, you need to load \verb!xcolor! package first.
-\verb!Tabularray! package will also load
+To add colors to your tables, you need to load \PP{xcolor} package first.
+\PP{Tabularray} package will also load
 \href{https://ctan.org/pkg/ninecolors}{\texttt{ninecolors}} package for proper color contrast.
-First you can specify background option for \verb!Q! rows/columns inside \verb!rowspec!/\verb!colspec!:
+First you can specify background option for \Q{Q} rows/columns inside \K{rowspec}/\K{colspec}:
 
 \begin{demohigh}
 \begin{tblr}{colspec={lcr},rowspec={|Q[cyan7]|Q[azure7]|Q[blue7]|}}
@@ -534,7 +570,7 @@
 \end{tblr}
 \end{demohigh}
 
-Also you can use \verb!\SetRow! or \verb!\SetColumn! command to specify row or column colors:
+Also you can use \CC{\SetRow} or \CC{\SetColumn} command to specify row or column colors:
 
 \begin{demohigh}
 \begin{tblr}{colspec={lcr},rowspec={|Q|Q|Q|}}
@@ -576,14 +612,14 @@
 \chapter{Basic Interfaces}
 \label{chap:basic}
 
-\section{Old and New Interfaces}
+\section{Old and new interfaces}
 
-With \verb!tabularray! package, you can change the styles of tables via old interfaces or new interfaces.
+With \PP{tabularray} package, you can change the styles of tables via old interfaces or new interfaces.
 
 The old interfaces consist of some table commands inside the table contents.
-Same as \verb!tabular! and \verb!array! environments,
-all table commands \textcolor{red3}{must} be put at the beginning of the cell text.
-Also, new table commands \textcolor{red3}{must} be defined with \verb!\NewTableCommand!.
+Same as \EE{tabular} and \EE{array} environments,
+all table commands \emph{must} be put at the beginning of the cell text.
+Also, new table commands \emph{must} be defined with \CC{\NewTblrTableCommand}.
 
 The new interfaces consist of some options inside the mandatory argument,
 hence totally separating the styles and the contents of tables.
@@ -591,23 +627,23 @@
 \begin{newtblr}[
   caption = {Old Interfaces and New Interfaces},
   label = {key:interface},
-]{verb}
-  Old Interfaces                                 & New Interfaces          \\
-  \verb!\SetHlines!                              & \K{hlines}              \\
-  \verb!\SetHline!, \verb!\hline!, \verb!\hborder!, \verb!\cline!
-                                                 & \K{hline}, \K{hborder}, \K{rowspec}  \\
-  \verb!\SetVlines!                              & \K{vlines}              \\
-  \verb!\SetVline!, \verb!\vline!, \verb!\vborder!, \verb!\rline!
-                                                 & \K{vline}, \K{vborder}, \K{colspec}  \\
-  \verb!\SetCells!                               & \K{cells}               \\
-  \verb!\SetCell!                                & \K{cell}                \\
-  \verb!\SetRows!                                & \K{rows}                \\
-  \verb!\SetRow!                                 & \K{row}, \K{rowspec}    \\
-  \verb!\SetColumns!                             & \K{columns}             \\
-  \verb!\SetColumn!                              & \K{column}, \K{colspec} \\
+]{}
+  Old Interfaces                               & New Interfaces          \\
+  \CC{\SetHlines}                              & \K{hlines}              \\
+  \CC{\SetHline}, \CC{\hline}, \CC{\hborder}, \CC{\cline}
+                                               & \K{hline}, \K{hborder}, \K{rowspec}  \\
+  \CC{\SetVlines}                              & \K{vlines}              \\
+  \CC{\SetVline}, \CC{\vline}, \CC{\vborder}, \CC{\rline}
+                                               & \K{vline}, \K{vborder}, \K{colspec}  \\
+  \CC{\SetCells}                               & \K{cells}               \\
+  \CC{\SetCell}                                & \K{cell}                \\
+  \CC{\SetRows}                                & \K{rows}                \\
+  \CC{\SetRow}                                 & \K{row}, \K{rowspec}    \\
+  \CC{\SetColumns}                             & \K{columns}             \\
+  \CC{\SetColumn}                              & \K{column}, \K{colspec} \\
 \end{newtblr}
 
-\section{Hlines and Vlines}
+\section{Hlines and vlines}\label{sec:hlines-vlines}
 
 All available keys for hlines and vlines are described in Table \ref{key:hline} and Table \ref{key:vline}.
 
@@ -623,6 +659,9 @@
   \underline{\K{fg}}   & rule color name & \None \\
   \K{leftpos}          & crossing or trimming position at the left side  & \V{1} \\
   \K{rightpos}         & crossing or trimming position at the right side & \V{1} \\
+  \K{l}                & same as \K{leftpos}, default \V{-0.8} & \V{1} \\
+  \K{r}                & same as \K{rightpos}, default \V{-0.8} & \V{1} \\
+  \K{lr}               & crossing or trimming positions at both sides, default \V{-0.8} & \V{1} \\
   \K{endpos}           & adjust leftpos/rightpos for only the leftmost/rightmost column & \V{false} \\
 \end{spectblr}
 \vspace{-2em}
@@ -640,9 +679,9 @@
   \K{belowpos}         & crossing or trimming position at the below side & \V{0} \\
 \end{spectblr}
 
-\subsection{Hlines and Vlines in New Interfaces}
+\subsection{Hlines and vlines in new interfaces}
 
-Options \verb!hlines! and \verb!vlines! are for setting all hlines and vlines, respectively.
+Options \K{hlines} and \K{vlines} are for setting all hlines and vlines, respectively.
 With empty value, all hlines/vlines will be solid.
 
 \begin{demohigh}
@@ -680,9 +719,7 @@
 \end{tblr}
 \end{demohigh}
 
-The above example can be simplified with \verb!odd! and \verb!even! values.
-(More child selectors can be defined with \verb!\NewChildSelector! command.
-Advanced users could read the source code for this.)
+The above example can be simplified with \CS{odd} and \CS{even} values.
 
 \begin{demohigh}
 \begin{tblr}{
@@ -697,7 +734,7 @@
 \end{tblr}
 \end{demohigh}
 
-Another pair of braces before will draw more hlines/vlines (in which \verb!-! stands for all line segments).
+Another pair of braces before will draw more hlines/vlines (in which \V{-} stands for all line segments).
 
 \begin{demohigh}
 \begin{tblr}{
@@ -709,10 +746,10 @@
 \end{tblr}
 \end{demohigh}
 
-Note that you \underline{must} use indexes in order: first 1, then 2, etc.
+Note that you \emph{must} use indexes in order: first 1, then 2, etc.
 
-Options \verb!hline{i}! and \verb!vline{j}! are for setting some hlines and vlines, respectively.
-Their values are the same as options \verb!hlines! and \verb!vlines!:
+Options \KK{hline{i}} and \KK{vline{j}} are for setting some hlines and vlines, respectively.
+Their values are the same as options \K{hlines} and \K{vlines}:
 
 \begin{demohigh}
 \begin{tblr}{
@@ -729,7 +766,7 @@
 \end{tblr}
 \end{demohigh}
 
-You can use \verb!U!, \verb!V!, \verb!W!, \verb!X!, \verb!Y!, \verb!Z! to
+You can use \V{U}, \V{V}, \V{W}, \V{X}, \V{Y}, \V{Z} to
 denote the last six children, respectively.
 It is especially useful when you are writing long tables:
 
@@ -748,7 +785,7 @@
 \end{tblr}
 \end{demohigh}
 
-Now we show the usage of \verb!text! key by the following example%
+Now we show the usage of \K{text} key by the following example%
 \footnote{Code from \url{https://tex.stackexchange.com/questions/603023/tabularray-and-tabularx-column-separator}.}:
 
 \begin{demohigh}
@@ -766,11 +803,11 @@
 \end{tblr}
 \end{demohigh}
 
-You need to load \verb!chemmacros! package for the \verb!\ch! command.
+You need to load \PP{chemmacros} package for the \CC{\ch} command.
 
-The \verb!leftpos! and \verb!rightpos! keys specify crossing or trimming positions for hlines.
-The possible values for them are decimal numbers between \verb!-1! and \verb!1!.
-Their initial values are \verb!1!.
+The \K{leftpos} and \K{rightpos} keys specify crossing or trimming positions for hlines.
+The possible values for them are decimal numbers between \V{-1} and \V{1}.
+Their initial values are \V{1}.
 
 \begin{center}
 \begin{tblr}{width=0.5\textwidth,colspec={rX[l]},hlines}
@@ -780,8 +817,8 @@
 \end{tblr}
 \end{center}
 
-The \verb!abovepos! and \verb!belowpos! keys for vlines have similar meanings.
-But their initial values are \verb!0!.
+The \K{abovepos} and \K{belowpos} keys for vlines have similar meanings.
+But their initial values are \V{0}.
 
 \begin{center}
 \begin{tblr}{width=0.5\textwidth,colspec={rX[l]},hlines}
@@ -807,7 +844,7 @@
 \end{tblr}
 \end{demohigh}
 
-There is also an \verb!endpos! option for adjusting \verb!leftpos!/\verb!rightpos!
+There is also an \K{endpos} option for adjusting \K{leftpos}/\K{rightpos}
 for only the leftmost/rightmost column:
 
 \begin{demohigh}
@@ -823,9 +860,9 @@
 \end{tblr}
 \end{demohigh}
 
-\subsection{Hlines and Vlines in Old Interfaces}
+\subsection{Hlines and vlines in old interfaces}
 
-The \verb!\hline! command has an optional argument which accepts key-value options.
+The \CC{\hline} command has an optional argument which accepts key-value options.
 The available keys are described in Table \ref{key:hline}.
 
 \begin{demohigh}
@@ -840,7 +877,7 @@
 \end{tblr}
 \end{demohigh}
 
-The \verb!\cline! command also has an optional argument which is the same as \verb!\hline!.
+The \CC{\cline} command also has an optional argument which is the same as \CC{\hline}.
 
 \begin{demohigh}
 \begin{tblr}{llll}
@@ -854,7 +891,7 @@
 \end{tblr}
 \end{demohigh}
 
-You can use child selectors in the mandatory argument of \verb!\cline!.
+You can use child selectors in the mandatory argument of \CC{\cline}.
 
 \begin{demohigh}
 \begin{tblr}{llll}
@@ -868,8 +905,8 @@
 \end{tblr}
 \end{demohigh}
 
-Commands \verb!\SetHline! combines the usages of \verb!\hline! and \verb!\cline!:
- 
+Commands \CC{\SetHline} combines the usages of \CC{\hline} and \CC{\cline}:
+
 \begin{demohigh}
 \begin{tblr}{llll}
 \SetHline{1-3}{blue5,1pt}
@@ -892,21 +929,21 @@
 \end{tblr}
 \end{demohigh}
 
-In fact, table command \verb!\SetHline[<index>]{<columns>}{<styles>}! at the beginning of row \verb!i!
-is the same as table option \verb!hline{i}={<index>}{<columns>}{<styles>}!.
+In fact, table command \CC{\SetHline[<index>]{<columns>}{<styles>}} at the beginning of row \V{i}
+is the same as table option \KV{hline{i}={<index>}{<columns>}{<styles>}}.
 
-Also, table command \verb!\SetHlines[<index>]{<columns>}{<styles>}! at the beginning of some row
-is the same as table option \verb!hlines={<index>}{<columns>}{<styles>}!.
+Also, table command \CC{\SetHlines[<index>]{<columns>}{<styles>}} at the beginning of some row
+is the same as table option \KV{hlines={<index>}{<columns>}{<styles>}}.
 
-The usages of table commands \verb!\vline!, \verb!\rline!, \verb!\SetVline!, \verb!\SetVlines!
-are similar to those of \verb!\hline!, \verb!\cline!, \verb!\SetHline!, \verb!\SetHlines!, respectively.
+The usages of table commands \CC{\vline}, \CC{\rline}, \CC{\SetVline}, \CC{\SetVlines}
+are similar to those of \CC{\hline}, \CC{\cline}, \CC{\SetHline}, \CC{\SetHlines}, respectively.
 But normally you don't need to use them.
 
-\section{Hborders and Vborders}
+\section{Hborders and vborders}
 
-Options \verb!hborder{i}! and \verb!vborder{j}! are similar to \verb!hline{i}! and \verb!vline{j}!,
+Options \KK{hborder{i}} and \KK{vborder{j}} are similar to \KK{hline{i}} and \KK{vline{j}},
 respectively, but they hold border specifications not related to one specific hline and vline.
-All available keys for \verb!hborder{i}! and \verb!vborder{j}! are described in
+All available keys for \KK{hborder{i}} and \KK{vborder{j}} are described in
 Table~\ref{key:hborder} and Table~\ref{key:vborder}.
 
 \begin{spectblr}[
@@ -933,12 +970,12 @@
   \K{rightspace+} & increase \V{leftsep} of current column  & \None \\
 \end{spectblr}
 
-Furthermore, table command \verb!\hborder{<specs>}! at the beginning of row \verb!i!
-is the same as table option \verb!hborder{i}={<specs>}!,
-and table command \verb!\vborder{<specs>}! at the beginning of column \verb!j!
-is the same as table option \verb!vborder{j}={<specs>}!.
+Furthermore, table command \CC{\hborder{<specs>}} at the beginning of row \V{i}
+is the same as table option \KV{hborder{i}={<specs>}},
+and table command \CC{\vborder{<specs>}} at the beginning of column \V{j}
+is the same as table option \KV{vborder{j}={<specs>}}.
 
-\section{Cells and Spancells}
+\section{Cells and spancells}
 
 All available keys for cells are described in Table \ref{key:cell} and Table \ref{key:cellspan}.
 
@@ -946,7 +983,7 @@
   caption = {Keys for the Content of Cells},
   label = {key:cell},
   remark{Note} = {In most cases, you can omit the underlined key names and write only their values.}
-]{verb}
+]{}
   Key & Description and Values & Initial Value \\
   \underline{\K{halign}}
     & horizontal alignment: \V{l} (left), \V{c} (center), \V{r} (right) or \V{j} (justify)
@@ -960,8 +997,8 @@
   \K{fg}    & foreground color name & \None \\
   \K{font}  & font commands & \None \\
   \K{mode}  & set cell mode: \V{math}, \V{imath}, \V{dmath} or \V{text} & \None \\
-  \verb!$!  & same as \V{mode=math} & \None \\
-  \verb!$$! & same as \V{mode=dmath} & \None \\
+  \KK{$}  & same as \V{mode=math} & \None \\
+  \KK{$$} & same as \V{mode=dmath} & \None \\
   \K{cmd}   & execute command for the cell text & \None \\
   \K{preto} & prepend text to the cell & \None \\
   \K{appto} & append text to the cell & \None \\
@@ -976,9 +1013,9 @@
   \K{c} & number of columns the cell spans & 1 \\
 \end{spectblr}
 
-\subsection{Cells and Spancells in New Interfaces}
+\subsection{Cells and spancells in new interfaces}
 
-Option \verb!cells! is for setting all cells.
+Option \K{cells} is for setting all cells.
 \nopagebreak
 \begin{demohigh}
 \begin{tblr}{hlines={white},cells={c,blue7}}
@@ -989,8 +1026,8 @@
 \end{tblr}
 \end{demohigh}
 
-Option \verb!cell{i}{j}! is for setting some cells,
-where \verb!i! stands for the row numbers and \verb!j! stands for the column numbers.
+Option \KK{cell{i}{j}} is for setting some cells,
+where \V{i} stands for the row numbers and \V{j} stands for the column numbers.
 \nopagebreak
 \begin{demohigh}
 \begin{tblr}{
@@ -1019,9 +1056,27 @@
 \end{tblr}
 \end{demohigh}
 
-\subsection{Cells and Spancells in Old Interfaces}
+From version 2025A, you can select cells with a list of two dimensional indexes:
+\begin{demohigh}
+\begin{tblr}{
+  cell{{2}{6},{7}{3}} = {bg=blue7},
+  cell{{1}{1}-{4}{4},{5}{8}-{8}{5}} = {bg=red7}
+}
+  1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 \\
+  2 & 2 & 3 & 4 & 5 & 6 & 7 & 8 \\
+  3 & 2 & 3 & 4 & 5 & 6 & 7 & 8 \\
+  4 & 2 & 3 & 4 & 5 & 6 & 7 & 8 \\
+  5 & 2 & 3 & 4 & 5 & 6 & 7 & 8 \\
+  6 & 2 & 3 & 4 & 5 & 6 & 7 & 8 \\
+  7 & 2 & 3 & 4 & 5 & 6 & 7 & 8 \\
+  8 & 2 & 3 & 4 & 5 & 6 & 7 & 8
+\end{tblr}
+\end{demohigh}
+In this example, \TT{-} characters are used for diagonal selection.
 
-The \verb!\SetCell! command has a mandatory argument for setting the styles of current cell.
+\subsection{Cells and spancells in old interfaces}
+
+The \CC{\SetCell} command has a mandatory argument for setting the styles of current cell.
 The available keys are described in Table \ref{key:cell}.
 
 \begin{demohigh}
@@ -1036,7 +1091,7 @@
 \end{tblr}
 \end{demohigh}
 
-The \verb!\SetCell! command also has an optional argument for setting the multispan of current cell.
+The \CC{\SetCell} command also has an optional argument for setting the multispan of current cell.
 The available keys are described in Table \ref{key:cellspan}.
 
 \begin{demohigh}
@@ -1069,13 +1124,13 @@
 \end{tblr}
 \end{demohigh}
 
-In fact, table command \verb!\SetCell[<span>]{<styles>}! at the beginning of cell at row \verb!i!
-and column \verb!j! is the same as table option \verb!cell{i}{j}={<span>}{<styles>}!.
+In fact, table command \CC{\SetCell[<span>]{<styles>}} at the beginning of cell at row \V{i}
+and column \V{j} is the same as table option \KV{cell{i}{j}={<span>}{<styles>}}.
 
-Also, table command \verb!\SetCells[<span>]{<styles>}! at the beginning of some cell
-is the same as table option \verb!cells={<span>}{<styles>}!.
+Also, table command \CC{\SetCells[<span>]{<styles>}} at the beginning of some cell
+is the same as table option \KV{cells={<span>}{<styles>}}.
 
-\section{Rows and Columns}
+\section{Rows and columns}
 
 All available keys for rows and columns are described in Table \ref{key:row} and Table \ref{key:column}.
 
@@ -1083,7 +1138,7 @@
   caption = {Keys for Rows},
   label = {key:row},
   remark{Note} = {In most cases, you can omit the underlined key names and write only their values.}
-]{verb}
+]{}
   Key & Description and Values & Initial Value \\
   \underline{\K{halign}}
     & horizontal alignment: \V{l} (left), \V{c} (center), \V{r} (right) or \V{j} (justify)
@@ -1097,8 +1152,8 @@
   \K{fg} & foreground color name & \None \\
   \K{font} & font commands & \None \\
   \K{mode}  & set mode for row cells: \V{math}, \V{imath}, \V{dmath} or \V{text} & \None \\
-  \verb!$!  & same as \V{mode=math} & \None \\
-  \verb!$$! & same as \V{mode=dmath} & \None \\
+  \KK{$}  & same as \V{mode=math} & \None \\
+  \KK{$$} & same as \V{mode=dmath} & \None \\
   \K{cmd}   & execute command for every cell text & \None \\
   \K{abovesep} & set vertical space above the row & \V{2pt} \\
   \K{abovesep+} & increase vertical space above the row & \None \\
@@ -1114,7 +1169,7 @@
   caption = {Keys for Columns},
   label = {key:column},
   remark{Note} = {In most cases, you can omit the underlined key names and write only their values.}
-]{verb}
+]{}
   Key & Description and Values & Initial Value \\
   \underline{\K{halign}}
     & horizontal alignment: \V{l} (left), \V{c} (center), \V{r} (right) or \V{j} (justify)
@@ -1129,8 +1184,8 @@
   \K{fg} & foreground color name & \None \\
   \K{font} & font commands & \None \\
   \K{mode}  & set mode for column cells: \V{math}, \V{imath}, \V{dmath} or \V{text} & \None \\
-  \verb!$!  & same as \V{mode=math} & \None \\
-  \verb!$$! & same as \V{mode=dmath} & \None \\
+  \KK{$}  & same as \V{mode=math} & \None \\
+  \KK{$$} & same as \V{mode=dmath} & \None \\
   \K{cmd}   & execute command for every cell text & \None \\
   \K{leftsep} & set horizontal space to the left of the column & \V{6pt} \\
   \K{leftsep+} & increase horizontal space to the left of the column & \None \\
@@ -1142,9 +1197,9 @@
   \K{appto} & append text to every cell (like \V{<} specifier in \K{colspec}) & \None \\
 \end{spectblr}
 
-\subsection{Rows and Columns in New Interfaces}
+\subsection{Rows and columns in new interfaces}
 
-Options \verb!rows! and \verb!columns! are for setting all rows and columns, respectively.
+Options \K{rows} and \K{columns} are for setting all rows and columns, respectively.
 \nopagebreak
 \begin{demohigh}
 \begin{tblr}{
@@ -1157,7 +1212,7 @@
 \end{tblr}
 \end{demohigh}
 
-Options \verb!row{i}! and \verb!column{j}! are for setting some rows and columns, respectively.
+Options \KK{row{i}} and \KK{column{j}} are for setting some rows and columns, respectively.
 
 \begin{demohigh}
 \begin{tblr}{
@@ -1175,7 +1230,7 @@
 \end{tblr}
 \end{demohigh}
 
-The following example demonstrates the usages of \verb!bg!, \verb!fg! and \verb!font! keys:
+The following example demonstrates the usages of \K{bg}, \K{fg} and \K{font} keys:
 \nopagebreak
 \begin{demohigh}
 \begin{tblr}{
@@ -1190,7 +1245,7 @@
 \end{tblr}
 \end{demohigh}
 
-The following example demonstrates the usages of \verb!mode! key:
+The following example demonstrates the usages of \K{mode} key:
 \nopagebreak
 \begin{demohigh}
 $\begin{tblr}{
@@ -1204,9 +1259,11 @@
 \hline
 \end{tblr}$
 \end{demohigh}
+Note that you \emph{can not} write multiline math directly (such as \TT{\alpha\ \\\\ \beta})
+in any math-mode cell.
 
 The following example demonstrates the usages of
-\verb!abovesep!, \verb!belowsep!, \verb!leftsep!, \verb!rightsep! keys:
+\K{abovesep}, \K{belowsep}, \K{leftsep}, \K{rightsep} keys:
 %\nopagebreak
 \begin{demohigh}
 \begin{tblr}{
@@ -1220,7 +1277,7 @@
 \end{tblr}
 \end{demohigh}
 
-The following example shows that we can replace \verb!\\[dimen]! with \verb!belowsep+! key.
+The following example shows that we can replace \CC{\\\\[dimen]} with \K{belowsep+} key.
 
 \begin{demohigh}
 \begin{tblr}{
@@ -1232,9 +1289,9 @@
 \end{tblr}
 \end{demohigh}
 
-\subsection{Rows and Columns in Old Interfaces}
+\subsection{Rows and columns in old interfaces}
 
-The \verb!\SetRow! command has a mandatory argument for setting the styles of current row.
+The \CC{\SetRow} command has a mandatory argument for setting the styles of current row.
 The available keys are described in Table \ref{key:row}.
 
 \begin{demohigh}
@@ -1249,25 +1306,25 @@
 \end{tblr}
 \end{demohigh}
 
-In fact, table command \verb!\SetRow{<styles>}! at the beginning of row \verb!i!
-is the same as table option \verb!row{i}={<styles>}!.
+In fact, table command \CC{\SetRow{<styles>}} at the beginning of row \V{i}
+is the same as table option \KV{row{i}={<styles>}}.
 
-Also, table command \verb!\SetRows{<styles>}! at the beginning of some row
-is the same as table option \verb!rows={<styles>}!.
+Also, table command \CC{\SetRows{<styles>}} at the beginning of some row
+is the same as table option \KV{rows={<styles>}}.
 
-The usages of table commands \verb!\SetColumn! and \verb!\SetColumns!
-are similar to those of \verb!\SetRow! and \verb!\SetRows!, respectively.
+The usages of table commands \CC{\SetColumn} and \CC{\SetColumns}
+are similar to those of \CC{\SetRow} and \CC{\SetRows}, respectively.
 But normally you don't need to use them.
 
-\section{Colspec and Rowspec}
+\section{Colspec and rowspec}
 
-Options \verb!colspec!/\verb!rowspec! are for setting column/row specifications
+Options \K{colspec}/\K{rowspec} are for setting column/row specifications
 with column/row type specifiers.
 
-\subsection{Colspec and Width}
+\subsection{Colspec and width}
 
-Option \verb!width! is for setting the width of the table with extendable columns.
-The following example demonstrates the usage of \verb!width! option.
+Option \K{width} is for setting the width of the table with extendable columns.
+The following example demonstrates the usage of \K{width} option.
 \nopagebreak
 \begin{demohigh}
 \begin{tblr}{width=0.8\textwidth, colspec={|l|X[2]|X[3]|X[-1]|}}
@@ -1277,8 +1334,8 @@
 \end{tblr}
 \end{demohigh}
 
-You can omit \verb!colspec! name if it is the only key you use inside the mandatory argument.
-The following example demonstrates the usages of \verb!$! and \verb!$$! keys:
+You can omit \K{colspec} name if it is the only key you use inside the mandatory argument.
+The following example demonstrates the usages of \KK{$} and \KK{$$} keys:
 \nopagebreak
 \begin{demohigh}
 \begin{tblr}{Q[l]Q[r,$]Q[r,$$]}
@@ -1290,21 +1347,21 @@
 \end{tblr}
 \end{demohigh}
 
-\subsection{Column Types}
+\subsection{Column types}
 
-The \verb!tabularray! package has only one type of primitive column: the \verb!Q! column.
-Other types of columns are defined as \verb!Q! columns with some keys.
+The \PP{tabularray} package has only one type of primitive column: the \Q{Q} column.
+Other types of columns are defined as \Q{Q} columns with some keys.
 
 \begin{codehigh}
-\NewColumnType{l}{Q[l]}
-\NewColumnType{c}{Q[c]}
-\NewColumnType{r}{Q[r]}
-\NewColumnType{t}[1]{Q[t,wd=#1]}
-\NewColumnType{m}[1]{Q[m,wd=#1]}
-\NewColumnType{b}[1]{Q[b,wd=#1]}
-\NewColumnType{h}[1]{Q[h,wd=#1]}
-\NewColumnType{f}[1]{Q[f,wd=#1]}
-\NewColumnType{X}[1][]{Q[co=1,#1]}
+\NewTblrColumnType{l}{Q[l]}
+\NewTblrColumnType{c}{Q[c]}
+\NewTblrColumnType{r}{Q[r]}
+\NewTblrColumnType{t}[1]{Q[t,wd=#1]}
+\NewTblrColumnType{m}[1]{Q[m,wd=#1]}
+\NewTblrColumnType{b}[1]{Q[b,wd=#1]}
+\NewTblrColumnType{h}[1]{Q[h,wd=#1]}
+\NewTblrColumnType{f}[1]{Q[f,wd=#1]}
+\NewTblrColumnType{X}[1][]{Q[co=1,#1]}
 \end{codehigh}
 
 \begin{demohigh}
@@ -1315,23 +1372,23 @@
 \end{tblr}
 \end{demohigh}
 
-Any new column type must be defined with \verb!\NewColumnType! command.
+Any new column type must be defined with \CC{\NewTblrColumnType} command.
 It can have an optional argument when it's defined.
 
-\subsection{Row Types}
+\subsection{Row types}
 
-The \verb!tabularray! package has only one type of primitive row: the \verb!Q! row.
-Other types of rows are defined as \verb!Q! rows with some keys.
+The \PP{tabularray} package has only one type of primitive row: the \Q{Q} row.
+Other types of rows are defined as \Q{Q} rows with some keys.
 
 \begin{codehigh}
-\NewRowType{l}{Q[l]}
-\NewRowType{c}{Q[c]}
-\NewRowType{r}{Q[r]}
-\NewRowType{t}[1]{Q[t,ht=#1]}
-\NewRowType{m}[1]{Q[m,ht=#1]}
-\NewRowType{b}[1]{Q[b,ht=#1]}
-\NewRowType{h}[1]{Q[h,ht=#1]}
-\NewRowType{f}[1]{Q[f,ht=#1]}
+\NewTblrRowType{l}{Q[l]}
+\NewTblrRowType{c}{Q[c]}
+\NewTblrRowType{r}{Q[r]}
+\NewTblrRowType{t}[1]{Q[t,ht=#1]}
+\NewTblrRowType{m}[1]{Q[m,ht=#1]}
+\NewTblrRowType{b}[1]{Q[b,ht=#1]}
+\NewTblrRowType{h}[1]{Q[h,ht=#1]}
+\NewTblrRowType{f}[1]{Q[f,ht=#1]}
 \end{codehigh}
 
 \begin{demohigh}
@@ -1342,13 +1399,13 @@
 \end{tblr}
 \end{demohigh}
 
-Any new row type must be defined with \verb!\NewRowType! command.
+Any new row type must be defined with \CC{\NewTblrRowType} command.
 It can have an optional argument when it's defined.
 
 \chapter{Extra Interfaces}
 \label{chap:extra}
 
-In general, \verb!tblr! environment can accepts both inner and outer specifications:
+In general, \EE{tblr} environment accepts both inner and outer specifications:
 
 \begin{codehigh}
 \begin{tblr}[<outer specs>]{<inner specs>}
@@ -1357,15 +1414,15 @@
 \end{codehigh}
 
 \textbf{Inner specifications} are all specifications written in the \underline{mandatory} argument
-of \verb!tblr! environment, which include new interfaces described in Chapter \ref{chap:basic}.
+of \EE{tblr} environment, which include new interfaces described in Chapter \ref{chap:basic}.
 
 \textbf{Outer specifications} are all specifications written in the \underline{optional} argument
-of \verb!tblr! environment, most of which are used for long tables (see Chapter \ref{chap:long}).
+of \EE{tblr} environment, most of which are used for long tables (see Chapter \ref{chap:long}).
 
-You can use \verb!\SetTblrInner! and \verb!\SetTblrOuter! commands
+You can use \CC{\SetTblrInner} and \CC{\SetTblrOuter} commands
 to set default inner and outer specifications of tables, respectively (see Section \ref{sec:default}).
 
-\section{Inner Specifications}
+\section{Inner specifications}
 
 In addition to new interfaces in Chapter \ref{chap:basic},
 there are several inner specifications which are described in Table~\ref{key:inner}.
@@ -1389,9 +1446,9 @@
   \K{baseline} & set the baseline of the table & \V{m} \\
 \end{spectblr}
 
-\subsection{Space between Double Rules}
+\subsection{Space between double rules}
 
-The following example shows that we can replace \verb!\doublerulesep! parameter with \verb!rulesep! key.
+The following example shows that we can replace \CC{\doublerulesep} parameter with \K{rulesep} key.
 \nopagebreak
 \begin{demohigh}
 \begin{tblr}{
@@ -1403,9 +1460,9 @@
 \end{tblr}
 \end{demohigh}
 
-\subsection{Minimal Strut for Cell Text}
+\subsection{Minimal strut for cell text}
 
-The following example shows that we can replace \verb!\arraystretch! parameter with \verb!stretch! key.
+The following example shows that we can replace \CC{\arraystretch} parameter with \K{stretch} key.
 
 \begin{demohigh}
 \begin{tblr}{hlines,stretch=1.5}
@@ -1425,9 +1482,9 @@
 \end{tblr}
 \end{demohigh}
 
-\subsection{Rowseps and Colseps for All}
+\subsection{Rowseps and colseps for all}
 
-The following example uses \verb!rowsep! and \verb!colsep! keys to set padding for all rows and columns.
+The following example uses \K{rowsep} and \K{colsep} keys to set padding for all rows and columns.
 \nopagebreak
 \begin{demohigh}
 \SetTblrInner{rowsep=2pt,colsep=2pt}
@@ -1438,12 +1495,12 @@
 \end{tblr}
 \end{demohigh}
 
-\subsection{Hspan and Vspan Algorithms}
+\subsection{Hspan and vspan algorithms}
 
-With \verb!hspan=default! or \verb!hspan=even!,
-\verb!tabularray! package will compute column widths from span widths.
-But with \verb!hspan=minimal!, it will compute span widths from column widths.
-The following examples show the results from different \verb!hspan! values.
+With \KV{hspan=default} or \KV{hspan=even},
+\PP{tabularray} package will compute column widths from span widths.
+But with \KV{hspan=minimal}, it will compute span widths from column widths.
+The following examples show the results from different \K{hspan} values.
 
 \begin{demohigh}
 \SetTblrInner{hlines, vlines, hspan=default}
@@ -1475,7 +1532,7 @@
 \end{tblr}
 \end{demohigh}
 
-The following examples show the results from different \verb!vspan! values.
+The following examples show the results from different \K{vspan} values.
 \nopagebreak
 \begin{demohigh}
 \SetTblrInner{hlines, vlines, vspan=default}
@@ -1503,25 +1560,11 @@
 \end{tblr}
 \end{demohigh}
 
-\subsection{Use Verbatim Commands}
+\subsection{Set baseline for the table}
 
-%With \verb!verb! key, you can write \verb!\verb! commands in the cell text:
-%
-%\begin{demohigh}
-%\begin{tblr}{hlines,verb}
-%  20 & 30 & \verb!\hello{world}!40 \\
-%  50 & \verb!\hello!60 & 70 \\
-%\end{tblr}
-%\end{demohigh}
+With \K{baseline} key, you can set baseline for the table.
+All possible values for \K{baseline} are as follows:
 
-The inner key \verb!verb! is obsolete from version 2023A, and will be removed in the future.
-Instead you can use more reliable \verb!\fakeverb! command (see Section \ref{sec:fakeverb}).
-
-\subsection{Set Baseline for the Table}
-
-With \verb!baseline! key, you can set baseline for the table.
-All possible values for \verb!baseline! are as follows:
-
 \begin{center}
 \begin{tblr}{width=0.6\textwidth,colspec={cX[l]},hlines}
   \V{t}    & align the table at the top \\
@@ -1552,11 +1595,11 @@
 \end{tblr}Baseline
 \end{demohigh}
 
-The differences between \verb!b! and \verb!B! are similar to \verb!t! and \verb!T!.
-In fact, these two values \verb!T! and \verb!B! are better replacements
-for currently obsolete \verb!\firsthline! and \verb!\lasthline! commands.
+The differences between \V{b} and \V{B} are similar to \V{t} and \V{T}.
+In fact, these two values \V{T} and \V{B} are better replacements
+for currently obsolete \CC{\firsthline} and \CC{\lasthline} commands.
 
-\section{Outer Specifications}
+\section{Outer specifications}
 
 Except for specifications to be introduced in Chapter \ref{chap:long},
 there are several other outer specifications which are described in Table~\ref{key:outer}.
@@ -1573,11 +1616,11 @@
   \K{expand+} & like \K{expand} but appends to previous values & \None \\
 \end{spectblr}
 
-\subsection{Set Baseline in Another Way}
+\subsection{Set baseline in another way}
 
 You may notice that you can write \K{baseline} option as either an inner or an outer specification.
 It is true that either way would do the job. But there is a small difference:
-when \verb!baseline=t/T/m/b/B! is an outer specification,
+when \KV{baseline=t/T/m/b/B} is an outer specification,
 you can omit the key name and write the value only.
 
 \begin{demohigh}
@@ -1588,7 +1631,7 @@
 \end{tblr}Baseline
 \end{demohigh}
 
-\subsection{Long and Tall Tables}
+\subsection{Long and tall tables}
 
 You can change a table to long table by passing outer specification \K{long},
 or change it to tall table by passing outer specification \K{tall} (see Chapter~\ref{chap:long}).
@@ -1602,17 +1645,17 @@
 \end{tblr}
 \end{codehigh}
 
-\subsection{Expand Macros First}
+\subsection{Expand macros first}
 
-In contrast to traditional \verb!tabular! environment, \verb!tabularray! environments
-need to see every \verb!&! and \verb!\\! when splitting the table body with \verb!l3regex!.
-And you can not put cell text inside any table command defined with \verb!\NewTableCommand!.
-But you could use outer key \verb!expand! to make \verb!tabularray! expand
-every occurrence of any of the specified macros \underline{once} and \underline{in the given oder} before splitting the table body.
-Note that you \underline{can not} expand a command defined with \verb!\NewDocumentCommand!.
-You can also use \verb!expand+! if you still want to keep the macros in the current \verb!expand! setting.
+In contrast to traditional \EE{tabular} environment, \PP{tabularray} environments
+need to see every \TT{&} and \TT{\\\\} when splitting the table body.
+And you can not put cell text inside any table command defined with \CC{\NewTblrTableCommand}.
+But you could use outer key \K{expand} to make \PP{tabularray} expand
+every occurrence of any of the specified macros \emph{once} and in the given order before splitting the table body.
+Note that you \emph{can not} expand a command defined with \CC{\NewDocumentCommand}.
+You can also use \K{expand+} if you still want to keep the macros in the current \K{expand} setting.
 
-To expand a command without optional argument, you can define it with \verb!\newcommand!.
+To expand a command without optional argument, you can define it with \CC{\newcommand}.
 
 \begin{demohigh}
 \newcommand*\tblrrowa{
@@ -1639,9 +1682,9 @@
 \end{tblr}
 \end{demohigh}
 
-To expand commands with optional arguments, you \underline{can not} define them
-with \verb!\newcommand!. But you can define them with \verb!\NewExpandableDocumentCommand!,
-and use option \verb!expand=\expanded! to do exhaustive expansions.
+To expand commands with optional arguments, you \emph{can not} define them
+with \CC{\newcommand}. But you can define them with \CC{\NewExpandableDocumentCommand},
+and use option \KV{expand=\expanded} to do exhaustive expansions.
 
 \begin{demohigh}
 \NewExpandableDocumentCommand\yes{O{Yes}m}{\SetCell{bg=green9}#1}
@@ -1653,21 +1696,21 @@
 \end{tblr}
 \end{demohigh}
 
-Note that you need to protect fragile commands (if any) inside them with \verb!\unexpanded! command.
+Note that you need to protect fragile commands (if any) inside them with \CC{\unexpanded} command.
 
-\section{Default Specifications}%
+\section{Default specifications}%
 \label{sec:default}
 
-\verb!Tabularray! package provides \verb!\SetTblrInner! and \verb!\SetTblrOuter! commands
+\PP{Tabularray} package provides \CC{\SetTblrInner} and \CC{\SetTblrOuter} commands
 for you to change the default inner and outer specifications of tables.
 
-In general different \verb!tabularray! environments (\verb!tblr!, \verb!talltblr!,
-\verb!longtblr!, etc) could have different default specifications.
+In general different \PP{tabularray} environments (\EE{tblr}, \EE{talltblr},
+\EE{longtblr}, etc) could have different default specifications.
 You can list the environments in the optional arguments of these two commands,
-and they only apply to \verb!tblr! environment when the optional arguments are omitted.
+and they only apply to \EE{tblr} environment when the optional arguments are omitted.
 
-In the following example, the first line draws all hlines and vlines for all \verb!tblr! tables
-created afterwards, while the second line makes all \verb!tblr! tables created afterwards
+In the following example, the first line draws all hlines and vlines for all \EE{tblr} tables
+created afterwards, while the second line makes all \EE{tblr} tables created afterwards
 vertically align at the last row.
 
 \begin{codehigh}
@@ -1675,7 +1718,7 @@
 \SetTblrOuter{baseline=B}
 \end{codehigh}
 
-And the following example sets zero \verb!rowsep! for all \verb!tblr! and \verb!longtblr! tables
+And the following example sets zero \K{rowsep} for all \EE{tblr} and \EE{longtblr} tables
 created afterwards.
 
 \begin{codehigh}
@@ -1682,9 +1725,9 @@
 \SetTblrInner[tblr,longtblr]{rowsep=0pt}
 \end{codehigh}
 
-\section{New Tabularray Environments}
+\section{New tabularray environments}
 
-You can define new \verb!tabularray! environments using \verb!\NewTblrEnviron! command:
+You can define new \PP{tabularray} environments using \CC{\NewTblrEnviron} command:
 
 \begin{demohigh}
 \NewTblrEnviron{mytblr}
@@ -1697,10 +1740,10 @@
 \end{mytblr} Text
 \end{demohigh}
 
-\section{New General Environments}
+\section{New general environments}
 
-With \verb!+b! argument type of \verb!\NewDocumentEnvironment! command,
-you can also define a new general environment based on \verb!tblr! environment
+With \V{+b} argument type of \CC{\NewDocumentEnvironment} command,
+you can also define a new general environment based on \EE{tblr} environment
 (note that there is an extra pair of curly braces at the end):
 
 \NewDocumentEnvironment{fancytblr}{+b}{
@@ -1727,13 +1770,13 @@
 \end{fancytblr}
 \end{demohigh}
 
-\section{New Table Commands}
+\section{New table commands}
 
-All commands which change the specifications of tables \textcolor{red3}{must} be defined with \verb!\NewTableCommand!.
+All commands which change the specifications of tables \emph{must} be defined with \CC{\NewTblrTableCommand}.
 The following example demonstrates how to define a new table command:
 
 \begin{demohigh}
-\NewTableCommand\myhline{\hline[0.1em,red5]}
+\NewTblrTableCommand\myhline{\hline[0.1em,red5]}
 \begin{tblr}{llll}
 \myhline
  Alpha   & Beta  & Gamma   & Delta \\
@@ -1743,50 +1786,73 @@
 \end{tblr}
 \end{demohigh}
 
-\section{Odd and Even Selectors}
+\section{Child indexers and selectors}
 
-From version 2022A, child selectors \verb!odd! and \verb!even! accept an optional argument,
-in which you can specify the start index and the end index of the children.
+From version 2025A, child indexer \CI{Z} accepts an optional argument for making a negative index.
 
 \begin{demohigh}
 \begin{tblr}{
-  cell{odd}{1} = {red9},
-  cell{odd[4]}{2} = {green9},
-  cell{odd[3-X]}{3} = {blue9},
+  cell{1}{2-Z[2]} = {red9},
+  cell{2}{3-Z[3]} = {green9},
+  cell{3}{Z[6]-Z[4]} = {blue9}
 }
-  Head   & Head    & Head   \\
-  Talk A & Place A & Date A \\
-  Talk B & Place B & Date B \\
-  Talk C & Place C & Date C \\
-  Talk D & Place D & Date D \\
-  Talk E & Place E & Date E \\
-  Talk F & Place F & Date F \\
-  Talk G & Place G & Date G \\
-  Talk H & Place H & Date H \\
+  A & B & C & D & E & F & G & H & I \\
+  A & B & C & D & E & F & G & H & I \\
+  A & B & C & D & E & F & G & H & I
 \end{tblr}
 \end{demohigh}
 
+From version 2022A, child selectors \CS{odd} and \CS{even} accept an optional argument,
+in which you can specify the start index of the children.
+
 \begin{demohigh}
 \begin{tblr}{
-  cell{even}{1} = {yellow9},
-  cell{even[4]}{2} = {cyan9},
-  cell{even[3-X]}{3} = {purple9},
+  cell{1}{odd} = {yellow9},
+  cell{2}{odd[5]} = {purple9},
+  cell{3}{odd[4]} = {cyan9}
 }
-  Head   & Head    & Head   \\
-  Talk A & Place A & Date A \\
-  Talk B & Place B & Date B \\
-  Talk C & Place C & Date C \\
-  Talk D & Place D & Date D \\
-  Talk E & Place E & Date E \\
-  Talk F & Place F & Date F \\
-  Talk G & Place G & Date G \\
-  Talk H & Place H & Date H \\
+  A & B & C & D & E & F & G & H & I & J \\
+  A & B & C & D & E & F & G & H & I & J \\
+  A & B & C & D & E & F & G & H & I & J
 \end{tblr}
 \end{demohigh}
 
-\section{Counters and Lengths}
+\begin{demohigh}
+\begin{tblr}{
+  cell{1}{even} = {red9},
+  cell{2}{even[4]} = {green9},
+  cell{3}{even[3]} = {blue9}
+}
+  A & B & C & D & E & F & G & H & I & J \\
+  A & B & C & D & E & F & G & H & I & J \\
+  A & B & C & D & E & F & G & H & I & J \\
+\end{tblr}
+\end{demohigh}
 
-Counters \verb!rownum!, \verb!colnum!, \verb!rowcount!, \verb!colcount! can be used in cell text:
+From version 2025A, there is a new child selector \CS{every}
+for selecting indexes in an arithmetic sequence.
+
+\begin{demohigh}
+\begin{tblr}{
+  cell{1}{every{2}{-2}} = {yellow9},
+  cell{2}{every[2]{2}{-2}} = {purple9},
+  cell{3}{every[3]{-9}{-2}} = {cyan9}
+}
+  A & B & C & D & E & F & G & H & I & J \\
+  A & B & C & D & E & F & G & H & I & J \\
+  A & B & C & D & E & F & G & H & I & J
+\end{tblr}
+\end{demohigh}
+
+The interface of \CS{every} selector is \fakeverb{every[<step>]{<start>}{<end>}},
+where \fakeverb{<start>} and \fakeverb{<end>} are postive or negative indexes.
+and they can not be child indexers such as \CI{U} or \CI{Z}.
+
+More child indexers and selectors can be defined by users (see Section~\ref{sect:newchild}).
+
+\section{Counters and lengths}
+
+Counters \CO{rownum}, \CO{colnum}, \CO{rowcount}, \CO{colcount} can be used in cell text:
 \nopagebreak
 \begin{demohigh}
 \begin{tblr}{hlines}
@@ -1801,20 +1867,20 @@
 \end{tblr}
 \end{demohigh}
 
-Also, lengths \verb!\leftsep!, \verb!\rightsep!, \verb!\abovesep!, \verb!\belowsep! can be used in cell text.
+Also, lengths \CC{\leftsep}, \CC{\rightsep}, \CC{\abovesep}, \CC{\belowsep} can be used in cell text.
 
-\section{Tracing Tabularray}
+\section{Tracing tabularray}
 
-To trace internal data behind \verb!tblr! environment, you can use \verb!\SetTblrTracing! command.
-For example, \verb!\SetTblrTracing{all}! will turn on all tracings,
-and \verb!\SetTblrTracing{none}! will turn off all tracings.
-\verb!\SetTblrTracing{+row,+column}! will only tracing row and column data.
+To trace internal data behind \EE{tblr} environment, you can use \CC{\SetTblrTracing} command.
+For example, \CC{\SetTblrTracing{all}} will turn on all tracings,
+and \CC{\SetTblrTracing{none}} will turn off all tracings.
+\CC{\SetTblrTracing{+row,+column}} will only tracing row and column data.
 All tracing messages will be written to the log files.
 
 \chapter{Use Long Tables}
 \label{chap:long}
 
-\section{A Simple Example}
+\section{A simple example}
 
 To make a decent long table with header and footer, it is better to separate header/footer as
 \underline{table head/foot} (which includes caption, footnotes, continuation text)
@@ -2031,8 +2097,8 @@
 \hline
 \end{longtblr}
 
-As you can see in the above example, the appearance of long tables of \verb!tabularray! package
-is similar to that of \verb!threeparttablex! packages.
+As you can see in the above example, the appearance of long tables of \PP{tabularray} package
+is similar to that of \PP{threeparttablex} packages.
 It supports table footnotes, but not page footnotes.
 
 \newpage
@@ -2097,12 +2163,12 @@
 \end{longtblr}
 \end{codehigh}
 
-As you can see in the above code, we typeset long tables with \verb!longtblr! environment.
-And we can totally separate contents and styles of long tables with \verb!tabularray! package.
+As you can see in the above code, we typeset long tables with \EE{longtblr} environment.
+And we can totally separate contents and styles of long tables with \PP{tabularray} package.
 
 Row head and row foot consist of some lines of the table and should appear in every page.
-Their options are inner specifications and should be put in the mandatory argument of the \verb!longtblr! environment.
-In the above example, We set \verb!rowhead=2! and \verb!rowfoot=1!.
+Their options are inner specifications and should be put in the mandatory argument of the \EE{longtblr} environment.
+In the above example, We set \KV{rowhead=2} and \KV{rowfoot=1}.
 
 \begin{spectblr}[
   caption = {Inner Specifications for Row Heads and Row Foots}
@@ -2113,43 +2179,41 @@
 \end{spectblr}
 
 Table head and table foot consist of the caption, continuation text, footnotes and remarks.
-Their options are outer specifications and should be put in the optional argument of the \verb!longtblr! environment.
+Their options are outer specifications and should be put in the optional argument of the \EE{longtblr} environment.
 
 \begin{spectblr}[
   caption = {Outer Specifications for Table Heads and Table Foots}
-]{verb}
+]{}
   Key Name            & Key Description & Initial Value \\
-  \V{headsep}         & vertical space between table head and table body & \V{6pt} \\
-  \V{footsep}         & vertical space between table foot and table body & \V{6pt} \\
-  \V{presep}          & vertical space between table head and the above text & \verb!1.5\bigskipamount! \\
-  \V{postsep}         & vertical space between table foot and the below text & \verb!1.5\bigskipamount! \\
-  \V{theme}           & table theme (including settings for templates and styles) & \None \\
-  \V{caption}         & table caption & \None \\
-  \V{entry}           & short table caption to be put in List of Tables & \None \\
-  \V{label}           & table label & \None \\
-  \V{note\{<name>\}}   & table note with \V{<name>} as tag & \None \\
-  \V{remark\{<name>\}} & table remark with \V{<name>} as tag & \None \\
+  \K{headsep}         & vertical space between table head and table body & \V{6pt} \\
+  \K{footsep}         & vertical space between table foot and table body & \V{6pt} \\
+  \K{presep}          & vertical space between table head and the above text & \VV{1.5\bigskipamount} \\
+  \K{postsep}         & vertical space between table foot and the below text & \VV{1.5\bigskipamount} \\
+  \K{theme}           & table theme (including settings for templates and styles) & \None \\
+  \K{caption}         & table caption & \None \\
+  \K{entry}           & short table caption to be put in List of Tables & \None \\
+  \K{label}           & table label & \None \\
+  \KK{note{<name>}}   & table note with \V{<name>} as tag & \None \\
+  \KK{remark{<name>}} & table remark with \V{<name>} as tag & \None \\
 \end{spectblr}
 
-If you write \verb!entry=none!, \verb!tabularray! package will not add an entry in List of Tables.
-Therefore \verb!caption=text,entry=none! is similar to \verb!\caption[]{text}! in \verb!longtable!.
+If you write \KV{entry=none}, \PP{tabularray} package will not add an entry in List of Tables.
+Therefore \KV{caption=text,entry=none} is similar to \CC{\caption[]{text}} in \PP{longtable}.
 
-If you write \verb!label=none!, \verb!tabularray! package will not step \verb!table! counter,
-and set the \verb!caption-tag! and \verb!caption-sep! elements (see below) to empty.
-Therefore \verb!caption=text,entry=none,label=none! is similar to \verb!\caption*{text}!
-in \verb!longtable!, except for the counter.
+If you write \KV{label=none}, \PP{tabularray} package will not step \CO{table} counter,
+and set the \EN{caption-tag} and \EN{caption-sep} elements (see below) to empty.
+Therefore \KV{caption=text,entry=none,label=none} is similar to \CC{\caption*{text}}
+in \PP{longtable}, except for the counter.
 
-\section{Customize Templates}
+\section{Customize templates}
 
-\subsection{Overview of Templates}
+\subsection{Overview of templates}
 
-The template system for table heads and table foots in \verb!tabularray! is largely inspired
-by \verb!beamer!, \verb!caption! and \verb!longtable! packages. For elements in Table \ref{tblr:element},
-you can use \verb!\DefTblrTemplate!\footnote{From version 2022A,
-\texttt{\string\DefTblrTemplate} has another name \texttt{\string\DeclareTblrTemplate}.}
-to define and modify templates,
-and use \verb!\SetTblrTemplate! to choose default templates. In defining templates,
-you can include other templates with \verb!\UseTblrTemplate! and \verb!\ExpTblrTemplate! commands.
+The template system for table heads and table foots in \PP{tabularray} is largely inspired
+by \PP{beamer}, \PP{caption} and \PP{longtable} packages. For elements in Table \ref{tblr:element},
+you can use \CC{\DeclareTblrTemplate} to define and modify templates,
+and use \CC{\SetTblrTemplate} to choose default templates. In defining templates,
+you can include other templates with \CC{\UseTblrTemplate} and \CC{\ExpTblrTemplate} commands.
 
 \begin{spectblr}[
   caption = {Elements for Table Heads and Table Foots},
@@ -2183,14 +2247,14 @@
 \end{spectblr}
 
 An element which only includes short text is called a \underline{sub element}.
-Normally there is one \verb!-! in the name of a sub element.
+Normally there is one \TT{-} in the name of a sub element.
 An element which includes one or more paragraphs is called a \underline{main element}.
-Normally there isn't any \verb!-! in the name of a main element.
+Normally there isn't any \TT{-} in the name of a main element.
 
-For each of the above elements, two templates \verb!normal! and \verb!empty! are always defined.
-You can select one of them with \verb!\SetTblrTemplate! command.
+For each of the above elements, two templates \TN{normal} and \TN{empty} are always defined.
+You can select one of them with \CC{\SetTblrTemplate} command.
 
-\subsection{Continuation Templates}
+\subsection{Continuation templates}
 
 Let us have a look at the code for defining templates of continuation text first:%
 \footnote{To tell the truth, the default \texttt{conthead-text} and \texttt{contfoot-text}
@@ -2198,55 +2262,55 @@
 respectively. And you may contribute your translations of them to \textbf{babel} package.}
 
 \begin{codehigh}
-\DefTblrTemplate{contfoot-text}{normal}{Continued on next page}
+\DeclareTblrTemplate{contfoot-text}{normal}{Continued on next page}
 \SetTblrTemplate{contfoot-text}{normal}
-\DefTblrTemplate{conthead-text}{normal}{(Continued)}
+\DeclareTblrTemplate{conthead-text}{normal}{(Continued)}
 \SetTblrTemplate{conthead-text}{normal}
 \end{codehigh}
 
-In the above code, command \verb!\DefTblrTemplate! defines the templates with name \verb!normal!,
-and then command \verb!\SetTblrTemplate! sets the templates with name \verb!normal! as default.
-The \verb!normal! template is always defined and set as default for any element in \verb!tabularray!.
+In the above code, command \CC{\DeclareTblrTemplate} defines the templates with name \TN{normal},
+and then command \CC{\SetTblrTemplate} sets the templates with name \TN{normal} as default.
+The \TN{normal} template is always defined and set as default for any element in \PP{tabularray}.
 Therefore you had better use another name when defining new templates.
 
-If you use \verb!default! as template name in \verb!\DefTblrTemplate!,
+If you use \TN{default} as template name in \CC{\DeclareTblrTemplate},
 you define and set it as default at the same time.
 Therefore the above code can be written in another way:
 
 \begin{codehigh}
-\DefTblrTemplate{contfoot-text}{default}{Continued on next page}
-\DefTblrTemplate{conthead-text}{default}{(Continued)}
+\DeclareTblrTemplate{contfoot-text}{default}{Continued on next page}
+\DeclareTblrTemplate{conthead-text}{default}{(Continued)}
 \end{codehigh}
 
 You may modify the code to customize continuation text to fit your needs.
 
-The templates for \verb!contfoot! and \verb!conthead! normally
-include the templates of their sub elements with \verb!\UseTblrTemplate! commands.
+The templates for \EN{contfoot} and \EN{conthead} normally
+include the templates of their sub elements with \CC{\UseTblrTemplate} commands.
 But you can also handle user settings such as horizontal alignment here.
 
 \begin{codehigh}
-\DefTblrTemplate{contfoot}{default}{\UseTblrTemplate{contfoot-text}{default}}
-\DefTblrTemplate{conthead}{default}{\UseTblrTemplate{conthead-text}{default}}
+\DeclareTblrTemplate{contfoot}{default}{\UseTblrTemplate{contfoot-text}{default}}
+\DeclareTblrTemplate{conthead}{default}{\UseTblrTemplate{conthead-text}{default}}
 \end{codehigh}
 
-\subsection{Caption Templates}
+\subsection{Caption templates}
 
 Normally a caption consists of three parts, and their templates are defined with the follow code:
 
 \begin{codehigh}
-\DefTblrTemplate{caption-tag}{default}{Table\hspace{0.25em}\thetable}
-\DefTblrTemplate{caption-sep}{default}{:\enskip}
-\DefTblrTemplate{caption-text}{default}{\InsertTblrText{caption}}
+\DeclareTblrTemplate{caption-tag}{default}{Table\hspace{0.25em}\thetable}
+\DeclareTblrTemplate{caption-sep}{default}{:\enskip}
+\DeclareTblrTemplate{caption-text}{default}{\InsertTblrText{caption}}
 \end{codehigh}
 
-The command \verb!\InsertTblrText{caption}! inserts the value of \verb!caption! key,
-which you could write in the optional argument of \verb!longtblr! environment.
+The command \CC{\InsertTblrText{caption}} inserts the value of \K{caption} key,
+which you could write in the optional argument of \EE{longtblr} environment.
 
-The \verb!caption! template normally includes three sub templates with \verb!\UseTblrTemplate! commands:
-The \verb!caption! template will be used in \verb!firsthead! template.
+The \EN{caption} template normally includes three sub templates with \CC{\UseTblrTemplate} commands:
+The \EN{caption} template will be used in \EN{firsthead} template.
 
 \begin{codehigh}
-\DefTblrTemplate{caption}{default}{
+\DeclareTblrTemplate{caption}{default}{
   \UseTblrTemplate{caption-tag}{default}
   \UseTblrTemplate{caption-sep}{default}
   \UseTblrTemplate{caption-text}{default}
@@ -2253,11 +2317,11 @@
 }
 \end{codehigh}
 
-Furthermore \verb!capcont! template includes \verb!conthead! template as well.
-The \verb!capcont! template will be used in \verb!middlehead! and \verb!lasthead! templates.
+Furthermore \EN{capcont} template includes \EN{conthead} template as well.
+The \EN{capcont} template will be used in \EN{middlehead} and \EN{lasthead} templates.
 
 \begin{codehigh}
-\DefTblrTemplate{capcont}{default}{
+\DeclareTblrTemplate{capcont}{default}{
   \UseTblrTemplate{caption-tag}{default}
   \UseTblrTemplate{caption-sep}{default}
   \UseTblrTemplate{caption-text}{default}
@@ -2265,17 +2329,17 @@
 }
 \end{codehigh}
 
-\subsection{Note and Remark Templates}
+\subsection{Note and remark templates}
 
 The templates for table notes can be defined like this:
 
 \begin{codehigh}
-\DefTblrTemplate{note-tag}{default}{\textsuperscript{\InsertTblrNoteTag}}
-\DefTblrTemplate{note-sep}{default}{\space}
-\DefTblrTemplate{note-text}{default}{\InsertTblrNoteText}
+\DeclareTblrTemplate{note-tag}{default}{\textsuperscript{\InsertTblrNoteTag}}
+\DeclareTblrTemplate{note-sep}{default}{\space}
+\DeclareTblrTemplate{note-text}{default}{\InsertTblrNoteText}
 \end{codehigh}
 \begin{codehigh}
-\DefTblrTemplate{note}{default}{
+\DeclareTblrTemplate{note}{default}{
   \MapTblrNotes{
     \noindent
     \UseTblrTemplate{note-tag}{default}
@@ -2286,20 +2350,20 @@
 }
 \end{codehigh}
 
-The \verb!\MapTblrNotes! command loops for all table notes,
-which are written in the optional argument of \verb!longtblr! environment.
-Inside the loop, you can use \verb!\InsertTblrNoteTag! and \verb!\InsertTblrNoteText!
+The \CC{\MapTblrNotes} command loops for all table notes,
+which are written in the optional argument of \EE{longtblr} environment.
+Inside the loop, you can use \CC{\InsertTblrNoteTag} and \CC{\InsertTblrNoteText}
 commands to insert current note tag and note text, respectively.
 
 The definition of remark templates are similar to note templates.
 \nopagebreak
 \begin{codehigh}
-\DefTblrTemplate{remark-tag}{default}{\InsertTblrRemarkTag}
-\DefTblrTemplate{remark-sep}{default}{:\space}
-\DefTblrTemplate{remark-text}{default}{\InsertTblrRemarkText}
+\DeclareTblrTemplate{remark-tag}{default}{\InsertTblrRemarkTag}
+\DeclareTblrTemplate{remark-sep}{default}{:\space}
+\DeclareTblrTemplate{remark-text}{default}{\InsertTblrRemarkText}
 \end{codehigh}
 \begin{codehigh}
-\DefTblrTemplate{remark}{default}{
+\DeclareTblrTemplate{remark}{default}{
   \MapTblrRemarks{
     \noindent
     \UseTblrTemplate{remark-tag}{default}
@@ -2310,37 +2374,37 @@
 }
 \end{codehigh}
 
-\subsection{Head and Foot Templates}
+\subsection{Head and foot templates}
 
 The templates for table heads and foots are defined as including other templates:
 
 \begin{codehigh}
-\DefTblrTemplate{firsthead}{default}{
+\DeclareTblrTemplate{firsthead}{default}{
   \UseTblrTemplate{caption}{default}
 }
-\DefTblrTemplate{middlehead,lasthead}{default}{
+\DeclareTblrTemplate{middlehead,lasthead}{default}{
   \UseTblrTemplate{capcont}{default}
 }
-\DefTblrTemplate{firstfoot,middlefoot}{default}{
+\DeclareTblrTemplate{firstfoot,middlefoot}{default}{
   \UseTblrTemplate{contfoot}{default}
 }
-\DefTblrTemplate{lastfoot}{default}{
+\DeclareTblrTemplate{lastfoot}{default}{
   \UseTblrTemplate{note}{default}
   \UseTblrTemplate{remark}{default}
 }
 \end{codehigh}
 
-Note that you can define the same template for multiple elements in \verb!\DefTblrTemplate! command.
+Note that you can define the same template for multiple elements in \CC{\DeclareTblrTemplate} command.
 If you only want to show table caption in the first page, you may change the definitions of
-\verb!middlehead! and \verb!lasthead! elements:
+\EN{middlehead} and \EN{lasthead} elements:
 
 \begin{codehigh}
-\DefTblrTemplate{middlehead,lasthead}{default}{
+\DeclareTblrTemplate{middlehead,lasthead}{default}{
   \UseTblrTemplate{conthead}{default}
 }
 \end{codehigh}
 
-\section{Change Styles}
+\section{Change styles}
 
 All available keys for template elements are described in Table \ref{key:element}.
 
@@ -2360,7 +2424,7 @@
   \K{hang}               & hangindent value & \V{0pt} or \V{0.7em} \\
 \end{spectblr}
 
-You may change the styles of elements with \verb!\SetTblrStyle! command:
+You may change the styles of elements with \CC{\SetTblrStyle} command:
 
 \begin{codehigh}
 \SetTblrStyle{firsthead}{font=\bfseries}
@@ -2369,19 +2433,19 @@
 \SetTblrStyle{caption-tag}{red2}
 \end{codehigh}
 
-When you write \verb!\UseTblrTemplate{element}{default}! in defining a template,
-beside including template code of the \verb!element!, the foreground color and font commands
-of the \verb!element! will be set up automatically.
-In contrast, \verb!\ExpTblrTemplate{element}{default}! will only include template code.
+When you write \CC{\UseTblrTemplate{element}{default}} in defining a template,
+beside including template code of the \EN{element}, the foreground color and font commands
+of the \EN{element} will be set up automatically.
+In contrast, \CC{\ExpTblrTemplate{element}{default}} will only include template code.
 
-\section{Define Themes}
+\section{Define themes}
 
-You may define your own themes for table heads and foots with \verb!\NewTblrTheme! command.
+You may define your own themes for table heads and foots with \CC{\NewTblrTheme} command.
 a theme consists of some template and style settings. For example:
 \nopagebreak
 \begin{codehigh}
 \NewTblrTheme{fancy}{
-  \DefTblrTemplate{conthead}{default}{[Continued]}
+  \DeclareTblrTemplate{conthead}{default}{[Continued]}
   \SetTblrStyle{firsthead}{font=\bfseries}
   \SetTblrStyle{firstfoot}{fg=blue2}
   \SetTblrStyle{middlefoot}{\itshape}
@@ -2389,19 +2453,19 @@
 }
 \end{codehigh}
 
-After defining the theme \verb!fancy!, you can use it
-by writing \verb!theme=fancy! in the optional argument of \verb!longtblr! environment.
+After defining the theme \V{fancy}, you can use it
+by writing \KV{theme=fancy} in the optional argument of \EE{longtblr} environment.
 
-\section{Control Page Breaks}
+\section{Control page breaks}
 
-Just like \verb!longtable! package, inside \verb!longtblr! environment,
-you can use \verb!\\*! or \verb!\nopagebreak! to prohibit a page break,
-and use \verb!\pagebreak! to force a page break.
+Just like \PP{longtable} package, inside \EE{longtblr} environment,
+you can use \CC{\\\\*} or \CC{\nopagebreak} to prohibit a page break,
+and use \CC{\pagebreak} to force a page break.
 
-\section{Floatable Tall Tables}
+\section{Floatable tall tables}
 
-There is also a \verb!talltblr! environment as an alternative to \verb!threeparttable! environment.
-It can not cross multiple pages, but it can be put inside \verb!table! environment.
+There is also a \EE{talltblr} environment as an alternative to \EE{threeparttable} environment.
+It can not cross multiple pages, but it can be put inside \EE{table} environment.
 
 \begin{demohigh}
 TEXT\begin{talltblr}[
@@ -2420,18 +2484,20 @@
 \end{demohigh}
 
 \chapter{Use Some Libraries}
+\label{chap:lib}
 
-The \verb!tabularray! package emulates or fixes some commands in other packages.
-To avoid potential conflict, you need to enable them with \verb!\UseTblrLibrary! command.
+A \PP{tabularray} library could be loaded by \CC{\UseTblrLibrary} command.
+From version 2025A, an external library \fakeverb{foo} could also be loaded
+if its filename is \FF{tblrlibfoo.sty}.
 
 \section{Library \texttt{amsmath}}
 
-With \verb!\UseTblrLibrary{amsmath}! in the preamble of the document,
-\verb!tabularray! will load \verb!amsmath! package, and define \verb!+array!, \verb!+matrix!,
-\verb!+bmatrix!, \verb!+Bmatrix!, \verb!+pmatrix!, \verb!+vmatrix!, \verb!+Vmatrix! and \verb!+cases!
-environments. Each of the environments is similar to the environment without \verb!+! prefix in its name,
-but has default \verb!rowsep=2pt! just as \verb!tblr! environment. Every environment
-except \verb!+array! accepts an optional argument, where you can write inner specifications.
+With \CC{\UseTblrLibrary{amsmath}} in the preamble of the document,
+\PP{tabularray} will load \PP{amsmath} package, and define \EE{+array}, \EE{+matrix},
+\EE{+bmatrix}, \EE{+Bmatrix}, \EE{+pmatrix}, \EE{+vmatrix}, \EE{+Vmatrix} and \EE{+cases}
+environments. Each of the environments is similar to the environment without \TT{+} prefix in its name,
+but has default \KV{rowsep=2pt} just as \EE{tblr} environment. Every environment
+except \EE{+array} accepts an optional argument, where you can write inner specifications.
 
 \begin{demo}
 $\begin{pmatrix}
@@ -2469,10 +2535,13 @@
 
 \section{Library \texttt{booktabs}}
 
-With \verb!\UseTblrLibrary{booktabs}! in the preamble of the document,
-\verb!tabularray! will load \verb!booktabs! package,
-and define \verb!\toprule!, \verb!\midrule!,
-\verb!\bottomrule! and \verb!\cmidrule! inside \verb!tblr! environment.
+With \CC{\UseTblrLibrary{booktabs}} in the preamble of the document,
+\PP{tabularray} will load \PP{booktabs} package,
+and define
+  \CC{\toprule}, \CC{\midrule}, \CC{\bottomrule},
+  \CC{\cmidrule}, \CC{\cmidrulemore}, \CC{\morecmidrules},
+  \CC{\specialrule}, \CC{\addrowspace}, and \CC{\addlinespace}
+as table commands.
 
 \begin{demohigh}
 \begin{tblr}{llll}
@@ -2488,9 +2557,16 @@
 \end{tblr}
 \end{demohigh}
 
-Just like \verb!\hline! and \verb!\cline! commands,
-you can also specify rule width and color in the optional argument of any of these commands.
+Just like \CC{\hline} and \CC{\cline} commands,
+you can also specify rule width and color by using hline keys in the optional
+argument of any of these commands.
 
+Like in \PP{booktabs}, by default
+  width of \CC{\toprule} and \CC{\bottomrule} are determined by \CC{\heavyrulewidth},
+  width of \CC{\midrule} is determined by \CC{\lightrulewidth}, and
+  width of \CC{\cmidrule} and \CC{\cmidrulemore} are determined by \CC{\cmidrulewidth}, respectively.
+All three \CC{\...rulewidth} are dimensions.
+
 \begin{demohigh}
 \begin{tblr}{llll}
 \toprule[2pt,purple3]
@@ -2503,7 +2579,10 @@
 \end{tblr}
 \end{demohigh}
 
-If you need more than one \verb!\cmidrule!s, you can use \verb!\cmidrulemore! command.
+If you need more than one \verb!\cmidrule!s, you can use \verb!\cmidrulemore!
+command, which is simpler than the \verb!booktabs! usage
+\verb!\morecmidrules\cmidrule!.
+\verb!\cmidrulemore! can receive hline keys in an optional argument too.
 
 \begin{demohigh}
 \begin{tblr}{llll}
@@ -2517,8 +2596,11 @@
 \end{tblr}
 \end{demohigh}
 
-From version 2021N (2021-09-01), trim options (\verb!l!, \verb!r!, \verb!lr!)
-for \verb!\cmidrule! command are also supported.
+From version 2021N, you can set trimming positions of
+\CC{\cmidrule} and \CC{\cmidrulemore}, using newly introduced trimming
+options (\K{leftpos}, \K{rightpos}, \K{endpos}, \K{l}, \K{r},
+and \K{lr}) (see Section~\ref{sec:hlines-vlines}).
+Option \K{endpos} is already applied to these two commands.
 
 \begin{demohigh}
 \begin{tblr}{llll}
@@ -2532,16 +2614,15 @@
 \end{tblr}
 \end{demohigh}
 
-Note that you need to put \verb!l!, \verb!r! or \verb!lr! option into
-the \underline{\color{red3}square brackets}.
-and the possible values are decimal numbers between \verb!-1! and \verb!0!,
-where \verb!-1! means trimming the whole colsep, and \verb!0! means no trimming.
-The default value is \verb!-0.8!, which makes similar result as \verb!booktabs! package does.
+Since \PP{booktabs} tables usually don't have vlines, the meaningful values
+here are decimal numbers between \V{-1} and \V{0}.
+The default value \V{-0.8} for \V{l}, \V{r}, and \V{lr} is chosen to
+make similar result as \PP{booktabs} package does.
 
-There is also a \verb!booktabs! environment for you. With this environment,
-the default \verb!rowsep=0pt!, but extra vertical space will be added by
-\verb!\toprule!, \verb!\midrule!, \verb!\bottomrule! and \verb!\cmidrule! commands.
-The sizes of vertical space are determined by \verb!\aboverulesep! and \verb!\belowrulesep! dimensions.
+There is also a \EE{booktabs} environment for you. With this environment,
+the default \KV{rowsep=0pt}, but extra vertical space will be added by
+\CC{\toprule}, \CC{\midrule}, \CC{\bottomrule} and \CC{\cmidrule} commands.
+The sizes of vertical space are determined by \CC{\aboverulesep} and \CC{\belowrulesep} dimensions.
 
 \begin{demohigh}
 \begin{booktabs}{
@@ -2561,9 +2642,9 @@
 \end{demohigh}
 % S4     & 8 & 5 & 6 & 7 \\
 
-You can also use \verb!\specialrule! command.
-The second argument sets \verb!belowsep! of previous row,
-and the third argument sets \verb!abovesep! of current row,
+You can also use \CC{\specialrule} command.
+The second argument sets \K{belowsep} of previous row,
+and the third argument sets \K{abovesep} of current row,
 
 \begin{demohigh}
 \begin{booktabs}{row{2}={olive9}}
@@ -2577,11 +2658,13 @@
 \end{booktabs}
 \end{demohigh}
 
-At last, there is also an \verb!\addlinespace! command.
-You can specify the size of vertical space to be added in its optional argument,
-and the default size is \verb!0.5em!.
-This command adds one half of the space to \verb!belowsep! of previous row,
-and the other half to \verb!abovesep! of current row.
+At last, there is also an \CC{\addlinespace} command, with an alternative
+name \CC{\addrowspace}.
+You can specify the size of vertical space to be added in its optional
+argument, and the default size is determinted by \CC{\defaultaddspace}
+dimension, initially \V{0.5em}.
+This command adds one half of the space to \K{belowsep} of previous row,
+and the other half to \K{abovesep} of current row.
 
 \begin{demohigh}
 \begin{booktabs}{row{2}={olive9}}
@@ -2595,14 +2678,14 @@
 \end{booktabs}
 \end{demohigh}
 
-From version 2022A (2022-03-01), there is a \verb!longtabs! environment
-for writing long \verb!booktabs! tables,
-and a \verb!talltabs! environment for writing tall \verb!booktabs! tables.
+From version 2022A, there is a \EE{longtabs} environment
+for writing long \PP{booktabs} tables,
+and a \EE{talltabs} environment for writing tall \PP{booktabs} tables.
 
 \section{Library \texttt{counter}}
 
-You need to load \verb!counter! library with \verb!\UseTblrLibrary{counter}!,
-if you want to modify some LaTeX counters inside \verb!tabularray! tables.
+You need to load \LL{counter} library with \CC{\UseTblrLibrary{counter}},
+if you want to modify some LaTeX counters inside \PP{tabularray} tables.
 
 \begin{demohigh}
 \newcounter{mycnta}
@@ -2616,9 +2699,9 @@
 
 \section{Library \texttt{diagbox}}
 
-When writing \verb!\UseTblrLibrary{diagbox}! in the preamble of the document,
-\verb!tabularray! package loads \verb!diagbox! package,
-and you can use \verb!\diagbox! and \verb!\diagboxthree! commands inside \verb!tblr! environment.
+When writing \CC{\UseTblrLibrary{diagbox}} in the preamble of the document,
+\PP{tabularray} package loads \PP{diagbox} package,
+and you can use \CC{\diagbox} and \CC{\diagboxthree} commands inside \EE{tblr} environment.
 
 \begin{demohigh}
 \begin{tblr}{hlines,vlines}
@@ -2636,7 +2719,7 @@
 \end{tblr}
 \end{demohigh}
 
-You can also use \verb!\diagbox! and \verb!\diagboxthree! commands in math mode.
+You can also use \CC{\diagbox} and \CC{\diagboxthree} commands in math mode.
 \nopagebreak
 \begin{demohigh}
 $\begin{tblr}{|c|cc|}
@@ -2651,19 +2734,64 @@
 
 \section{Library \texttt{functional}}
 
-With \verb!\UseTblrLibrary{functional}! in the preamble of the document,
-\verb!tabularray! will load \href{https://ctan.org/pkg/functional}{\texttt{functional}} package,
-and define outer key \verb!evaluate! and inner key \verb!process!.
-These two new keys are useful for doing functional programming inside tables.
+With \CC{\UseTblrLibrary{functional}} in the preamble of the document,
+\PP{tabularray} will load \href{https://ctan.org/pkg/functional}{\PP{functional}} package,
+and define outer key \K{evaluate} and inner key \K{process}.
+This library brings intuitive functional programming into \PP{tabularray} tables.
 
-\subsection{Outer key \texttt{evaluate} in action}
+\subsection{Evaluate inner specifications}
 
-With outer key \verb!evaluate!, you can evaluate every occurrence of a specified protected function
-(defined with \verb!\prgNewFunction!) and replace it with the return value before splitting the table body.
+With this library, \PP{tabularray} will evaluate every function
+(defined with \CC{\prgNewFunction}) within inner specifications,
+replacing it with its return value, before parsing the key-value pairs. Here is an example:
+\begin{demohigh}
+\begin{tblr}{
+  hlines,
+  row{2} = {bg=\funColor{RGB}{180,180,255}}
+}
+  Alpha   & Beta  & Gamma  \\
+  Epsilon & Zeta  & Eta    \\
+  Iota    & Kappa & Lambda
+\end{tblr}
+\end{demohigh}
+The \CC{\funColor} function is provided by \PP{functional} package.
+And now let's see another example:
+\begin{demohigh}
+\begin{tblr}{
+  row{2} = {bg=\intIfOddTF{\value{page}}{\prgReturn{red7}}{\prgReturn{blue7}}}
+}
+  Alpha   & Beta  & Gamma  \\
+  Epsilon & Zeta  & Eta    \\
+  Iota    & Kappa & Lambda \\
+\end{tblr}
+\end{demohigh}
 
-The first application of \verb!evaluate! key is for inputting files inside tables.
-Assume you have two files \verb!test1.tmp! and \verb!test2.tmp! with the following contents:
+You may like to define a new function for it if you need to use it several times:
 
+\begin{demohigh}
+\IgnoreSpacesOn
+\prgNewFunction \colorMagic {mm} {
+  \intIfOddTF{\value{page}}{\prgReturn{#1}}{\prgReturn{#2}}
+}
+\IgnoreSpacesOff
+\begin{tblr}{
+  row{1} = {bg=\colorMagic{yellow7}{brown7}},
+  row{3} = {bg=\colorMagic{green7}{teal7}}
+}
+  Alpha   & Beta  & Gamma  \\
+  Epsilon & Zeta  & Eta    \\
+  Iota    & Kappa & Lambda \\
+\end{tblr}
+\end{demohigh}
+
+\subsection{Evaluate table body}
+
+With outer key \K{evaluate}, you can evaluate every occurrence of a specified protected function
+(defined with \CC{\prgNewFunction}) and replace it with the return value before splitting the table body.
+
+The first application of \K{evaluate} key is for inputting files inside tables.
+Assume you have two files \FF{test1.tmp} and \FF{test2.tmp} with the following contents:
+
 \begin{filecontents*}[overwrite]{test1.tmp}
 Some & Some \\
 \end{filecontents*}
@@ -2683,8 +2811,8 @@
 \end{filecontents*}
 \end{codehigh}
 
-Then you can input them with outer specification \verb!evaluate=\fileInput!.
-The \verb!\fileInput! function is provided by \verb!functional! package.
+Then you can input them with outer specification \KV{evaluate=\fileInput}.
+The \CC{\fileInput} function is provided by \PP{functional} package.
 
 \begin{demohigh}
 \begin{tblr}[evaluate=\fileInput]{hlines}
@@ -2697,19 +2825,19 @@
 \end{demohigh}
 
 In general, you can define your functions which return parts of table contents,
-and use \verb!evaluate! key to evaluate them inside tables.
+and use \K{evaluate} key to evaluate them inside tables.
 
 \begin{demohigh}
 \IgnoreSpacesOn
-\prgNewFunction \someFunc {m} {
+\prgNewFunction \myFunOne {m} {
   \prgReturn {#1 & #1 \\}
 }
 \IgnoreSpacesOff
-\begin{tblr}[evaluate=\someFunc]{hlines}
+\begin{tblr}[evaluate=\myFunOne]{hlines}
   Row1 & 1 \\
-  \someFunc{Text}
+  \myFunOne{Text}
   Row3 & 3 \\
-  \someFunc{Text}
+  \myFunOne{Text}
   Row5 & 5 \\
 \end{tblr}
 \end{demohigh}
@@ -2716,15 +2844,15 @@
 
 \begin{demohigh}
 \IgnoreSpacesOn
-\prgNewFunction \otherFunc {} {
+\prgNewFunction \myFunTwo {} {
   \prgReturn {Other & Other \\}
 }
 \IgnoreSpacesOff
-\begin{tblr}[evaluate=\otherFunc]{hlines}
+\begin{tblr}[evaluate=\myFunTwo]{hlines}
   Row1 & 1 \\
-  \otherFunc
+  \myFunTwo
   Row3 & 3 \\
-  \otherFunc
+  \myFunTwo
   Row5 & 5 \\
 \end{tblr}
 \end{demohigh}
@@ -2747,23 +2875,23 @@
 From version 2023A, you can evaluate all functions in the table body
 with option \texttt{evaluate=all}.
 
-\subsection{Inner key \texttt{process} in action}
+\subsection{Process table elements}
 
-With inner key \verb!process!, you can modify the contents and styles before the table is built.
-Several public functions defined with \verb!\prgNewFuncton! are provided for you:
+With inner key \K{process}, you can modify the contents and styles before the table is built.
+Several public functions defined with \CC{\prgNewFuncton} are provided for you:
 
 \begin{itemize}
-  \item \verb!\cellGetText{<rownum>}{<colnum>}!
-  \item \verb!\cellSetText{<rownum>}{<colnum>}{<text>}!
-  \item \verb!\cellSetStyle{<rownum>}{<colnum>}{<style>}!
-  \item \verb!\rowSetStyle{<rownum>}{<style>}!
-  \item \verb!\columnSetStyle{<colnum>}{<style>}!
+  \item \CC{\cellGetText{<rownum>}{<colnum>}}
+  \item \CC{\cellSetText{<rownum>}{<colnum>}{<text>}}
+  \item \CC{\cellSetStyle{<rownum>}{<colnum>}{<style>}}
+  \item \CC{\rowSetStyle{<rownum>}{<style>}}
+  \item \CC{\columnSetStyle{<colnum>}{<style>}}
 \end{itemize}
 
 As the first example, let's calculate the sums of cells column by column:
 
 \IgnoreSpacesOn
-\prgNewFunction \funcSum {} {
+\prgNewFunction \calcSum {} {
   \intStepOneInline {1} {\arabic{colcount}} {
     \intZero \lTmpaInt
     \intStepOneInline {1} {\arabic{rowcount}-1} {
@@ -2775,7 +2903,7 @@
 \IgnoreSpacesOff
 \begin{codehigh}
 \IgnoreSpacesOn
-\prgNewFunction \funcSum {} {
+\prgNewFunction \calcSum {} {
   \intStepOneInline {1} {\arabic{colcount}} {
     \intZero \lTmpaInt
     \intStepOneInline {1} {\arabic{rowcount}-1} {
@@ -2788,7 +2916,7 @@
 \end{codehigh}
 
 \begin{demohigh}
-\begin{tblr}{colspec={rrr},process=\funcSum}
+\begin{tblr}{colspec={rrr},process=\calcSum}
 \hline
   1 & 2 & 3 \\
   4 & 5 & 6 \\
@@ -2802,7 +2930,7 @@
 Now, let's set background colors of cells depending on their contents:
 
 \IgnoreSpacesOn
-\prgNewFunction \funcColor {} {
+\prgNewFunction \colorBack {} {
   \intStepOneInline {1} {\arabic{rowcount}} {
     \intStepOneInline {1} {\arabic{colcount}} {
       \intSet \lTmpaInt {\cellGetText {##1} {####1}}
@@ -2815,7 +2943,7 @@
 \IgnoreSpacesOff
 \begin{codehigh}
 \IgnoreSpacesOn
-\prgNewFunction \funcColor {} {
+\prgNewFunction \colorBack {} {
   \intStepOneInline {1} {\arabic{rowcount}} {
     \intStepOneInline {1} {\arabic{colcount}} {
       \intSet \lTmpaInt {\cellGetText {##1} {####1}}
@@ -2829,7 +2957,7 @@
 \end{codehigh}
 
 \begin{demohigh}
-\begin{tblr}{hlines,vlines,cells={r,$},process=\funcColor}
+\begin{tblr}{hlines,vlines,cells={r,$},process=\colorBack}
   -1 &  2 &  3 \\
    4 &  5 & -6 \\
    7 & -8 &  9 \\
@@ -2836,13 +2964,13 @@
 \end{tblr}
 \end{demohigh}
 
-We can also use color series of \verb!xcolor! package to color table rows:
+We can also use color series of \PP{xcolor} package to color table rows:
 
 \definecolor{lightb}{RGB}{217,224,250}
 \definecolorseries{tblrow}{rgb}{last}{lightb}{white}
 \resetcolorseries[3]{tblrow}
 \IgnoreSpacesOn
-\prgNewFunction \funcSeries {} {
+\prgNewFunction \colorSeries {} {
   \intStepOneInline {1} {\arabic{rowcount}} {
     \tlSet \lTmpaTl {\intMathMod {##1-1} {3}}
     \rowSetStyle {##1} {\expWhole{bg=tblrow!![\lTmpaTl]}}
@@ -2854,7 +2982,7 @@
 \definecolorseries{tblrow}{rgb}{last}{lightb}{white}
 \resetcolorseries[3]{tblrow}
 \IgnoreSpacesOn
-\prgNewFunction \funcSeries {} {
+\prgNewFunction \colorSeries {} {
   \intStepOneInline {1} {\arabic{rowcount}} {
     \tlSet \lTmpaTl {\intMathMod {##1-1} {3}}
     \rowSetStyle {##1} {\expWhole{bg=tblrow!![\lTmpaTl]}}
@@ -2864,7 +2992,7 @@
 \end{codehigh}
 
 \begin{demohigh}
-\begin{tblr}{hlines,process=\funcSeries}
+\begin{tblr}{hlines,process=\colorSeries}
   Row1 & 1 \\
   Row2 & 2 \\
   Row3 & 3 \\
@@ -2876,24 +3004,25 @@
 
 \section{Library \texttt{hook}}
 
-This library is experimental, please see\\
-\url{https://github.com/lvjr/tabularray/wiki/HooksAndVariables}.
+This library is \emph{experimental}.
+It will also load \LL{varwidth} library and set \KV{measure=vstore} as default.
+See Section~\ref{sect:hook} for more details of the library.
 
 \section{Library \texttt{html}}
 
-This library is experimental, please see\\
-\url{https://github.com/lvjr/tabularray/wiki/HooksAndVariables}.
+This library is \emph{experimental}.
+See Section~\ref{sect:pubvar} for more details of the library.
 
 \section{Library \texttt{nameref}}
 
-From version 2022D, you can load \verb!nameref! library
-to make \verb!\nameref! and \verb!longtblr! work together.
+From version 2022D, you can load \LL{nameref} library
+to make \CC{\nameref} and \EE{longtblr} work together.
 
 \section{Library \texttt{siunitx}}
 
-When writing \verb!\UseTblrLibrary{siunitx}! in the preamble of the document,
-\verb!tabularray! package loads \verb!siunitx! package,
-and defines \verb!S! column as \verb!Q! column with \verb!si! key.
+When writing \CC{\UseTblrLibrary{siunitx}} in the preamble of the document,
+\PP{tabularray} package loads \PP{siunitx} package,
+and defines \Q{S} column as \Q{Q} column with \K{si} key.
 
 \begin{demohigh}
 \begin{tblr}{
@@ -2900,10 +3029,10 @@
   hlines, vlines,
   colspec={S[table-format=3.2]S[table-format=3.2]}
 }
- {{{Head}}} & {{{Head}}} \\
-   111      &   111      \\
-     2.1    &     2.2    \\
-    33.11   &    33.22   \\
+   {Head}  & {Head} \\
+  111      & 111    \\
+    2.1    &   2.2  \\
+   33.11   &  33.22 \\
 \end{tblr}
 \end{demohigh}
 
@@ -2912,16 +3041,17 @@
   hlines, vlines,
   colspec={Q[si={table-format=3.2},c]Q[si={table-format=3.2},c]}
 }
- {{{Head}}} & {{{Head}}} \\
-   111      &   111      \\
-     2.1    &     2.2    \\
-    33.11   &    33.22   \\
+   {Head}  & {Head} \\
+  111      & 111    \\
+    2.1    &   2.2  \\
+   33.11   &  33.22 \\
 \end{tblr}
 \end{demohigh}
 
-Note that you need to use \underline{\color{red3}triple} pairs of curly braces to guard non-numeric cells.
-But it is cumbersome to enclose each cell with braces. From version 2022B (2022-06-01)
-a new key \verb!guard! is provided for cells and rows. With \verb!guard! key the previous example
+Note that you need to use one pairs of curly braces to guard non-numeric cells%
+\footnote{Before version 2025A, three pairs of braces are needed.}.
+But it is cumbersome to enclose each cell with braces. From version 2022B
+a new key \K{guard} is provided for cells and rows. With \K{guard} key the previous example
 can be largely simplified.
 
 \begin{demohigh}
@@ -2937,7 +3067,7 @@
 \end{tblr}
 \end{demohigh}
 
-Also you must use \verb!l!, \verb!c! or \verb!r! to set horizontal alignment for non-numeric cells:
+Also you must use \K{l}, \K{c} or \K{r} to set horizontal alignment for non-numeric cells:
 \nopagebreak
 \begin{demohigh}
 \begin{tblr}{
@@ -2956,23 +3086,185 @@
 \end{tblr}
 \end{demohigh}
 
-Both \verb!S! and \verb!s! columns are supported. In fact, These two columns have been defined as follows:
+Both \Q{S} and \Q{s} columns are supported. In fact, These two columns have been defined as follows:
 \begin{codehigh}
-\NewColumnType{S}[1][]{Q[si={#1},c]}
-\NewColumnType{s}[1][]{Q[si={#1},c,cmd=\TblrUnit]}
+\NewTblrColumnType{S}[1][]{Q[si={#1},c]}
+\NewTblrColumnType{s}[1][]{Q[si={#1},c,cmd=\TblrUnit]}
 \end{codehigh}
 You don't need to and are not allowed to define them again.
 
+\section{Library \texttt{tikz}}
+\label{sect:tikz}
+
+With this \emph{experimental} \LL{tikz} library,%
+\footnote{The author thanks \href{https://github.com/jasperhabicht}{Jasper Habicht}
+for his contributions to this library.}
+you can draw \PP{tikz} pictures below or above (short or tall) tables.
+This library depends on and loads \PP{tabularray} library \PP{hook} and \PP{tikz} library \TT{calc}.%
+\footnote{Please have a look at this \href{https://github.com/pgf-tikz/pgf/issues/1213}{\PP{tikz} issue}
+first if you need to write \fakeverb{\UseTblrLibrary{tikz}} in your LaTeX3 package.}
+
+To draw below/above a table,
+write some \PP{tikz} code inside \EE{tblrtikzbelow}/\EE{tblrtikzabove} environment.
+Both of them should be put before the table, and two compilations are needed to get desired result.
+
+Inside \EE{tblrtikzbelow}/\EE{tblrtikzabove} environment, you can use these predefined nodes:
+
+\begin{spectblr}[
+  caption = {Nodes created by \PP{tikz} library}
+]{}
+  Node Name &  Node Description \\
+  table node \NN{table}  & rectangle node for the whole table \\
+  cell node \NN{<i>-<j>} & rectangle node for \KK{cell{<i>}{<j>}} \\
+  corner node \NN{h<i>}  & coordinate node at the instersection point
+                           of \KK{hborder{<i>}} and \KK{vborder{1}} \\
+  corner node \NN{v<j>}  & coordinate node at the instersection point
+                           of \KK{vborder{<j>}} and \KK{hborder{1}}
+\end{spectblr}
+
+The first example below demonstrates the table node and cell nodes:
+
+\begin{demohigh}
+\begin{tblrtikzbelow}
+  \path[pattern color=gray9,pattern=checkerboard,
+        draw=blue3, ultra thick, rounded corners]
+    (table.north west) rectangle (table.south east);
+\end{tblrtikzbelow}%
+\begin{tblrtikzabove}
+  \draw[red3, thick]
+    (2-2.north west) -- (2-3.south east)
+    (2-2.south west) -- (2-3.north east);
+\end{tblrtikzabove}%
+\begin{tblr}{hline{2-3},vline{2-4}}
+  1-1 & 1-2 & 1-3 & 1-4 \\
+  2-1 & 2-2 & 2-3 & 2-4 \\
+  3-1 & 3-2 & 3-3 & 3-4
+\end{tblr}
+\end{demohigh}
+
+The second example below demonstrates corner nodes:
+
+\begin{demohigh}
+\begin{tblrtikzabove}
+  \draw[color=white,thick]
+       (h1-|v1) -- (h1-|v2) -- (h2-|v2)
+    -- (h2-|v3) -- (h3-|v3) -- (h3-|v4)
+    -- (h4-|v4) -- (h4-|v5) -- (h2-|v5)
+    -- (h2-|v6) -- (h1-|v6);
+\end{tblrtikzabove}%
+\begin{tblr}{hlines={wd=4pt},vlines={wd=3pt}}
+  1-1 & 1-2 & 1-3 & 1-4 & 1-5 \\
+  2-1 & 2-2 & 2-3 & 2-4 & 2-5 \\
+  3-1 & 3-2 & 3-3 & 3-4 & 3-5
+\end{tblr}
+\end{demohigh}
+
+By \PP{tikz} intersection syntax, \fakeverb{h<i>-|v<j>} is
+the instersection point of \KK{horder{<i>}} and \KK{vborder{<j>}}.
+
+It is rather complicated to add full support for drawing \PP{tikz} pictures on long tables.
+At present, the support is limited: only cell nodes are available for multi-page long tables.
+In writng drawing code,
+users are responsible for making sure the elements to draw are actually in current page table.
+These public variables might come in handy: \CC{\lTblrRowHeadInt}, \CC{\lTblrRowFootInt},
+\CC{\lTblrTablePageInt}, \CC{\lTblrRowFirstInt}, \CC{\lTblrRowLastInt}
+(they are described in Section~\ref{sect:pubvar}). Here is an example:
+
+\ExplSyntaxOn
+\cs_generate_variant:Nn \clist_map_inline:nn {e}
+\cs_new_protected:Npn \mymagic #1
+  {
+    \clist_map_inline:en {\ExpTblrChildClass {#1}}
+      {
+        \bool_lazy_and:nnT
+          { \int_compare_p:n {\lTblrRowFirstInt <= \use_i:nn ##1} }
+          { \int_compare_p:n {\lTblrRowLastInt >= \use_i:nn ##1} }
+          { \exp_args:Noo \mymagicfill {\use_i:nn ##1} {\use_ii:nn ##1} }
+      }
+  }
+\ExplSyntaxOff
+\newcommand\mymagicfill[2]{
+  \fill[teal7,rounded corners=8pt] (#1-#2.north west) rectangle (#1-#2.south east);
+}
+\begin{tblrtikzbelow}
+  \mymagic{magic}
+\end{tblrtikzbelow}%
+\begin{longtblr}[
+  caption = Long Table Tikz
+]{
+  rowhead=1, rowfoot=1, hlines, vlines, colspec={*{5}{X[r]}}
+}
+  Head1 & Head2 & Head3 & Head4 & Head5 \\
+    2-1 &   2-2 & \SetChild{class=magic}2-3 & 2-4 & 2-5 \\
+    3-1 &   3-2 &   3-3 &   3-4 &   3-5 \\
+    4-1 &   4-2 &   4-3 & \SetChild{class=magic}4-4 &4-5 \\
+    5-1 &   5-2 &   5-3 &   5-4 &   5-5 \\
+    6-1 &   6-2 &   6-3 &   6-4 &   6-5 \\
+    7-1 &   \SetChild{class=magic}7-2 & 7-3 & 7-4 & 7-5 \\
+    8-1 &   8-2 &   8-3 &   8-4 & \SetChild{class=magic}8-5 \\
+    9-1 &   9-2 &   9-3 &   9-4 &   9-5 \\
+    0-1 &   0-2 & \SetChild{class=magic}0-3 & 0-4 & 0-5 \\
+    1-1 &   1-2 &   1-3 &   1-4 &   1-5 \\
+   \SetChild{class=magic}2-1 & 2-2 & 2-3 & 2-4 & 2-5 \\
+    3-1 &   3-2 &   3-3 &   3-4 &   3-5 \\
+    4-1 &   4-2 &   4-3 & \SetChild{class=magic}4-4 &4-5 \\
+    5-1 &   5-2 &   5-3 &   5-4 &   5-5 \\
+  Foot1 & Foot2 & Foot3 & Foot4 & Foot5 \\
+\end{longtblr}
+
+\begin{codehigh}[language=latex/latex3]
+\ExplSyntaxOn
+\cs_generate_variant:Nn \clist_map_inline:nn {e}
+\cs_new_protected:Npn \mymagic #1
+  {
+    \clist_map_inline:en {\ExpTblrChildClass {#1}}
+      {
+        \bool_lazy_and:nnT
+          { \int_compare_p:n {\lTblrRowFirstInt <= \use_i:nn ##1} }
+          { \int_compare_p:n {\lTblrRowLastInt >= \use_i:nn ##1} }
+          { \exp_args:Noo \mymagicfill {\use_i:nn ##1} {\use_ii:nn ##1} }
+      }
+  }
+\ExplSyntaxOff
+\newcommand\mymagicfill[2]{
+  \fill[teal7,rounded corners=8pt] (#1-#2.north west) rectangle (#1-#2.south east);
+}
+\begin{tblrtikzbelow}
+  \mymagic{magic}
+\end{tblrtikzbelow}%
+\begin{longtblr}[
+  caption = Long Table Tikz
+]{
+  rowhead=1, rowfoot=1, hlines, vlines, colspec={*{5}{X[r]}}
+}
+  Head1 & Head2 & Head3 & Head4 & Head5 \\
+    2-1 &   2-2 & \SetChild{class=magic}2-3 & 2-4 & 2-5 \\
+    3-1 &   3-2 &   3-3 &   3-4 &   3-5 \\
+    4-1 &   4-2 &   4-3 & \SetChild{class=magic}4-4 &4-5 \\
+    5-1 &   5-2 &   5-3 &   5-4 &   5-5 \\
+    6-1 &   6-2 &   6-3 &   6-4 &   6-5 \\
+    7-1 &   \SetChild{class=magic}7-2 & 7-3 & 7-4 & 7-5 \\
+    8-1 &   8-2 &   8-3 &   8-4 & \SetChild{class=magic}8-5 \\
+    9-1 &   9-2 &   9-3 &   9-4 &   9-5 \\
+    0-1 &   0-2 & \SetChild{class=magic}0-3 & 0-4 & 0-5 \\
+    1-1 &   1-2 &   1-3 &   1-4 &   1-5 \\
+   \SetChild{class=magic}2-1 & 2-2 & 2-3 & 2-4 & 2-5 \\
+    3-1 &   3-2 &   3-3 &   3-4 &   3-5 \\
+    4-1 &   4-2 &   4-3 & \SetChild{class=magic}4-4 &4-5 \\
+    5-1 &   5-2 &   5-3 &   5-4 &   5-5 \\
+  Foot1 & Foot2 & Foot3 & Foot4 & Foot5 \\
+\end{longtblr}
+\end{codehigh}
+
 \section{Library \texttt{varwidth}}
 
-To build a nice table, \verb!tabularray! need to measure the widths of cells.
-By default, it uses \verb!\hbox! to measure the sizes.
+To build a nice table, \PP{tabularray} need to measure the widths of cells.
+By default, it uses \CC{\hbox} to measure the sizes.
 This causes an error if a cell contains some vertical material, such as lists or display maths.
 
-With \verb!\UseTblrLibrary{varwidth}! in the preamble of the document,
-\verb!tabularray! will load \verb!varwidth! package,
-and add a new inner specification \verb!measure! for tables.
-After setting \verb!measure=vbox!, it will use \verb!\vbox! to measure cell widths.
+With \PP{varwidth} library, \PP{tabularray} will load \PP{varwidth} package,
+add a new inner specification \K{measure}, and set \KV{measure=vbox}
+so that it will use \CC{\vbox} to measure cell widths.
 
 \begin{demohigh}
 \begin{tblr}{hlines,measure=vbox}
@@ -2985,9 +3277,9 @@
 \end{tblr}
 \end{demohigh}
 
-From version 2022A (2022-03-01), you can remove extra space above and below lists,
-by adding option \verb!stretch=-1!.
-The following example also needs \verb!enumitem! package and its \verb!nosep! option:
+From version 2022A, you can remove extra space above and below lists,
+by adding option \KV{stretch=-1}.
+The following example also needs \PP{enumitem} package and its \K{nosep} option:
 
 {\centering\begin{tblr}{
   hlines,vlines,rowspec={Q[l,t]Q[l,b]},
@@ -3022,19 +3314,48 @@
 \end{codehigh}
 %\end{demohigh}
 
-Note that option \verb!stretch=-1! also removes struts from cells, therefore it may not work well
-in \verb!tabularray! environments with \verb!rowsep=0pt!, such as
-\verb!booktabs!/\verb!longtabs!/\verb!talltabs! environments from \verb!booktabs! library.
+Note that option \KV{stretch=-1} also removes struts from cells, therefore it may not work well
+in \PP{tabularray} environments with \KV{rowsep=0pt}, such as
+\EE{booktabs}/\EE{longtabs}/\EE{talltabs} environments from \LL{booktabs} library.
 
+From version 2025A, \KK{measure} key also accepts an \emph{experimental} \VV{vstore} value.
+With \KV{measure=vstore}, \PP{tabularray} also measures cells with \CC{\vbox},
+but it will store the boxes for later use,
+which is necessary to make \CC{\lTblrMeasuringBool} status correct.
+
+From version 2025A, the setting of \KK{measure} key also applies to subtables.
+
 \section{Library \texttt{zref}}
 
-From version 2022D, you can load \verb!zref! library
-to make \verb!\zref! and \verb!longtblr! work together.
+From version 2022D, you can load \LL{zref} library
+to make \CC{\zref} and \EE{longtblr} work together.
 
 \chapter{Tips and Tricks}
 
-\section{Control Horizontal Alignment}
+\section{Default rule widths and colors}
 
+From version 2025A, default hrule and vrule widths are stored in variables
+\CC{\lTblrDefaultHruleWidthDim} and \CC{\lTblrDefaultVruleWidthDim} respectively,
+and default hrule and vrule colors are stored in variables \CC{\lTblrDefaultHruleColorTl}
+and \CC{\lTblrDefaultVruleColorTl} respectively. Here is an example:
+
+\begin{demohigh}
+\setlength\lTblrDefaultHruleWidthDim{1pt}%
+\setlength\lTblrDefaultVruleWidthDim{2pt}%
+\renewcommand\lTblrDefaultHruleColorTl{blue5}%
+\renewcommand\lTblrDefaultVruleColorTl{red5}%
+\begin{tblr}{
+  hlines, hline{2} = {wd=2pt, fg=cyan5},
+  vlines, vline{2} = {wd=1pt, fg=green5}
+}
+  Alpha   & Beta  & Gamma  \\
+  Epsilon & Zeta  & Eta    \\
+  Iota    & Kappa & Lambda \\
+\end{tblr}
+\end{demohigh}
+
+\section{Control horizontal alignment}
+
 You can control horizontal alignment of cells in \texttt{tabularray} with
 \href{https://www.ctan.org/pkg/ragged2e}{\texttt{ragged2e}} package,
 by redefining some of the following commands:
@@ -3049,20 +3370,19 @@
 Please read the documentation of \texttt{ragged2e} package for more details of
 their alignment commands.
 
-\section{Use Safe Verbatim Commands}%
+\section{Use safe verbatim commands}%
 \label{sec:fakeverb}
 
-Due to the limitation of TeX, even if you have passed \texttt{verb} option to a
-\texttt{tabularray} table, you still could not use some special characters in a
-\verb!\verb! command. As an replacement, you may use \verb|\fakeverb| command from \href{https://www.ctan.org/pkg/codehigh}{\texttt{codehigh}} package.
+Due to the limitations of TeX,
+we are not able to make \CC{\verb} command behave well inside \PP{tabularray} tables.
+As a replacement, you may use \CC{\fakeverb} command from \href{https://www.ctan.org/pkg/codehigh}{\PP{codehigh}} package.
 
-The \verb|\fakeverb| command will remove the backslashes in the following control symbols before
-typesetting its content: \fakeverb{\\\\}, \fakeverb{\\\{}, \fakeverb{\\\}}, \fakeverb{\\\#}, \fakeverb{\\\^} and \texttt{\textbackslash\textvisiblespace}, \fakeverb{\\\%}.
-Also the argument of \verb|\fakeverb| command need to be enclosed with curly braces.
-Therefore it could be safely used inside \verb|tabularray| tables and other LaTeX commands.
+The \CC{\fakeverb} command will remove the backslashes in the following control symbols before
+typesetting its content: \CC{\\\\}, \CC{\\\{}, \CC{\\\}}, \CC{\\\#}, \CC{\\\^} and \texttt{\textbackslash\textvisiblespace}, \CC{\\\%}.
+Also the argument of \CC{\fakeverb} command need to be enclosed with curly braces.
+Therefore it could be safely used inside \PP{tabularray} tables and other LaTeX commands.
 
-Here is an example of using \verb!\fakeverb! commands inside a \verb|tblr| environment
-(you don't need \texttt{verb} option to use \verb!\fakeverb!):
+Here is an example of using \CC{\fakeverb} commands inside a \EE{tblr} environment:
 
 \begin{demohigh}
 \begin{tblr}{hlines}
@@ -3072,48 +3392,366 @@
 \end{tblr}
 \end{demohigh}
 
-In the above example, balanced curly braces and control words (such as \verb!\bfseries!)
+In the above example, balanced curly braces and control words (such as \CC{\bfseries})
 need not to be escaped---only several special characters need to be escaped.
-Please read the documentation of \texttt{codehigh} package for more details of
-\verb|\fakeverb| commands.%
-\footnote{By the way, \fakeverb{\EscVerb} command from
-\href{https://www.ctan.org/pkg/fvextra}{\texttt{fvextra}} package is similar to
-\fakeverb{\fakeverb} command, but with \fakeverb{\EscVerb} you need to escape every control word.}
+Please read the documentation of \PP{codehigh} package for more details of
+\CC{\fakeverb} commands.%
+\footnote{By the way, \CC{\EscVerb} command from
+\href{https://www.ctan.org/pkg/fvextra}{\PP{fvextra}} package is similar to
+\CC{\fakeverb} command, but with \CC{\EscVerb} you need to escape every control word.}
 
+\section{Blank lines around cells}
+
+In \PP{tabularray} tables, there could be a blank line before a cell, after a cell,
+or between table commands and cell text. Here is an example:
+
+\begin{demohigh}
+\begin{tblr}{rl}
+
+\hline
+
+  One
+
+  &
+
+  Two
+
+  \\
+
+\hline
+
+  Three
+
+  &
+
+  Four
+
+  \\
+
+\hline
+
+\end{tblr}
+\end{demohigh}
+
+But more blank lines are not supported.
+Therefore putting more than one blank line at any of these positions may cause wrong result.
+
+
+\chapter{Experimental Interfaces}
+\label{chap:exp}
+
+\mywarning{The interfaces in this chapter
+(and other undocumented public interfaces even if mentioned in the changelog) should be seen as \emph{experimental}
+and are likely to change in future releases, if necessary. Don’t use them in important documents.}
+
+\section{Experimental public key paths}
+
+In version 2025A, all \PP{tabularray} key paths were cleaned up as follows:
+\begin{itemize}[nosep]
+  \item \KP{tabularray/table/inner} (from \KP{tblr}): for inner specifications.
+  \item \KP{tabularray/table/outer} (from \KP{tblr-outer}): for outer specifications.
+  \item \KP{tabularray/column/inner} (from \KP{tblr-column}): for column specifications.
+  \item \KP{tabularray/row/inner} (from \KP{tblr-row}): for row specifications.
+  \item \KP{tabularray/cell/inner} (from \KP{tblr-cell-spec}): for cell specifications.
+  \item \KP{tabularray/cell/outer} (from \KP{tblr-cell-span}): for cell spanning specifications.
+  \item \KP{tabularray/hline/inner} (from \KP{tblr-hline}): for hline specifications.
+  \item \KP{tabularray/vline/inner} (from \KP{tblr-vline}): for vline specifications.
+  \item \KP{tabularray/hborder/inner} (from \KP{tblr-hborder}) for hborder specifications.
+  \item \KP{tabularray/vborder/inner} (from \KP{tblr-vborder}) for vborder specifications.
+\end{itemize}
+An advanced user or package writer can use \CC{\DeclareKeys} and \CC{\SetKeys} commands
+(provided by LaTeX format) to declare new keys and apply key-value lists, respectively.
+
+The key paths are quite long, therefore \PP{tabularray} provides two shortcut commands
+\CC{\DeclareTblrKeys} and \CC{\SetTblrKeys}:
+
+\begin{codehigh}
+\DeclareTblrKeys{<path>}{<keyvals>} = \DeclareKeys[tabularray/<path>]{<keyvals>}
+\SetTblrKeys{<path>}{<keyvals>} = \SetKeys[tabularray/<path>]{<keyvals>}
+\end{codehigh}
+
+\section{Experimental public hook names}
+\label{sect:hook}
+
+All experimental public \PP{tabularray} hook names provided by \LL{hook} library are as follows:
+\begin{itemize}[nosep]
+  \item \HP{tabularray/trial/before}: hook before trial typesetting.
+  \item \HP{tabularray/trial/after}: hook after trial typesetting.
+  \item \HP{tabularray/table/before}: hook before building the whole table.
+  \item \HP{tabularray/table/after}: hook after building the whole table.
+  \item \HP{tabularray/row/before}: hook before typesetting a table row.
+  \item \HP{tabularray/row/after}: hook after typesetting a table row.
+  \item \HP{tabularray/cell/before}: hook before typesetting a table cell.
+  \item \HP{tabularray/cell/after}: hook after typesetting a table cell.
+\end{itemize}
+An advanced user or package writer can use \CC{\AddToHook} and \CC{\AddToHookNext} commands
+(provided by LaTeX format) to inject code to \PP{tabularray} tables.
+
+The hook names are quite long, therefore \PP{tabularray} provides two shortcut commands
+\CC{\AddToTblrHook} and \CC{\AddToTblrHookNext}:
+
+%\begin{codehigh}
+%\AddToTblrHook{<name>}[<label>]{<code>}=\AddToHook{tabularray/<name>}[<label>]{<code>}
+%\AddToTblrHookNext{<name>}{<code>}=\AddToHookNext{tabularray/<name>}{<code>}
+%\end{codehigh}
+\begin{codehigh}
+\AddToTblrHook{<name>}{<code>} = \AddToHook{tabularray/<name>}{<code>}
+\AddToTblrHookNext{<name>}{<code>} = \AddToHookNext{tabularray/<name>}{<code>}
+\end{codehigh}
+
+\section{Experimental public variables}
+\label{sect:pubvar}
+
+This variable can be used to change page break settings for multirow cells:
+\begin{itemize}[nosep]
+  \item \CC{\lTblrCellBreakBool}: whether to allow page breaks in the middle of multirow cells.
+\end{itemize}
+
+This variable is always available throughout the whole typesetting process of tables:
+\begin{itemize}[nosep]
+  \item \CC{\lTblrMeasuringBool}: if \PP{tabularray} is doing trial typesetting.
+\end{itemize}
+You need to make sure \KV{measure=vstore} to make \CC{\lTblrMeasuringBool} correct.
+
+This variable is available before building every table:
+\begin{itemize}[nosep]
+  \item \CC{\lTblrPortraitTypeTl}: table type (\VV{short}, \VV{tall} or \VV{long}).
+\end{itemize}
+
+These variables are updated in building long tables:
+\begin{itemize}[nosep]
+  \item \CC{\lTblrRowHeadInt}: total number of head rows.
+  \item \CC{\lTblrRowFootInt}: total number of foot rows.
+  \item \CC{\lTblrTablePageInt}: index number of current page table.
+  \item \CC{\lTblrRowFirstInt}: first row number in row body of current page table.
+  \item \CC{\lTblrRowLastInt}: last row number in row body of curent page table.
+\end{itemize}
+
+These variables are updated by default before building every cell:
+\begin{itemize}[nosep]
+  \item \CC{\lTblrCellRowSpanInt}: how many rows are spanned by current cell.
+  \item \CC{\lTblrCellColSpanInt}: how many columns are spanned by current cell.
+  \item \CC{\lTblrCellOmittedBool}: if current cell is spanned by another cell.
+  \item \CC{\lTblrCellBackgroundTl}: background color of current cell.
+\end{itemize}
+
+These variables are updated by \LL{html} library before building every cell:
+%(you need to write \CC{\UseTblrLibrary{html}} first):
+\begin{itemize}[nosep]
+  \item \CC{\lTblrCellAboveBorderStyleTl}
+  \item \CC{\lTblrCellAboveBorderWidthDim}
+  \item \CC{\lTblrCellAboveBorderColorTl}
+  \item \CC{\lTblrCellBelowBorderStyleTl}
+  \item \CC{\lTblrCellBelowBorderWidthDim}
+  \item \CC{\lTblrCellBelowBorderColorTl}
+  \item \CC{\lTblrCellLeftBorderStyleTl}
+  \item \CC{\lTblrCellLeftBorderWidthDim}
+  \item \CC{\lTblrCellLeftBorderColorTl}
+  \item \CC{\lTblrCellRightBorderStyleTl}
+  \item \CC{\lTblrCellRightBorderWidthDim}
+  \item \CC{\lTblrCellRightBorderColorTl}
+\end{itemize}
+In the above, \TT{BorderStyle}, \TT{BorderWidth}, \TT{BorderColor} are similar to
+\K{border-style}, \K{border-width}, \K{border-color} in HTML/CSS, respectively.
+\TT{BorderStyle} and \TT{BorderColor} are empty by default.
+
+\section{New child indexers and selectors}
+\label{sect:newchild}
+
+\subsection{One dimensional indexers and selectors}
+
+You can define new child indexers with \CC{\NewTblrChildIndexer} command.
+As an example, the following is the simplified definition of \CI{Z} indexer:
+\begin{codehigh}[language=latex/latex3]
+\ExplSyntaxOn
+\NewTblrChildIndexer {Z} [1] [1]
+  {
+    \tl_set:Ne \lTblrChildIndexTl { \int_eval:n {\lTblrChildTotalInt + 1 - #1} {1} }
+  }
+\ExplSyntaxOff
+\end{codehigh}
+In the definition, you can use \CC{\lTblrChildTotalInt} which is the total number of children.
+And you only need to store the result index \fakeverb{<i>} in \CC{\lTblrChildIndexTl}.
+The name of an indexer \emph{must} consist of letters and start with an uppercase letter.
+
+You can define new child selectors with \CC{\NewTblrChildSelector} command.
+As an example, the following is the simplified definition of \CI{odd} selector:
+\begin{codehigh}[language=latex/latex3]
+\ExplSyntaxOn
+\NewTblrChildSelector {odd} [1] [1]
+  {
+    \clist_set:Ne \lTblrChildClist { {#1} {2} {\int_use:N \lTblrChildTotalInt} }
+  }
+\ExplSyntaxOff
+\end{codehigh}
+In the definition, you can use \CC{\lTblrChildTotalInt} which is the total number of children.
+And you only need to store the result indexes in \CC{\lTblrChildClist}.
+When some indexes form an arithmetic sequence,
+you can simplify them as \fakeverb{{<start>}{<step>}{<end>}}.
+The name of a selector \emph{must} consist of letters and start with a lowercase letter.
+
+\subsection{Two dimensional indexers and selectors}
+
+When selecting cells, you may need two dimensional indexers and selectors.
+You can also define new two dimensional child indexers with \CC{\NewTblrChildIndexer} command,
+and two dimensional child selectors with \CC{\NewTblrChildSelector} command.
+
+In the definitions, you can use \CC{\lTblrChildHtotalInt}
+which is the total number of horizontal children (rows), and \CC{\lTblrChildVtotalInt}
+which is the total number of vertical children (columns).
+
+You also need to store the result index \fakeverb{{<i>}{<j>}} in \CC{\lTblrChildIndexTl}
+in defining two dimensional child indexers.
+Similarly you also need to store the result indexes in \CC{\lTblrChildClist}
+in defining two dimensional child selectors.
+
+\subsection{Child ids and classes}
+
+When the table is long, it is clumsy to select children with indexes, positive or negative.
+In version 2025A, \PP{tabularray} borrows ideas of ids and classes from HTML/CSS.
+With table command \CC{\SetChild}, you can mark a hborder/vborder/row/column/cell with
+an id or class, and use it in inner specifications.
+
+The \CC{\SetChild} command accepts key-value input:
+\begin{spectblr}[
+  caption = {Key-value input in \fakeverb{\\SetChild} command} %FIXME
+]{}
+  Input & Description \\
+  \KV{id=Hello}  & create a child indexer \TT{Hello} which is an index \TT{{<i>}{<j>}} \\
+  \KV{idh=Hello} & create a child indexer \TT{Helloh} which is a horizontal index \TT{<i>} \\
+  \KV{idv=Hello} & create a child indexer \TT{Hellov} which is a vertical index  \TT{<j>} \\
+  \KV{id*=Hello} & create all of the above three child indexers \\
+  \KV{class=world}  & create a child selector \TT{world} which is a list of indexes \TT{{<i>}{<j>}} \\
+  \KV{classh=world} & create a child selector \TT{worldh} which is a list of horizontal indexes \TT{<i>} \\
+  \KV{classv=world} & create a child selector \TT{worldv} which is a list of vertical indexes \TT{<j>} \\
+  \KV{class*=world} & create all of the above three child selectors
+\end{spectblr}
+
+The following is an example of child ids
+(every id name must start with uppercase letter since it creates a child indexer):
+
+\begin{demohigh}
+\begin{tblr}{
+  hline{1,Z},
+  row{Barh,Quxh} = {bg=azure7},
+  column{Bazv,Quxv} = {fg=red3},
+  cell{Foo,Qux} = {cmd=\fbox}
+}
+  1 & 2 & 3 & \SetChild{id=Foo}  4 & 5 & 6 & 7 & 8 \\
+  2 & 2 & \SetChild{idh=Bar} 3 & 4 & 5 & 6 & 7 & 8 \\
+  3 & \SetChild{idv=Baz} 2 & 3 & 4 & 5 & 6 & 7 & 8 \\
+  \SetChild{id*=Qux} 4 & 2 & 3 & 4 & 5 & 6 & 7 & 8
+\end{tblr}
+\end{demohigh}
+
+The following is an example of child classes
+(every class name must start with lowercase letter since it creates a child selector):
+
+\begin{demohigh}
+\begin{tblr}{
+  hline{1,Z},
+  row{fooh} = {bg=azure7},
+  column{barv} = {fg=red3},
+  cell{baz} = {cmd=\fbox}
+}
+  1 & 2 & \SetChild{classh=foo} 3 & 4 & 5 & 6 & 7 \\
+  2 & \SetChild{classv=bar} 2 & 3 & 4 & 5 & 6 & 7 \\
+  \SetChild{class=baz} 3  & 2 & 3 & 4 & 5 & 6 & 7 \\
+  4 & 2 & 3 & 4 & \SetChild{class=baz}  5 & 6 & 7 \\
+  5 & 2 & 3 & 4 & 5 & \SetChild{classh=foo} 6 & 7 \\
+  6 & 2 & 3 & 4 & 5 & 6 & \SetChild{classv=bar} 7
+\end{tblr}
+\end{demohigh}
+
+Since \CC{\SetChild} commands need to be extracted first
+before parsing inner specifications, they \emph{must} be put
+at the beginning of cells, before other table commands such as \CC{\hline}.
+Therefore it conflicts with syntaxes \fakeverb{\\\\[<dimen>]} and \fakeverb{\\\\*}.
+They can be replaced with \fakeverb{\\\\\SetRow{belowsep+=<dimen>}}
+and \fakeverb{\\\\\nopagebreak} respectively,
+so that \CC{\SetChild} can be inserted in the middle:
+\begin{demohigh}
+\begin{tblr}{cell{Foo,Bar} = {fg=red3}}
+\hline
+  1 & 2 & 3 & 4 & 5 & 6 & 7 \\
+\SetChild{id=Foo}\SetRow{belowsep+=5pt}\hline
+  2 & 2 & 3 & 4 & 5 & 6 & 7 \\
+\SetChild{id=Bar}\nopagebreak\hline
+  3 & 2 & 3 & 4 & 5 & 6 & 7 \\
+\hline
+\end{tblr}
+\end{demohigh}
+
+Only one \CC{\SetChild} command in each cell is supported.
+But you can create multiple ids or classes with single \CC{\SetChild} command.
+
+In drawing \PP{tikz} pictures on tables (see Section~\ref{sect:tikz}),
+you may want to get the value of a child id or class with \CC{\ExpTblrChildId} or \CC{\ExpTblrChildClass}.
+These two commands are fully expandable.
+
 \chapter{History and Future}
 
-\section{The Future}
+\section{The future}
 
-Starting from 2022, except for hotfix releases for critical bugs,
-every new release will be published on the first day of some month.
-You may watch the milestones page for the scheduled dates of upcoming releases
-and their changes:\newline
-\centerline{\url{https://github.com/lvjr/tabularray/milestones}}
+As a policy, \PP{tabularray} can support at most four TeX Live releases with latest updates.
+For example, \PP{tabularray} releases published in \the\year{} could be used in TeX Live
+\inteval{\year-3}--\the\year{} with latest updates.
 
 To make the upcoming releases more stable, you are very welcome to test the latest package file
-in the repository. To test it, you only need to download the following \verb!tabularray.sty!
+in the repository. To test it, you only need to download the following \FF{tabularray.sty}
 and put it into the folder of your TeX documents:\newline
 \centerline{\url{https://github.com/lvjr/tabularray/raw/main/tabularray.sty}}
 
-\section{The History}
+\section{The history}
 
-The change log of \verb!tabularray! package will be updated on the wiki page:\newline
+The change log of \PP{tabularray} package will be updated on the wiki page:\newline
 \centerline{\url{https://github.com/lvjr/tabularray/wiki/ChangeLog}}
 
-In version 2023A, inner key \verb!verb! was marked as obsolete,
-and it will be removed in the future. But don't worry too much,
-because you can stick to old version by \verb!\usepackage{tabularray}[=2023-03-01]!.
+When \PP{tabularray} makes some breaking changes of \emph{stable public} interfaces in a new release,
+you will be able to roll back to previous release to make your existing documents unaffected.
 
+Normally you don't know when there will be a new breaking release.
+To keep your old documents as they are, you may add the date of current release
+to the last optional argument of \CC{\usepackage} in loading \PP{tabularray},
+such as \CC{\usepackage{tabularray}[=2024-02-16]}.
+
+\subsection{Important changes in version 2025A}
+
+In version 2025A, there were several important changes:
+
+\begin{itemize}[nosep]
+  \item Inner key \KK{verb} (deprecated before) was removed;
+        it is better to use \CC{\fakeverb} command.
+  \item Support for end index in \CS{odd} and \CS{even} selectors was removed;
+        it is better to use \CS{every} selector.
+  \item Page breaks in the middle of multirow cells were disabled.%;
+        %use \CC{\lTblrCellBreakBool} to change it.
+  \item \CC{\DefTblrTemplate} was deprecated in favor of \CC{\DeclareTblrTemplate}.
+  \item \CC{\NewColumnType} was deprecated in favor of \CC{\NewTblrColumnType}.
+  \item \CC{\NewRowType} was deprecated in favor of \CC{\NewTblrRowType}.
+  \item \CC{\NewColumnRowType} was deprecated in favor of \CC{\NewTblrColumnRowType}.
+  \item \CC{\NewDashStyle} was deprecated in favor of \CC{\NewTblrDashStyle}.
+  \item \CC{\NewChildSelector} was deprecated in favor of \CC{\NewTblrChildSelector}.
+  \item \CC{\NewTableCommand} was deprecated in favor of \CC{\NewTblrTableCommand}.
+ %\item \CC{\NewContentCommand} was deprecated in favor of \CC{\NewTblrContentCommand}.
+  \item \CC{\tablewidth} was deprecated in favor of \CC{\lTblrTableWidthDim}.
+ %\item \CC{\g_tblr_level_int} was deprecated in favor of \CC{\gTblrLevelInt}.
+\end{itemize}
+
+For your old documents, you can still rollback to version 2024 by \CC{\usepackage{tabularray}[=v2024]}.
+
+\subsection{Important changes in version 2022A}
+
 In version 2022A, there were several breaking changes:
 
 \begin{itemize}[nosep]
-  \item \verb!\multicolumn! command was removed; it is better to use \verb!\SetCell! command.
-  \item \verb!\multirow! command was removed; it is better to use \verb!\SetCell! command.
-  \item \verb!\firsthline! command was removed; it is better to use \verb!baseline=T! option.
-  \item \verb!\lasthline! command was removed; it is better to use \verb!baseline=B! option.
+  \item \CC{\multicolumn} command was removed; it is better to use \CC{\SetCell} command.
+  \item \CC{\multirow} command was removed; it is better to use \CC{\SetCell} command.
+  \item \CC{\firsthline} command was removed; it is better to use \KV{baseline=T} option.
+  \item \CC{\lasthline} command was removed; it is better to use \KV{baseline=B} option.
 \end{itemize}
 
-For your old documents, you can still rollback to version 2021 by \verb!\usepackage{tabularray}[=v2021]!.
+For your old documents, you can still rollback to version 2021 by \CC{\usepackage{tabularray}[=v2021]}.
 
 \chapter{The Source Code}
 

Added: trunk/Master/texmf-dist/tex/latex/tabularray/tabularray-2024.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/tabularray/tabularray-2024.sty	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/latex/tabularray/tabularray-2024.sty	2025-03-11 20:06:02 UTC (rev 74569)
@@ -0,0 +1,7444 @@
+%%% % -*- coding: utf-8 -*-
+%%% ----------------------------------------------------------------------------
+%%% Tabularray: Typeset tabulars and arrays with LaTeX3
+%%% Copyright : 2021-2023 (c) Jianrui Lyu <tolvjr at 163.com>
+%%% Repository: https://github.com/lvjr/tabularray
+%%% License   : The LaTeX Project Public License 1.3c
+%%% ----------------------------------------------------------------------------
+
+%%% --------------------------------------------------------
+%%> \section{Scratch Variables and Function Variants}
+%%% --------------------------------------------------------
+
+%% \DeclareRelease and \DeclareCurrentRelease are added in LaTeX 2018-04-01
+\NeedsTeXFormat{LaTeX2e}[2018-04-01]
+\providecommand\DeclareRelease[3]{}
+\providecommand\DeclareCurrentRelease[2]{}
+\DeclareRelease{v2021}{2021-01-01}{tabularray-2021.sty}
+\DeclareCurrentRelease{}{2022-01-01}
+
+\RequirePackage{expl3}
+\ProvidesExplPackage{tabularray}{2024-02-16}{2024A}
+  {Typeset tabulars and arrays with LaTeX3}
+
+%% \IfFormatAtLeastTF, xparse and lthooks are added in LaTeX 2020-10-01
+%% Note that \@ifl at t@r or \@ifpackagelater means 'this date or later'
+\msg_new:nnn { tabularray } { latex-too-old }
+  {
+    Your ~ LaTeX ~ release ~ is ~ too ~ old. \\
+    Please ~ update ~ it ~ to ~ 2020-10-01 ~ first.
+  }
+\@ifl at t@r\fmtversion{2020-10-01}{}{
+  %% Support TeX Live 2020 on Overleaf
+  \msg_warning:nn { tabularray } { latex-too-old }
+  \usepackage{xparse}
+}
+
+\AtBeginDocument{
+  \@ifpackageloaded{xcolor}{\RequirePackage{ninecolors}}{}
+  \@ifpackageloaded{hyperref}{
+    \newenvironment{tblrNoHyper}{\NoHyper}{\endNoHyper}
+  }{
+    \newenvironment{tblrNoHyper}{}{}
+  }
+}
+
+\NewDocumentCommand \TblrParboxRestore { } { \@parboxrestore }
+
+\NewDocumentCommand \TblrAlignBoth { }
+  {
+    \let \\ = \@normalcr
+    \leftskip = \z at skip
+    \@rightskip = \z at skip
+    \rightskip = \@rightskip
+    \parfillskip = \@flushglue
+  }
+
+\NewDocumentCommand \TblrAlignLeft { } { \raggedright }
+
+\NewDocumentCommand \TblrAlignCenter { } { \centering }
+
+\NewDocumentCommand \TblrAlignRight { } { \raggedleft }
+
+\cs_set_eq:NN \TblrNewPage \newpage
+
+%% Note that \cs_if_exist:NTF doesn't treat \relax as an existing command.
+%% Therefore we define our \__tblr_cs_if_defined:NTF here.
+\prg_set_conditional:Npnn \__tblr_cs_if_defined:N #1 { p, T, F, TF }
+  {
+    %% \if_cs_exist:N = \ifdefined in eTeX
+    \if_cs_exist:N #1
+      \prg_return_true:
+    \else:
+      \prg_return_false:
+    \fi:
+  }
+\prg_set_conditional:Npnn \__tblr_cs_if_defined:c #1 { p, T, F, TF }
+  {
+    %% \if_cs_exist:w = \ifcsname in eTeX
+    \if_cs_exist:w #1 \cs_end:
+      \prg_return_true:
+    \else:
+      \prg_return_false:
+    \fi:
+  }
+
+\cs_generate_variant:Nn \msg_error:nnnn { nnVn }
+\cs_generate_variant:Nn \prop_item:Nn { Ne, NV }
+\cs_generate_variant:Nn \prop_put:Nnn { Nxn, Nxx, NxV }
+\cs_generate_variant:Nn \regex_replace_all:NnN { NVN }
+\cs_generate_variant:Nn \seq_map_indexed_inline:Nn { cn }
+\cs_generate_variant:Nn \tl_const:Nn { ce }
+\cs_generate_variant:Nn \tl_log:n { x }
+\cs_generate_variant:Nn \tl_gput_right:Nn { Nf }
+\cs_generate_variant:Nn \tl_put_left:Nn { Nv }
+\prg_generate_conditional_variant:Nnn \clist_if_in:Nn { Nx } { TF }
+\prg_generate_conditional_variant:Nnn \prop_if_in:Nn { c } { T }
+\prg_generate_conditional_variant:Nnn \regex_match:Nn { NV } { TF }
+\prg_generate_conditional_variant:Nnn \str_if_eq:nn { xn } { TF }
+\prg_generate_conditional_variant:Nnn \tl_if_eq:nn { en } { T, TF }
+\prg_generate_conditional_variant:Nnn \tl_if_head_eq_catcode:nN { VN } { TF }
+\prg_generate_conditional_variant:Nnn \tl_if_head_eq_meaning:nN { VN } { T, TF }
+
+\tl_new:N  \l__tblr_a_tl
+\tl_new:N  \l__tblr_b_tl
+\tl_new:N  \l__tblr_c_tl
+\tl_new:N  \l__tblr_d_tl
+\tl_new:N  \l__tblr_e_tl
+\tl_new:N  \l__tblr_f_tl
+\tl_new:N  \l__tblr_h_tl
+\tl_new:N  \l__tblr_i_tl  % for row index
+\tl_new:N  \l__tblr_j_tl  % for column index
+\tl_new:N  \l__tblr_k_tl
+\tl_new:N  \l__tblr_n_tl
+\tl_new:N  \l__tblr_o_tl
+\tl_new:N  \l__tblr_r_tl
+\tl_new:N  \l__tblr_s_tl
+\tl_new:N  \l__tblr_t_tl
+\tl_new:N  \l__tblr_u_tl
+\tl_new:N  \l__tblr_v_tl
+\tl_new:N  \l__tblr_w_tl
+\tl_new:N  \l__tblr_x_tl
+\tl_new:N  \l__tblr_y_tl
+\int_new:N \l__tblr_a_int
+\int_new:N \l__tblr_c_int % for column number
+\int_new:N \l__tblr_r_int % for row number
+\dim_new:N \l__tblr_d_dim % for depth
+\dim_new:N \l__tblr_h_dim % for height
+\dim_new:N \l__tblr_o_dim
+\dim_new:N \l__tblr_p_dim
+\dim_new:N \l__tblr_q_dim
+\dim_new:N \l__tblr_r_dim
+\dim_new:N \l__tblr_s_dim
+\dim_new:N \l__tblr_t_dim
+\dim_new:N \l__tblr_v_dim
+\dim_new:N \l__tblr_w_dim % for width
+\box_new:N \l__tblr_a_box
+\box_new:N \l__tblr_b_box
+\box_new:N \l__tblr_c_box % for cell box
+\box_new:N \l__tblr_d_box
+
+%% Total number of tblr tables
+\int_new:N \g__tblr_table_count_int
+
+%% Some commands for horizontal alignment
+\cs_new_eq:NN \__tblr_halign_command_j: \TblrAlignBoth
+\cs_new_eq:NN \__tblr_halign_command_l: \TblrAlignLeft
+\cs_new_eq:NN \__tblr_halign_command_c: \TblrAlignCenter
+\cs_new_eq:NN \__tblr_halign_command_r: \TblrAlignRight
+
+%% Some counters for row and column numbering.
+%% We may need to restore all LaTeX counters in measuring and building cells,
+%% so we must not define these counters with \newcounter command.
+\int_zero_new:N \c at rownum
+\int_zero_new:N \c at colnum
+\int_zero_new:N \c at rowcount
+\int_zero_new:N \c at colcount
+
+%% Add missing \therownum, \thecolnum, \therowcount, \thecolcount (issue #129)
+\ProvideExpandableDocumentCommand \therownum {} { \@arabic \c at rownum }
+\ProvideExpandableDocumentCommand \thecolnum {} { \@arabic \c at colnum }
+\ProvideExpandableDocumentCommand \therowcount {} { \@arabic \c at rowcount }
+\ProvideExpandableDocumentCommand \thecolcount {} { \@arabic \c at colcount }
+
+%% Some dimensions for row and column spacing
+\dim_new:N \abovesep
+\dim_new:N \belowsep
+\dim_new:N \leftsep
+\dim_new:N \rightsep
+
+%% Some functions for lwarp to remove rules and boxes
+\cs_new:Npn \tblr_hrule_ht:n #1
+  {
+    \hrule height ~ #1 \scan_stop:
+  }
+\cs_new:Npn \tblr_vrule_wd_ht_dp:nnn #1 #2 #3
+  {
+    \vrule width ~ #1 ~ height ~ #2 ~ depth ~ #3 \scan_stop:
+  }
+\cs_new_protected:Npn \tblr_box_use:N #1
+  {
+    \box_use:N #1
+  }
+\cs_new_protected:Npn \tblr_vbox_set:Nn #1 #2
+  {
+    \vbox_set:Nn #1 {#2}
+  }
+
+%%% --------------------------------------------------------
+%%> \section{Data Structures Based on Property Lists}
+%%% --------------------------------------------------------
+
+\int_new:N \g_tblr_level_int % store table nesting level
+
+\cs_new_protected:Npn \__tblr_clear_prop_lists:
+  {
+    \prop_gclear_new:c { g__tblr_text_ \int_use:N \g_tblr_level_int _prop }
+    \prop_gclear_new:c { g__tblr_command_ \int_use:N \g_tblr_level_int _prop }
+    \prop_gclear_new:c { g__tblr_inner_ \int_use:N \g_tblr_level_int _prop }
+    \prop_gclear_new:c { g__tblr_note_ \int_use:N \g_tblr_level_int _prop }
+    \prop_gclear_new:c { g__tblr_remark_ \int_use:N \g_tblr_level_int _prop }
+    \prop_gclear_new:c { g__tblr_more_ \int_use:N \g_tblr_level_int _prop }
+    \prop_gclear_new:c { g__tblr_row_ \int_use:N \g_tblr_level_int _prop }
+    \prop_gclear_new:c { g__tblr_column_ \int_use:N \g_tblr_level_int _prop }
+    \prop_gclear_new:c { g__tblr_cell_ \int_use:N \g_tblr_level_int _prop }
+    \prop_gclear_new:c { g__tblr_hline_ \int_use:N \g_tblr_level_int _prop }
+    \prop_gclear_new:c { g__tblr_vline_ \int_use:N \g_tblr_level_int _prop }
+  }
+
+\cs_new_protected:Npn \__tblr_prop_gput:nnn #1 #2 #3
+  {
+    \prop_gput:cnn
+      { g__tblr_#1_ \int_use:N \g_tblr_level_int _prop } { #2 } { #3 }
+  }
+\cs_generate_variant:Nn \__tblr_prop_gput:nnn { nnx, nnV, nxn, nxx, nxV }
+
+\cs_new:Npn \__tblr_prop_item:nn #1 #2
+  {
+    \prop_item:cn { g__tblr_#1_ \int_use:N \g_tblr_level_int _prop } { #2 }
+  }
+\cs_generate_variant:Nn \__tblr_prop_item:nn { ne }
+
+\cs_new_protected:Npn \__tblr_prop_if_in:nnT #1
+  {
+    \prop_if_in:cnT { g__tblr_#1_ \int_use:N \g_tblr_level_int _prop }
+  }
+\cs_new_protected:Npn \__tblr_prop_if_in:nnF #1
+  {
+    \prop_if_in:cnF { g__tblr_#1_ \int_use:N \g_tblr_level_int _prop }
+  }
+\cs_new_protected:Npn \__tblr_prop_if_in:nnTF #1
+  {
+    \prop_if_in:cnTF { g__tblr_#1_ \int_use:N \g_tblr_level_int _prop }
+  }
+\prg_generate_conditional_variant:Nnn \__tblr_prop_if_in:nn { nx } { T, F, TF }
+
+\cs_new_protected:Npn \__tblr_prop_log:n #1
+  {
+    \prop_log:c { g__tblr_#1_ \int_use:N \g_tblr_level_int _prop }
+  }
+
+\cs_new_protected:Npn \__tblr_prop_map_inline:nn #1 #2
+  {
+    \prop_map_inline:cn { g__tblr_#1_ \int_use:N \g_tblr_level_int _prop } {#2}
+  }
+
+\cs_new_protected:Npn \__tblr_prop_gput_if_larger:nnn #1 #2 #3
+  {
+    \__tblr_gput_if_larger:cnn
+      { g__tblr_#1_ \int_use:N \g_tblr_level_int _prop } { #2 } { #3 }
+  }
+\cs_generate_variant:Nn \__tblr_prop_gput_if_larger:nnn { nnx, nnV, nxn, nxx, nxV }
+
+\cs_new_protected:Npn \__tblr_prop_gadd_dimen_value:nnn #1 #2 #3
+  {
+    \__tblr_gadd_dimen_value:cnn
+      { g__tblr_#1_ \int_use:N \g_tblr_level_int _prop } { #2 } { #3 }
+  }
+\cs_generate_variant:Nn \__tblr_prop_gadd_dimen_value:nnn { nnx, nnV, nxn, nxx }
+
+%% Put the dimension to the prop list only if it's larger than the old one
+
+\tl_new:N \l__tblr_put_if_larger_tl
+
+\cs_new_protected:Npn \__tblr_put_if_larger:Nnn #1 #2 #3
+  {
+    \tl_set:Nx \l__tblr_put_if_larger_tl { \prop_item:Nn #1 { #2 } }
+    \bool_lazy_or:nnT
+      { \tl_if_empty_p:N \l__tblr_put_if_larger_tl }
+      { \dim_compare_p:nNn { #3 } > { \l__tblr_put_if_larger_tl } }
+      { \prop_put:Nnn #1 { #2 } { #3 }  }
+  }
+\cs_generate_variant:Nn \__tblr_put_if_larger:Nnn { Nnx, Nxn, Nxx, NnV }
+
+\cs_new_protected:Npn \__tblr_gput_if_larger:Nnn #1 #2 #3
+  {
+    \tl_set:Nx \l__tblr_put_if_larger_tl { \prop_item:Nn #1 { #2 } }
+    \bool_lazy_or:nnT
+      { \tl_if_empty_p:N \l__tblr_put_if_larger_tl }
+      { \dim_compare_p:nNn { #3 } > { \l__tblr_put_if_larger_tl } }
+      { \prop_gput:Nnn #1 { #2 } { #3 }  }
+  }
+\cs_generate_variant:Nn \__tblr_gput_if_larger:Nnn { Nnx, Nxn, Nxx, cnn }
+
+%% Add the dimension to some key value of the prop list
+%% #1: the prop list, #2: the key, #3: the dimen to add
+
+\cs_new_protected:Npn \__tblr_add_dimen_value:Nnn #1 #2 #3
+  {
+    \prop_put:Nnx #1 { #2 } { \dim_eval:n { \prop_item:Nn #1 { #2 } + #3 } }
+  }
+\cs_generate_variant:Nn \__tblr_add_dimen_value:Nnn { cnn }
+
+\cs_new_protected:Npn \__tblr_gadd_dimen_value:Nnn #1 #2 #3
+  {
+    \prop_gput:Nnx #1 { #2 } { \dim_eval:n { \prop_item:Nn #1 { #2 } + #3 } }
+  }
+\cs_generate_variant:Nn \__tblr_gadd_dimen_value:Nnn { cnn }
+
+%%% --------------------------------------------------------
+%%> \section{Data Structures Based on Token Lists}
+%%% --------------------------------------------------------
+
+\cs_new_protected:Npn \__tblr_clear_spec_lists:
+  {
+    %\__tblr_clear_one_spec_lists:n { row }
+    %\__tblr_clear_one_spec_lists:n { column }
+    %\__tblr_clear_one_spec_lists:n { cell }
+    \__tblr_clear_one_spec_lists:n { text }
+    \__tblr_clear_one_spec_lists:n { hline }
+    \__tblr_clear_one_spec_lists:n { vline }
+    \__tblr_clear_one_spec_lists:n { outer }
+  }
+
+\cs_new_protected:Npn \__tblr_clear_one_spec_lists:n #1
+  {
+    \clist_if_exist:cTF { g__tblr_#1_ \int_use:N \g_tblr_level_int _clist }
+      {
+        \clist_map_inline:cn { g__tblr_#1_ \int_use:N \g_tblr_level_int _clist }
+          {
+            \tl_gclear:c { g__tblr_spec_ \int_use:N \g_tblr_level_int _#1_##1_tl }
+          }
+      }
+      { \clist_new:c { g__tblr_#1_ \int_use:N \g_tblr_level_int _clist } }
+  }
+
+\cs_new_protected:Npn \__tblr_spec_gput:nnn #1 #2 #3
+  {
+    \tl_gset:cn
+      { g__tblr_spec_ \int_use:N \g_tblr_level_int _#1_#2_tl } {#3}
+    \clist_gput_right:cx { g__tblr_#1_ \int_use:N \g_tblr_level_int _clist } {#2}
+  }
+\cs_generate_variant:Nn \__tblr_spec_gput:nnn { nne, nnV, nen, nee, neV }
+
+\cs_new:Npn \__tblr_spec_item:nn #1 #2
+  {
+    \tl_if_exist:cT { g__tblr_spec_ \int_use:N \g_tblr_level_int _#1_#2_tl }
+      {
+        \exp_args:Nv \exp_not:n
+          { g__tblr_spec_ \int_use:N \g_tblr_level_int _#1_#2_tl }
+      }
+  }
+\cs_generate_variant:Nn \__tblr_spec_item:nn { ne }
+
+\cs_new_protected:Npn \__tblr_spec_gput_if_larger:nnn #1 #2 #3
+  {
+    \tl_set:Nx \l__tblr_put_if_larger_tl { \__tblr_spec_item:nn {#1} {#2} }
+    \bool_lazy_or:nnT
+      { \tl_if_empty_p:N \l__tblr_put_if_larger_tl }
+      { \dim_compare_p:nNn {#3} > { \l__tblr_put_if_larger_tl } }
+      { \__tblr_spec_gput:nnn {#1} {#2} {#3} }
+  }
+\cs_generate_variant:Nn \__tblr_spec_gput_if_larger:nnn { nne, nnV, nen, nee, neV }
+
+\cs_new_protected:Npn \__tblr_spec_gadd_dimen_value:nnn #1 #2 #3
+  {
+    \__tblr_spec_gput:nne {#1} {#2}
+      { \dim_eval:n { \__tblr_spec_item:ne {#1} {#2} + #3 } }
+  }
+\cs_generate_variant:Nn \__tblr_spec_gadd_dimen_value:nnn { nne, nnV, nen, nee }
+
+\cs_new_protected:Npn \__tblr_spec_log:n #1
+  {
+    \clist_gremove_duplicates:c
+      { g__tblr_#1_ \int_use:N \g_tblr_level_int _clist }
+    \tl_log:x
+      {
+        The ~ spec ~ list ~ #1 _ \int_use:N \g_tblr_level_int
+              \space contains ~ the ~ pairs:
+      }
+    \clist_map_inline:cn { g__tblr_#1_ \int_use:N \g_tblr_level_int _clist }
+      {
+        \tl_log:x
+          {
+            \space { ##1 } ~\space=>~\space { \__tblr_spec_item:nn {#1} {##1} }
+          }
+      }
+  }
+
+%%% --------------------------------------------------------
+%%> \section{Data Structures Based on Integer Arrays}
+%%% --------------------------------------------------------
+
+\msg_new:nnn { tabularray } { intarray-beyond-bound }
+  { Position ~ #2 ~ is ~ beyond ~ the ~ bound ~ of ~ intarray ~ #1.}
+
+\cs_new_protected:Npn \__tblr_intarray_gset:Nnn #1 #2 #3
+  {
+    \bool_lazy_or:nnTF
+      { \int_compare_p:nNn {#2} < {0} }
+      { \int_compare_p:nNn {#2} > {\intarray_count:N #1} }
+      {
+        \bool_if:NT \g__tblr_tracing_intarray_bool
+          { \msg_warning:nnnn { tabularray } { intarray-beyond-bound } {#1} {#2} }
+      }
+      { \intarray_gset:Nnn #1 {#2} {#3} }
+  }
+\cs_generate_variant:Nn \__tblr_intarray_gset:Nnn { cnn }
+
+%% #1: data name; #2: key name; #3: value type
+\cs_new_protected:Npn \__tblr_data_new_key:nnn #1 #2 #3
+  {
+    \int_gincr:c { g__tblr_data_#1_key_count_int }
+    \tl_const:ce
+      {
+        c__tblr_data_#1_key_name_
+          \int_use:c { g__tblr_data_#1_key_count_int } _tl
+      }
+      { #2 }
+    \tl_const:ce { c__tblr_data_#1_key_number_#2_tl }
+      { \int_use:c { g__tblr_data_#1_key_count_int } }
+    \tl_const:cn { c__tblr_data_#1_key_type_#2_tl } {#3}
+  }
+
+\int_new:N \g__tblr_data_row_key_count_int
+\__tblr_data_new_key:nnn { row } { height }      { dim }
+\__tblr_data_new_key:nnn { row } { coefficient } { dec }
+\__tblr_data_new_key:nnn { row } { abovesep }    { dim }
+\__tblr_data_new_key:nnn { row } { belowsep }    { dim }
+\__tblr_data_new_key:nnn { row } { @row-height } { dim }
+\__tblr_data_new_key:nnn { row } { @row-head }   { dim }
+\__tblr_data_new_key:nnn { row } { @row-foot }   { dim }
+\__tblr_data_new_key:nnn { row } { @row-upper }  { dim }
+\__tblr_data_new_key:nnn { row } { @row-lower }  { dim }
+
+\int_new:N \g__tblr_data_column_key_count_int
+\__tblr_data_new_key:nnn { column } { width }       { dim }
+\__tblr_data_new_key:nnn { column } { coefficient } { dec }
+\__tblr_data_new_key:nnn { column } { leftsep }     { dim }
+\__tblr_data_new_key:nnn { column } { rightsep }    { dim }
+\__tblr_data_new_key:nnn { column } { @col-width }  { dim }
+
+\int_new:N \g__tblr_data_cell_key_count_int
+\__tblr_data_new_key:nnn { cell } { width }        { dim }
+\__tblr_data_new_key:nnn { cell } { rowspan }      { int }
+\__tblr_data_new_key:nnn { cell } { colspan }      { int }
+\__tblr_data_new_key:nnn { cell } { halign }       { str }
+\__tblr_data_new_key:nnn { cell } { valign }       { str }
+\__tblr_data_new_key:nnn { cell } { background }   { str }
+\__tblr_data_new_key:nnn { cell } { foreground }   { str }
+\__tblr_data_new_key:nnn { cell } { font }         { str }
+\__tblr_data_new_key:nnn { cell } { mode }         { str }
+\__tblr_data_new_key:nnn { cell } { cmd }          { str }
+\__tblr_data_new_key:nnn { cell } { omit }         { int }
+\__tblr_data_new_key:nnn { cell } { @cell-width }  { dim }
+\__tblr_data_new_key:nnn { cell } { @cell-height } { dim }
+\__tblr_data_new_key:nnn { cell } { @cell-depth }  { dim }
+
+\clist_const:Nn \c__tblr_data_clist { row, column, cell }
+\tl_const:Nn \c__tblr_data_row_count_tl { \c at rowcount }
+\tl_const:Nn \c__tblr_data_column_count_tl { \c at colcount }
+\tl_const:Nn \c__tblr_data_cell_count_tl { \c at rowcount * \c at colcount }
+\tl_const:Nn \c__tblr_data_row_index_number_tl {1}
+\tl_const:Nn \c__tblr_data_column_index_number_tl {1}
+\tl_const:Nn \c__tblr_data_cell_index_number_tl {2}
+\int_new:N \g__tblr_array_int
+
+\cs_new_protected:Npn \__tblr_init_table_data:
+  {
+    \clist_map_function:NN \c__tblr_data_clist \__tblr_init_one_data:n
+  }
+
+\cs_new_protected:Npn \__tblr_init_one_data:n #1
+  {
+    \int_gincr:N \g__tblr_array_int
+    \intarray_new:cn { g__tblr_#1_ \int_use:N \g__tblr_array_int _intarray }
+      {
+        \int_use:c { g__tblr_data_#1_key_count_int }
+          * \tl_use:c { c__tblr_data_#1_count_tl }
+      }
+    \cs_set_eq:cc { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray }
+      { g__tblr_#1_ \int_use:N \g__tblr_array_int _intarray }
+    %\intarray_log:c { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray }
+  }
+
+%% #1: data name; #2: data index; #3: key name
+\cs_new:Npn \__tblr_data_key_to_int:nnn #1 #2 #3
+  {
+    ( #2 - 1 ) * \int_use:c { g__tblr_data_#1_key_count_int }
+      + \tl_use:c { c__tblr_data_#1_key_number_#3_tl }
+  }
+
+%% #1: data name; #2: data index 1; #3: data index 2; #4: key name
+\cs_new:Npn \__tblr_data_key_to_int:nnnn #1 #2 #3 #4
+  {
+    ( #2 - 1 ) * \c at colcount * \int_use:c { g__tblr_data_#1_key_count_int }
+      + ( #3 - 1 ) * \int_use:c { g__tblr_data_#1_key_count_int }
+      + \tl_use:c { c__tblr_data_#1_key_number_#4_tl }
+  }
+
+\int_new:N \l__tblr_key_count_int
+\int_new:N \l__tblr_key_quotient_int
+\int_new:N \l__tblr_key_quotient_two_int
+\int_new:N \l__tblr_key_remainder_int
+
+%% #1: data name; #2: array position;
+%% #3: returning tl with index; #4: returning tl with key name
+\cs_new:Npn \__tblr_data_int_to_key:nnNN #1 #2 #3 #4
+  {
+    \int_set_eq:Nc \l__tblr_key_count_int { g__tblr_data_#1_key_count_int }
+    \int_set:Nn \l__tblr_key_quotient_int
+      {
+        \int_div_truncate:nn
+          { #2 + \l__tblr_key_count_int - 1 } { \l__tblr_key_count_int }
+      }
+    \int_set:Nn \l__tblr_key_remainder_int
+      {
+        #2 + \l__tblr_key_count_int
+          - \l__tblr_key_quotient_int * \l__tblr_key_count_int
+      }
+    \int_compare:nNnT { \l__tblr_key_remainder_int } = { 0 }
+      { \int_set_eq:NN \l__tblr_key_remainder_int \l__tblr_key_count_int }
+    \tl_set:Nx #3 { \int_use:N \l__tblr_key_quotient_int }
+    \tl_set_eq:Nc #4
+      { c__tblr_data_#1_key_name_ \int_use:N \l__tblr_key_remainder_int _tl }
+  }
+
+%% #1: data name; #2: array position;
+%% #3: returning tl with index 1; #4: returning tl with index 2;
+%% #5: returning tl with key name
+\cs_new:Npn \__tblr_data_int_to_key:nnNNN #1 #2 #3 #4 #5
+  {
+    \int_set_eq:Nc \l__tblr_key_count_int { g__tblr_data_#1_key_count_int }
+    \int_set:Nn \l__tblr_key_quotient_int
+      {
+        \int_div_truncate:nn
+          { #2 + \l__tblr_key_count_int - 1 } { \l__tblr_key_count_int }
+      }
+    \int_set:Nn \l__tblr_key_remainder_int
+      {
+        #2 + \l__tblr_key_count_int
+          - \l__tblr_key_quotient_int * \l__tblr_key_count_int
+      }
+    \int_compare:nNnT { \l__tblr_key_remainder_int } = { 0 }
+      { \int_set_eq:NN \l__tblr_key_remainder_int \l__tblr_key_count_int }
+    \tl_set_eq:Nc #5
+      { c__tblr_data_#1_key_name_ \int_use:N \l__tblr_key_remainder_int _tl }
+    \int_set:Nn \l__tblr_key_quotient_two_int
+      {
+        \int_div_truncate:nn
+          { \l__tblr_key_quotient_int + \c at colcount - 1 } { \c at colcount }
+      }
+    \int_set:Nn \l__tblr_key_remainder_int
+      {
+        \l__tblr_key_quotient_int + \c at colcount
+          - \l__tblr_key_quotient_two_int * \c at colcount
+      }
+    \int_compare:nNnT { \l__tblr_key_remainder_int } = { 0 }
+      { \int_set_eq:NN \l__tblr_key_remainder_int \c at colcount }
+    \tl_set:Nx #4 { \int_use:N \l__tblr_key_remainder_int }
+    \tl_set:Nx #3 { \int_use:N \l__tblr_key_quotient_two_int }
+  }
+
+\tl_new:N \g__tblr_data_int_from_value_tl
+
+%% #1: data name; #2: key name; #3: value
+%% The result will be stored in \g__tblr_data_int_from_value_tl
+\cs_new_protected:Npn \__tblr_data_int_from_value:nnn #1 #2 #3
+  {
+    \cs:w
+      __tblr_data_int_from_ \tl_use:c { c__tblr_data_#1_key_type_#2_tl } :n
+    \cs_end:
+    {#3}
+  }
+
+%% #1: data name; #2: key name; #3: int
+\cs_new:Npn \__tblr_data_int_to_value:nnn #1 #2 #3
+  {
+    \cs:w
+      __tblr_data_int_to_ \tl_use:c { c__tblr_data_#1_key_type_#2_tl } :n
+    \cs_end:
+    {#3}
+  }
+\cs_generate_variant:Nn \__tblr_data_int_to_value:nnn { nne, nVe }
+
+\cs_new_protected:Npn \__tblr_data_int_from_int:n #1
+  {
+    \tl_gset:Nn \g__tblr_data_int_from_value_tl {#1}
+  }
+
+\cs_new:Npn \__tblr_data_int_to_int:n #1
+  {
+    #1
+  }
+
+\cs_new_protected:Npn \__tblr_data_int_from_dim:n #1
+  {
+    \tl_gset:Nx \g__tblr_data_int_from_value_tl { \dim_to_decimal_in_sp:n {#1} }
+  }
+
+%% Return a dimension in pt so that it's easier to understand in tracing messages
+\cs_new:Npn \__tblr_data_int_to_dim:n #1
+  {
+    %#1 sp
+    %\dim_eval:n { #1 sp }
+    \dim_to_decimal:n { #1 sp } pt
+  }
+
+\cs_new_protected:Npn \__tblr_data_int_from_dec:n #1
+  {
+    \tl_gset:Nx \g__tblr_data_int_from_value_tl
+      { \dim_to_decimal_in_sp:n {#1 pt} }
+  }
+
+\cs_new:Npn \__tblr_data_int_to_dec:n #1
+  {
+    \dim_to_decimal:n {#1 sp}
+  }
+
+\int_new:N \g__tblr_data_str_value_count_int
+\tl_gclear_new:c { g__tblr_data_0_to_str_tl }
+
+\cs_new_protected:Npn \__tblr_data_int_from_str:n #1
+  {
+    \tl_if_exist:cTF { g__tblr_data_ \tl_to_str:n {#1} _to_int_tl }
+      {
+        \tl_gset_eq:Nc \g__tblr_data_int_from_value_tl
+          { g__tblr_data_ \tl_to_str:n {#1} _to_int_tl }
+      }
+      {
+        \int_gincr:N \g__tblr_data_str_value_count_int
+        \tl_gset:cx { g__tblr_data_ \tl_to_str:n {#1} _to_int_tl }
+          { \int_use:N \g__tblr_data_str_value_count_int }
+        \tl_gset:cn
+          { g__tblr_data_ \int_use:N \g__tblr_data_str_value_count_int _to_str_tl }
+          { \exp_not:n {#1} }
+        \tl_gset:Nx \g__tblr_data_int_from_value_tl
+          { \int_use:N \g__tblr_data_str_value_count_int }
+      }
+  }
+
+\cs_new:Npn \__tblr_data_int_to_str:n #1
+  {
+    \tl_use:c { g__tblr_data_#1_to_str_tl }
+  }
+
+%% #1: data name; #2: data index; #3: key; #4: value
+\cs_new_protected:Npn \__tblr_data_gput:nnnn #1 #2 #3 #4
+  {
+    \__tblr_data_int_from_value:nnn {#1} {#3} {#4}
+    \__tblr_intarray_gset:cnn
+      { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray }
+      { \__tblr_data_key_to_int:nnn {#1} {#2} {#3} }
+      { \g__tblr_data_int_from_value_tl }
+  }
+\cs_generate_variant:Nn \__tblr_data_gput:nnnn
+  { nnne, nnnV, nenn, nene, nenV, nVnn }
+
+%% #1: data name; #2: data index 1; #3: data index 2; #4: key; #5: value
+\cs_new_protected:Npn \__tblr_data_gput:nnnnn #1 #2 #3 #4 #5
+  {
+    \__tblr_data_int_from_value:nnn {#1} {#4} {#5}
+    \__tblr_intarray_gset:cnn
+      { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray }
+      { \__tblr_data_key_to_int:nnnn {#1} {#2} {#3} {#4} }
+      { \g__tblr_data_int_from_value_tl }
+  }
+\cs_generate_variant:Nn \__tblr_data_gput:nnnnn
+  { nnnne, nnnnV, neenn, neene, neenV, neeen, nVVnn }
+
+%% #1: data name; #2: data index; #3: key
+\cs_new:Npn \__tblr_data_item:nnn #1 #2 #3
+  {
+    \__tblr_data_int_to_value:nne {#1} {#3}
+      {
+        \intarray_item:cn { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray }
+          { \__tblr_data_key_to_int:nnn {#1} {#2} {#3} }
+      }
+  }
+\cs_generate_variant:Nn \__tblr_data_item:nnn { nen }
+
+%% #1: data name; #2: data index 1; #3: data index 2; #4: key
+\cs_new:Npn \__tblr_data_item:nnnn #1 #2 #3 #4
+  {
+    \__tblr_data_int_to_value:nne {#1} {#4}
+      {
+        \intarray_item:cn { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray }
+          { \__tblr_data_key_to_int:nnnn {#1} {#2} {#3} {#4} }
+      }
+  }
+\cs_generate_variant:Nn \__tblr_data_item:nnnn { neen }
+
+\tl_new:N \l__tblr_data_key_tl
+\tl_new:N \l__tblr_data_index_tl
+\tl_new:N \l__tblr_data_index_two_tl
+
+\cs_new_protected:Npn \__tblr_data_log:n #1
+  {
+    \use:c { __tblr_data_log_ \use:c { c__tblr_data_#1_index_number_tl } :n } {#1}
+    \__tblr_prop_log:n {#1}
+  }
+
+\cs_new_protected:cpn { __tblr_data_log_1:n } #1
+  {
+    %\intarray_log:c { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray }
+    \tl_set:Nx \l_tmpa_tl { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray }
+    \tl_log:n { ----------~----------~----------~----------~---------- }
+    \int_step_inline:nn
+      { \intarray_count:c { \l_tmpa_tl } }
+      {
+        \__tblr_data_int_to_key:nnNN {#1} {##1}
+          \l__tblr_data_index_tl \l__tblr_data_key_tl
+        \tl_log:x
+          {
+            \space
+            { #1 [\l__tblr_data_index_tl] / \l__tblr_data_key_tl }
+            ~\space => ~\space
+            {
+              \__tblr_data_int_to_value:nVe {#1} \l__tblr_data_key_tl
+                { \intarray_item:cn { \l_tmpa_tl } {##1} }
+            }
+          }
+      }
+  }
+
+\cs_new_protected:cpn { __tblr_data_log_2:n } #1
+  {
+    %\intarray_log:c { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray }
+    \tl_set:Nx \l_tmpa_tl { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray }
+    \tl_log:n { ----------~----------~----------~----------~---------- }
+    \int_step_inline:nn
+      { \intarray_count:c { \l_tmpa_tl } }
+      {
+        \__tblr_data_int_to_key:nnNNN {#1} {##1}
+          \l__tblr_data_index_tl \l__tblr_data_index_two_tl \l__tblr_data_key_tl
+        \tl_log:x
+          {
+            \space
+            {
+              #1 [\l__tblr_data_index_tl][\l__tblr_data_index_two_tl]
+                 / \l__tblr_data_key_tl
+            }
+            ~\space => ~\space
+            {
+              \__tblr_data_int_to_value:nVe {#1} \l__tblr_data_key_tl
+                { \intarray_item:cn { \l_tmpa_tl } {##1} }
+            }
+          }
+      }
+  }
+
+%% #1: data name; #2: row index; #3: key; #4: value
+\cs_new_protected:Npn \__tblr_data_gput_if_larger:nnnn #1 #2 #3 #4
+  {
+    \__tblr_data_int_from_value:nnn {#1} {#3} {#4}
+    \__tblr_array_gput_if_larger:cnn
+      { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray }
+      { \__tblr_data_key_to_int:nnn {#1} {#2} {#3} }
+      { \g__tblr_data_int_from_value_tl }
+  }
+\cs_generate_variant:Nn \__tblr_data_gput_if_larger:nnnn { nnne, nnnV, nene, nenV }
+
+\cs_new_protected:Npn \__tblr_array_gput_if_larger:Nnn #1 #2 #3
+  {
+    \int_compare:nNnT {#3} > { \intarray_item:Nn #1 {#2} }
+      { \__tblr_intarray_gset:Nnn #1 {#2} {#3} }
+  }
+\cs_generate_variant:Nn \__tblr_array_gput_if_larger:Nnn { cnn }
+
+%% #1: data name; #2: data index; #3: key; #4: value
+\cs_new_protected:Npn \__tblr_data_gadd_dimen_value:nnnn #1 #2 #3 #4
+  {
+    \__tblr_data_int_from_value:nnn {#1} {#3} {#4}
+    \__tblr_array_gadd_value:cnn
+      { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray }
+      { \__tblr_data_key_to_int:nnn {#1} {#2} {#3} }
+      { \g__tblr_data_int_from_value_tl }
+  }
+\cs_generate_variant:Nn \__tblr_data_gadd_dimen_value:nnnn
+  { nnne, nnnV, nenn, nene }
+
+\cs_new_protected:Npn \__tblr_array_gadd_value:Nnn #1 #2 #3
+  {
+    \__tblr_intarray_gset:Nnn #1 {#2} { \intarray_item:Nn #1 {#2} + #3 }
+  }
+\cs_generate_variant:Nn \__tblr_array_gadd_value:Nnn { cnn }
+
+\bool_new:N \g__tblr_use_intarray_bool
+\bool_gset_true:N \g__tblr_use_intarray_bool
+
+\AtBeginDocument
+  {
+    \bool_if:NF \g__tblr_use_intarray_bool
+      {
+        \cs_set_protected:Npn \__tblr_data_gput:nnnn #1 #2 #3 #4
+          {
+            \__tblr_spec_gput:nnn {#1} { [#2] / #3 } {#4}
+          }
+        \cs_set_protected:Npn \__tblr_data_gput:nnnnn #1 #2 #3 #4 #5
+          {
+            \__tblr_spec_gput:nnn {#1} { [#2][#3] / #4 } {#5}
+          }
+        \cs_set:Npn \__tblr_data_item:nnn #1 #2 #3
+          {
+            \__tblr_spec_item:nn {#1} { [#2] / #3 }
+          }
+        \cs_set:Npn \__tblr_data_item:nnnn #1 #2 #3 #4
+          {
+            \__tblr_spec_item:nn {#1} { [#2][#3] / #4 }
+          }
+        \cs_set_protected:Npn \__tblr_data_log:n #1
+          {
+            \__tblr_spec_log:n {#1}
+          }
+        \cs_set_protected:Npn \__tblr_data_gput_if_larger:nnnn #1 #2 #3 #4
+          {
+            \__tblr_spec_gput_if_larger:nnn {#1} { [#2] / #3 } {#4}
+          }
+        \cs_set_protected:Npn \__tblr_data_gput_if_larger:nnnnn #1 #2 #3 #4 #5
+          {
+            \__tblr_spec_gput_if_larger:nnn {#1} { [#2][#3] / #4 } {#5}
+          }
+        \cs_set_protected:Npn \__tblr_data_gadd_dimen_value:nnnn #1 #2 #3 #4
+          {
+            \__tblr_spec_gadd_dimen_value:nnn {#1} { [#2] / #3 } {#4}
+          }
+        \cs_set_protected:Npn \__tblr_data_gadd_dimen_value:nnnnn #1 #2 #3 #4 #5
+          {
+            \__tblr_spec_gadd_dimen_value:nnn {#1} { [#2][#3] / #4 } {#5}
+          }
+      }
+  }
+
+%%% --------------------------------------------------------
+%%> \section{Child Selectors}
+%%% --------------------------------------------------------
+
+\clist_new:N \g_tblr_used_child_selectors_clist
+
+\tl_new:N \l__tblr_childs_arg_spec_tl
+
+\msg_new:nnn { tabularray } { used-child-selector }
+  { Child ~ selector ~ name ~ "#1" ~ has ~ been ~ used! }
+
+\NewDocumentCommand \NewChildSelector { m O{0} o m }
+  {
+    \__tblr_new_child_selector_aux:xnnn { \tl_trim_spaces:n {#1} } {#2} {#3} {#4}
+  }
+
+\cs_new_protected:Npn \__tblr_new_child_selector_aux:nnnn #1 #2 #3 #4
+  {
+    \clist_if_in:NnTF \g_tblr_used_child_selectors_clist { #1 }
+      {
+        \msg_error:nnn { tabularray } { used-child-selector } { #1 }
+        \clist_log:N \g_tblr_used_child_selectors_clist
+      }
+      {
+        \__tblr_make_xparse_arg_spec:nnN { #2 } { #3 } \l__tblr_childs_arg_spec_tl
+        \exp_args:NcV \NewDocumentCommand
+          { __tblr_child_selector_ #1 :w } \l__tblr_childs_arg_spec_tl { #4 }
+        \clist_gput_right:Nn \g_tblr_used_child_selectors_clist { #1 }
+      }
+  }
+\cs_generate_variant:Nn \__tblr_new_child_selector_aux:nnnn { xnnn }
+
+%% #1: argument number, #2: optional argument default, #3: result tl
+\cs_new_protected:Npn \__tblr_make_xparse_arg_spec:nnN #1 #2 #3
+  {
+    \tl_clear:N #3
+    \int_compare:nNnT { #1 } > { 0 }
+      {
+        \IfValueTF { #2 }
+          { \tl_set:Nn #3 { O{#2} } }
+          { \tl_set:Nn #3 { m } }
+        \tl_put_right:Nx #3 { \prg_replicate:nn { #1 - 1 } { m } }
+      }
+  }
+
+\clist_new:N \l_tblr_childs_clist
+\tl_new:N \l_tblr_childs_total_tl
+
+\NewChildSelector { odd } [1] []
+  {
+    \tl_if_blank:nTF {#1}
+      {
+        \int_step_inline:nnnn {1} {2} { \l_tblr_childs_total_tl }
+          { \clist_put_right:Nn \l_tblr_childs_clist {##1} }
+      }
+      { \__tblr_child_selector_odd_or_even:nn { odd } {#1} }
+  }
+
+\NewChildSelector { even } [1] []
+  {
+    \tl_if_blank:nTF {#1}
+      {
+        \int_step_inline:nnnn {2} {2} { \l_tblr_childs_total_tl }
+          { \clist_put_right:Nn \l_tblr_childs_clist {##1} }
+      }
+      { \__tblr_child_selector_odd_or_even:nn { even } {#1} }
+  }
+
+\tl_new:N \l__tblr_child_from_tl
+\tl_new:N \l__tblr_child_to_tl
+
+%% #1: odd or even; #2: selector option
+\cs_new_protected:Npn \__tblr_child_selector_odd_or_even:nn #1 #2
+  {
+    \seq_set_split:Nnn \l_tmpa_seq {-} { #2 - Z }
+    \tl_set:Nx \l__tblr_child_from_tl { \seq_item:Nn \l_tmpa_seq {1} }
+    \tl_set:Nx \l__tblr_child_to_tl { \seq_item:Nn \l_tmpa_seq {2} }
+    \tl_use:c { int_if_ #1 :nF } { \l__tblr_child_from_tl }
+      {
+        \tl_set:Nx \l__tblr_child_from_tl
+          { \int_eval:n { \l__tblr_child_from_tl + 1 } }
+      }
+    \__tblr_child_name_to_index:VN \l__tblr_child_to_tl \l__tblr_child_to_tl
+    \int_step_inline:nnnn { \l__tblr_child_from_tl } {2} { \l__tblr_child_to_tl }
+      { \clist_put_right:Nn \l_tblr_childs_clist {##1} }
+  }
+
+\regex_const:Nn \c__tblr_split_selector_name_regex { ^ ( [A-Za-z] {2,} ) ( . * ) }
+\seq_new:N \l__tblr_childs_split_seq
+\seq_new:N \l__tblr_childs_regex_seq
+\tl_new:N \l__tblr_childs_selector_tl
+
+%% #1, child specifications; #2, total number.
+%% The result will be put into \l_tblr_childs_clist
+\cs_new_protected:Npn \__tblr_get_childs:nn #1 #2
+  {
+    \clist_clear:N \l_tblr_childs_clist
+    \tl_set:Nx \l_tblr_childs_total_tl {#2}
+    \regex_extract_once:NnNTF \c__tblr_split_selector_name_regex {#1}
+      \l__tblr_childs_regex_seq
+      {
+        \tl_set:No \l__tblr_childs_selector_tl
+          {
+            \cs:w
+            __tblr_child_selector_ \seq_item:Nn \l__tblr_childs_regex_seq {2} :w
+            \cs_end:
+          }
+        \exp_last_unbraced:Nx \l__tblr_childs_selector_tl
+          { \seq_item:Nn \l__tblr_childs_regex_seq{3} }
+      }
+      {
+        \tl_if_eq:nnTF {#1} {-}
+          { \__tblr_get_childs_normal:nn {1-#2} {#2} }
+          { \__tblr_get_childs_normal:nn {#1} {#2} }
+      }
+    %\clist_log:N \l_tblr_childs_clist
+  }
+\cs_generate_variant:Nn \__tblr_get_childs:nn { nx }
+
+\cs_new_protected:Npn \__tblr_get_childs_normal:nn #1 #2
+  {
+    \seq_set_split:Nnn \l__tblr_childs_split_seq {,} {#1}
+    \seq_map_inline:Nn \l__tblr_childs_split_seq
+      {
+        \tl_if_in:nnTF {##1} {-}
+          { \__tblr_get_childs_normal_aux:w ##1 \scan_stop }
+          { \__tblr_get_childs_normal_aux:w ##1 - ##1 \scan_stop }
+      }
+  }
+
+\cs_new_protected_nopar:Npn \__tblr_get_childs_normal_aux:w #1 - #2 \scan_stop
+  {
+    \__tblr_child_name_to_index:nN {#1} \l__tblr_child_from_tl
+    \__tblr_child_name_to_index:nN {#2} \l__tblr_child_to_tl
+    \int_step_inline:nnn { \l__tblr_child_from_tl } { \l__tblr_child_to_tl }
+      { \clist_put_right:Nn \l_tblr_childs_clist {##1} }
+  }
+
+\regex_const:Nn \c__tblr_child_name_regex { ^ [U-Z] $ }
+
+%% Convert U, V, W, X, Y, Z to the indexes of the last six childs, respectively
+\cs_new_protected_nopar:Npn \__tblr_child_name_to_index:nN #1 #2
+  {
+    \regex_match:NnTF \c__tblr_child_name_regex {#1}
+      {
+        \tl_set:Nx #2
+          { \int_eval:n { \l_tblr_childs_total_tl + \int_from_alph:n {#1} - 26 } }
+      }
+      { \tl_set:Nx #2 { #1 } }
+  }
+\cs_generate_variant:Nn \__tblr_child_name_to_index:nN { VN }
+
+%%% --------------------------------------------------------
+%%> \section{New Table Commands}
+%%% --------------------------------------------------------
+
+%% We need some commands to modify table/row/column/cell specifications.
+%% These commands must be defined with \NewTableCommand command,
+%% so that we could extract them, execute them once, then disable them.
+
+\clist_new:N \g__tblr_table_commands_clist
+
+\msg_new:nnn { tabularray } { defined-table-command }
+  { Table ~ command ~ #1 already ~ defined! }
+
+\NewDocumentCommand \NewTableCommand { m O{0} o m }
+  {
+    \clist_if_in:NnTF \g__tblr_table_commands_clist { #1 }
+      {
+        \msg_error:nnn { tabularray } { defined-table-command } { #1 }
+        \clist_log:N \g__tblr_table_commands_clist
+      }
+      {
+        \__tblr_make_xparse_arg_spec:nnN { #2 } { #3 } \l__tblr_a_tl
+        \exp_args:NcV \NewDocumentCommand
+          { __tblr_table_command_ \cs_to_str:N #1 :w } \l__tblr_a_tl { #4 }
+        %% we can not use \cs_if_exist:NTF here (see issue #328)
+        \__tblr_cs_if_defined:NTF #1
+          {
+            \cs_set_eq:cN { __tblr_table_command_ \cs_to_str:N #1 _saved:w } #1
+          }
+          {
+            \exp_args:NcV \NewDocumentCommand
+              { __tblr_table_command_ \cs_to_str:N #1 _saved:w } \l__tblr_a_tl { }
+          }
+        \IfValueTF { #3 }
+          {
+            \tl_gset:cn { g__tblr_table_cmd_ \cs_to_str:N #1 _arg_numb_tl } {-#2}
+          }
+          {
+            \tl_gset:cn { g__tblr_table_cmd_ \cs_to_str:N #1 _arg_numb_tl } {#2}
+          }
+        \clist_gput_right:Nn \g__tblr_table_commands_clist { #1 }
+      }
+  }
+
+\cs_new_protected:Npn \__tblr_enable_table_commands:
+  {
+    \clist_map_inline:Nn \g__tblr_table_commands_clist
+      { \cs_set_eq:Nc ##1 { __tblr_table_command_ \cs_to_str:N ##1 :w } }
+  }
+
+\cs_new_protected:Npn \__tblr_disable_table_commands:
+  {
+    \clist_map_inline:Nn \g__tblr_table_commands_clist
+      { \cs_set_eq:Nc ##1 { __tblr_table_command_ \cs_to_str:N ##1 _saved:w } }
+  }
+
+\cs_new_protected:Npn \__tblr_execute_table_commands:
+  {
+    \__tblr_prop_map_inline:nn { command }
+      {
+        \__tblr_set_row_col_from_key_name:w ##1
+        ##2
+      }
+    \LogTblrTracing { cell }
+  }
+
+\cs_new_protected:Npn \__tblr_set_row_col_from_key_name:w [#1][#2]
+  {
+    \int_set:Nn \c at rownum {#1}
+    \int_set:Nn \c at colnum {#2}
+  }
+
+%% Add \empty as a table command so that users can write \\\empty\hline (see #328)
+\NewTableCommand\empty{}
+
+%% Table commands are defined only inside tblr environments,
+%% but some packages such as csvsimple need to use them outside tblr environments,
+%% therefore we define some of them first here.
+\ProvideDocumentCommand \SetHlines  { o m m } {}
+\ProvideDocumentCommand \SetHline   { o m m } {}
+\ProvideDocumentCommand \SetVlines  { o m m } {}
+\ProvideDocumentCommand \SetVline   { o m m } {}
+\ProvideDocumentCommand \SetCells   { o m } {}
+\ProvideDocumentCommand \SetCell    { o m } {}
+\ProvideDocumentCommand \SetRows    { o m } {}
+\ProvideDocumentCommand \SetRow     { o m } {}
+\ProvideDocumentCommand \SetColumns { o m } {}
+\ProvideDocumentCommand \SetColumn  { o m } {}
+
+%%% --------------------------------------------------------
+%%> \section{New Content Commands}
+%%% --------------------------------------------------------
+
+%% We need to emulate or fix some commands such as \diagbox in other packages
+%% These commands must be defined with \NewContentCommand command
+%% We only enable them inside tblr environment to avoid potential conflict
+
+\clist_new:N \g__tblr_content_commands_clist
+
+\msg_new:nnn { tabularray } { defined-content-command }
+  { Content ~ command ~ #1 already ~ defined! }
+
+\NewDocumentCommand \NewContentCommand { m O{0} o m }
+  {
+    \clist_if_in:NnTF \g__tblr_content_commands_clist { #1 }
+      {
+        \msg_error:nnn { tabularray } { defined-content-command } { #1 }
+        \clist_log:N \g__tblr_content_commands_clist
+      }
+      {
+        \__tblr_make_xparse_arg_spec:nnN { #2 } { #3 } \l__tblr_a_tl
+        \exp_args:NcV \NewDocumentCommand
+          { __tblr_content_command_ \cs_to_str:N #1 :w } \l__tblr_a_tl { #4 }
+        \clist_gput_right:Nn \g__tblr_content_commands_clist { #1 }
+      }
+  }
+
+\cs_new_protected:Npn \__tblr_enable_content_commands:
+  {
+    \clist_map_inline:Nn \g__tblr_content_commands_clist
+      { \cs_set_eq:Nc ##1 { __tblr_content_command_ \cs_to_str:N ##1 :w } }
+  }
+
+%%% --------------------------------------------------------
+%%> \section{New Dash Styles}
+%%% --------------------------------------------------------
+
+%% \NewDashStyle commands
+
+\dim_zero_new:N \rulewidth
+\dim_set:Nn \rulewidth {0.4pt}
+
+\prop_new:N \g__tblr_defined_hdash_styles_prop
+\prop_new:N \g__tblr_defined_vdash_styles_prop
+
+\prop_gset_from_keyval:Nn \g__tblr_defined_hdash_styles_prop
+  { solid = \hrule height \rulewidth }
+\prop_gset_from_keyval:Nn \g__tblr_defined_vdash_styles_prop
+  { solid = \vrule width \rulewidth }
+
+\NewDocumentCommand \NewDashStyle { m m }
+  {
+    \seq_set_split:Nnn \l_tmpa_seq { ~ } {#2}
+    \tl_set:Nx \l__tblr_a_tl { \seq_item:Nn \l_tmpa_seq {1} }
+    \tl_set:Nx \l__tblr_b_tl { \seq_item:Nn \l_tmpa_seq {2} }
+    \tl_set:Nx \l__tblr_c_tl { \seq_item:Nn \l_tmpa_seq {3} }
+    \tl_set:Nx \l__tblr_d_tl { \seq_item:Nn \l_tmpa_seq {4} }
+    \tl_if_eq:NnT \l__tblr_a_tl { on }
+      {
+        \tl_if_eq:NnT \l__tblr_c_tl { off }
+          {
+            \__tblr_dash_style_make_boxes:nxx {#1}
+              { \dim_eval:n {\l__tblr_b_tl} } { \dim_eval:n {\l__tblr_d_tl} }
+          }
+      }
+  }
+
+\cs_new_protected:Npn \__tblr_dash_style_make_boxes:nnn #1 #2 #3
+  {
+    \dim_set:Nn \l_tmpa_dim { #2 + #3 }
+    \tl_set:Nn \l__tblr_h_tl { \hbox_to_wd:nn }
+    \tl_put_right:Nx \l__tblr_h_tl { { \dim_use:N \l_tmpa_dim } }
+    \tl_put_right:Nn \l__tblr_h_tl
+      {
+        { \hss \vbox:n { \hbox_to_wd:nn {#2} {} \hrule height \rulewidth } \hss }
+      }
+    \prop_gput:NnV \g__tblr_defined_hdash_styles_prop {#1} \l__tblr_h_tl
+    %\prop_log:N \g__tblr_defined_hdash_styles_prop
+    \tl_set:Nn \l__tblr_v_tl { \vbox_to_ht:nn }
+    \tl_put_right:Nx \l__tblr_v_tl { { \dim_use:N \l_tmpa_dim } }
+    \tl_put_right:Nn \l__tblr_v_tl
+      {
+        { \vss \hbox:n { \vbox_to_ht:nn {#2} {} \vrule width \rulewidth } \vss }
+      }
+    \prop_gput:NnV \g__tblr_defined_vdash_styles_prop {#1} \l__tblr_v_tl
+    %\prop_log:N \g__tblr_defined_vdash_styles_prop
+  }
+\cs_generate_variant:Nn \__tblr_dash_style_make_boxes:nnn { nxx }
+
+\cs_new_protected:Npn \__tblr_get_hline_dash_style:N #1
+  {
+    \tl_set:Nx \l_tmpa_tl
+      { \prop_item:NV \g__tblr_defined_hdash_styles_prop #1 }
+    \tl_if_empty:NF \l_tmpa_tl { \tl_set_eq:NN #1 \l_tmpa_tl }
+  }
+
+\cs_new_protected:Npn \__tblr_get_vline_dash_style:N #1
+  {
+    \tl_set:Nx \l_tmpa_tl
+      { \prop_item:NV \g__tblr_defined_vdash_styles_prop #1 }
+    \tl_if_empty:NF \l_tmpa_tl { \tl_set_eq:NN #1 \l_tmpa_tl }
+  }
+
+\NewDashStyle {dashed} {on ~ 2pt ~ off ~ 2pt}
+\NewDashStyle {dotted} {on ~ 0.4pt ~ off ~ 1pt}
+
+%%% --------------------------------------------------------
+%%> \section{Set Hlines and Vlines}
+%%% --------------------------------------------------------
+
+\tl_const:Nn \@tblr at dash { dash }
+\tl_const:Nn \@tblr at text { text }
+
+\regex_const:Nn \c__tblr_is_color_key_regex { ^[A-Za-z] }
+
+%% \SetHlines command for setting every hline in the table
+\NewTableCommand \SetHlines [3] [+]
+  {
+    \tblr_set_every_hline:nnn {#1} {#2} {#3}
+  }
+
+%% We put all code inside a group to avoid affecting other table commands
+\cs_new_protected:Npn \tblr_set_every_hline:nnn #1 #2 #3
+  {
+    \group_begin:
+    \int_step_inline:nn { \int_eval:n { \c at rowcount + 1 } }
+      {
+        \int_set:Nn \c at rownum {##1}
+        \tblr_set_hline:nnn {#1} {#2} {#3}
+      }
+    \group_end:
+  }
+
+%% Check the number of arguments and call \tblr_set_every_hline in different ways
+%% This function is called when parsing table specifications
+\cs_new_protected:Npn \__tblr_set_every_hline_aux:n #1
+  {
+    \tl_if_head_is_group:nTF {#1}
+      {
+        \int_compare:nNnTF { \tl_count:n {#1} } = {3}
+          { \tblr_set_every_hline:nnn #1 }
+          { \tblr_set_every_hline:nnn {1} #1 }
+      }
+      { \tblr_set_every_hline:nnn {1} {-} {#1} }
+  }
+
+%% Add \SetHline, \hline and \cline commands
+
+\tl_new:N \l__tblr_hline_count_tl % the count of all hlines
+\tl_new:N \l__tblr_hline_num_tl   % the index of the hline
+\tl_new:N \l__tblr_hline_cols_tl  % the columns of the hline
+\tl_new:N \l__tblr_hline_dash_tl  % dash style
+\tl_new:N \l__tblr_hline_fg_tl    % dash foreground
+\tl_new:N \l__tblr_hline_wd_tl    % dash width
+\tl_new:N \l__tblr_hline_leftpos_tl  % left position
+\tl_new:N \l__tblr_hline_rightpos_tl % right position
+\bool_new:N \l__tblr_hline_endpos_bool % whether set positions only for both ends
+
+\NewTableCommand \cline [2] [] { \SetHline [=] {#2} {#1} }
+
+\NewTableCommand \hline [1] [] { \SetHline [+] {-} {#1} }
+
+%% #1: the index of the hline (may be + or =)
+%% #2: which columns of the hline, separate by commas
+%% #3: key=value pairs
+\NewTableCommand \SetHline [3] [+]
+  {
+    \tblr_set_hline:nnn {#1} {#2} {#3}
+  }
+
+%% We need to check "text" key first
+%% If it does exist and has empty value, then do nothing
+\cs_new_protected:Npn \tblr_set_hline:nnn #1 #2 #3
+  {
+    \group_begin:
+    \keys_set_groups:nnn { tblr-hline } { text } {#3}
+    \tl_if_eq:NnF \l__tblr_hline_dash_tl { \exp_not:N \@tblr at text }
+      {
+        \__tblr_set_hline_num:n {#1}
+        \tl_clear:N \l__tblr_hline_dash_tl
+        \keys_set:nn { tblr-hline } { dash = solid, #3 }
+        \__tblr_set_hline_cmd:n {#2}
+      }
+    \group_end:
+  }
+
+\cs_new_protected:Npn \tblr_set_hline:nnnn #1 #2 #3 #4
+  {
+    \group_begin:
+    \__tblr_get_childs:nx {#1} { \int_eval:n { \c at rowcount + 1 } }
+    \clist_map_inline:Nn \l_tblr_childs_clist
+      {
+        \int_set:Nn \c at rownum {##1}
+        \tblr_set_hline:nnn {#2} {#3} {#4}
+      }
+    \group_end:
+  }
+
+%% Check the number of arguments and call \tblr_set_hline in different ways
+%% Note that #1 always includes an outer pair of braces
+%% This function is called when parsing table specifications
+\cs_new_protected:Npn \__tblr_set_hline_aux:nn #1 #2
+  {
+    \tl_if_head_is_group:nTF {#2}
+      {
+        \int_compare:nNnTF { \tl_count:n {#2} } = {3}
+          { \tblr_set_hline:nnnn #1 #2 }
+          { \tblr_set_hline:nnnn #1 {1} #2 }
+      }
+      { \tblr_set_hline:nnnn #1 {1} {-} {#2} }
+  }
+\cs_generate_variant:Nn \__tblr_set_hline_aux:nn { Vn }
+
+%% #1: the index of hline to set (may be + or =)
+\cs_new_protected:Npn \__tblr_set_hline_num:n #1
+  {
+    \tl_clear:N \l__tblr_hline_num_tl
+    \tl_set:Nx \l__tblr_hline_count_tl
+      { \__tblr_spec_item:ne { hline } { [\int_use:N \c at rownum] / @hline-count } }
+    %% \l__tblr_hline_count_tl may be empty when rowspec has extra |'s
+    \int_compare:nNnTF { \l__tblr_hline_count_tl + 0 } = {0}
+      {
+        \tl_set:Nx \l__tblr_hline_num_tl { 1 }
+        \__tblr_spec_gput:nen { hline }
+          { [\int_use:N \c at rownum] / @hline-count } { 1 }
+      }
+      {
+        \tl_if_eq:nnTF {#1} {+}
+          { \__tblr_set_hline_num_incr: }
+          {
+            \tl_if_eq:nnTF {#1} {=}
+              { \tl_set_eq:NN \l__tblr_hline_num_tl \l__tblr_hline_count_tl }
+              {
+                \int_compare:nNnTF {#1} > { \l__tblr_hline_count_tl }
+                  { \__tblr_set_hline_num_incr: }
+                  { \tl_set:Nn \l__tblr_hline_num_tl {#1} }
+              }
+          }
+      }
+  }
+
+\cs_new_protected:Npn \__tblr_set_hline_num_incr:
+  {
+    \tl_set:Nx \l__tblr_hline_count_tl
+      { \int_eval:n { \l__tblr_hline_count_tl + 1 } }
+    \__tblr_spec_gput:nee { hline }
+      { [\int_use:N \c at rownum] / @hline-count } { \l__tblr_hline_count_tl }
+    \tl_set_eq:NN \l__tblr_hline_num_tl \l__tblr_hline_count_tl
+  }
+
+\keys_define:nn { tblr-hline }
+  {
+    dash .code:n = \tl_set:Nn \l__tblr_hline_dash_tl { \exp_not:N \@tblr at dash #1 },
+    text .code:n = \tl_set:Nn \l__tblr_hline_dash_tl { \exp_not:N \@tblr at text #1 },
+    text .groups:n = { text },
+    wd .code:n = \tl_set:Nn \l__tblr_hline_wd_tl { \dim_eval:n {#1} },
+    fg .code:n = \tl_set:Nn \l__tblr_hline_fg_tl {#1},
+    leftpos  .code:n = \tl_set:Nx \l__tblr_hline_leftpos_tl {#1},
+    rightpos .code:n = \tl_set:Nx \l__tblr_hline_rightpos_tl {#1},
+    l        .meta:n = { leftpos = #1 },
+    l     .default:n = { -0.8 },
+    r        .meta:n = { rightpos = #1 },
+    r     .default:n = { -0.8 },
+    lr       .meta:n = { leftpos = #1, rightpos = #1 },
+    lr    .default:n = { -0.8 },
+    endpos .bool_set:N = \l__tblr_hline_endpos_bool,
+    unknown .code:n = \__tblr_hline_unknown_key:V \l_keys_key_str,
+  }
+
+\cs_new_protected:Npn \__tblr_hline_unknown_key:n #1
+  {
+    \prop_if_in:NnTF \g__tblr_defined_hdash_styles_prop {#1}
+      { \tl_set:Nn \l__tblr_hline_dash_tl { \exp_not:N \@tblr at dash #1 } }
+      {
+        \regex_match:NnTF \c__tblr_is_color_key_regex {#1}
+          { \tl_set:Nn \l__tblr_hline_fg_tl {#1} }
+          {
+            \tl_set_rescan:Nnn \l__tblr_v_tl {} {#1}
+            \tl_set:Nn \l__tblr_hline_wd_tl { \dim_eval:n {\l__tblr_v_tl} }
+          }
+      }
+  }
+\cs_generate_variant:Nn \__tblr_hline_unknown_key:n { V }
+
+\cs_new_protected_nopar:Npn \__tblr_set_hline_cmd:n #1
+  {
+    \__tblr_get_childs:nx {#1} { \int_use:N \c at colcount }
+    \clist_map_inline:Nn \l_tblr_childs_clist
+      {
+        \__tblr_set_hline_option:nnn { ##1 } { @dash } { \l__tblr_hline_dash_tl }
+        \tl_if_empty:NF \l__tblr_hline_wd_tl
+          {
+            \__tblr_set_hline_option:nnn { ##1 } { wd } { \l__tblr_hline_wd_tl }
+          }
+        \tl_if_empty:NF \l__tblr_hline_fg_tl
+          {
+            \__tblr_set_hline_option:nnn { ##1 } { fg } { \l__tblr_hline_fg_tl }
+          }
+      }
+    \tl_if_empty:NF \l__tblr_hline_leftpos_tl
+      {
+        \bool_if:NTF \l__tblr_hline_endpos_bool
+          {
+            \__tblr_set_hline_option:nnn
+              { \clist_item:Nn \l_tblr_childs_clist {1} }
+              { leftpos }
+              { \l__tblr_hline_leftpos_tl }
+          }
+          {
+            \clist_map_inline:Nn \l_tblr_childs_clist
+              {
+                \__tblr_set_hline_option:nnn
+                  { ##1 } { leftpos } { \l__tblr_hline_leftpos_tl }
+              }
+          }
+      }
+    \tl_if_empty:NF \l__tblr_hline_rightpos_tl
+      {
+        \bool_if:NTF \l__tblr_hline_endpos_bool
+          {
+            \__tblr_set_hline_option:nnn
+              { \clist_item:Nn \l_tblr_childs_clist {-1} }
+              { rightpos }
+              { \l__tblr_hline_rightpos_tl }
+          }
+          {
+            \clist_map_inline:Nn \l_tblr_childs_clist
+              {
+                \__tblr_set_hline_option:nnn
+                  { ##1 } { rightpos } { \l__tblr_hline_rightpos_tl }
+              }
+          }
+      }
+  }
+
+%% #1: column; #2: key; #3: value
+\cs_new_protected_nopar:Npn \__tblr_set_hline_option:nnn #1 #2 #3
+  {
+    \__tblr_spec_gput:nee { hline }
+      { [\int_use:N \c at rownum][#1](\l__tblr_hline_num_tl) / #2 } { #3 }
+  }
+
+\msg_new:nnn { tabularray } { obsolete-firsthline }
+  { \firsthline ~ is ~ obsolete; ~ use ~ 'baseline=T' ~ instead. }
+
+\msg_new:nnn { tabularray } { obsolete-lasthline }
+  { \lasthline ~ is ~ obsolete; ~ use ~ 'baseline=B' ~ instead. }
+
+\NewTableCommand \firsthline [1] []
+  {
+    \msg_error:nn { tabularray } { obsolete-firsthline }
+  }
+
+\NewTableCommand \lasthline [1] []
+  {
+    \msg_error:nn { tabularray } { obsolete-lasthline }
+  }
+
+%% \SetVlines command for setting every vline in the table
+\NewTableCommand \SetVlines [3] [+]
+  {
+    \tblr_set_every_vline:nnn {#1} {#2} {#3}
+  }
+
+%% We put all code inside a group to avoid affecting other table commands
+\cs_new_protected:Npn \tblr_set_every_vline:nnn #1 #2 #3
+  {
+    \group_begin:
+    \int_step_inline:nn { \int_eval:n { \c at colcount + 1 } }
+      {
+        \int_set:Nn \c at colnum {##1}
+        \tblr_set_vline:nnn {#1} {#2} {#3}
+      }
+    \group_end:
+  }
+
+%% Check the number of arguments and call \tblr_set_every_vline in different ways
+%% This function is called when parsing table specifications
+\cs_new_protected:Npn \__tblr_set_every_vline_aux:n #1
+  {
+    \tl_if_head_is_group:nTF {#1}
+      {
+        \int_compare:nNnTF { \tl_count:n {#1} } = {3}
+          { \tblr_set_every_vline:nnn #1 }
+          { \tblr_set_every_vline:nnn {1} #1 }
+      }
+      { \tblr_set_every_vline:nnn {1} {-} {#1} }
+  }
+
+%% Add \SetVline, \vline and \rline commands
+
+\tl_new:N \l__tblr_vline_count_tl % the count of all vlines
+\tl_new:N \l__tblr_vline_num_tl   % the index of the vline
+\tl_new:N \l__tblr_vline_rows_tl  % the rows of the vline
+\tl_new:N \l__tblr_vline_dash_tl  % dash style
+\tl_new:N \l__tblr_vline_fg_tl    % dash foreground
+\tl_new:N \l__tblr_vline_wd_tl    % dash width
+\tl_new:N \l__tblr_vline_abovepos_tl % above position
+\tl_new:N \l__tblr_vline_belowpos_tl % below position
+
+\NewTableCommand \rline [2] [] { \SetVline [=] {#2} {#1} }
+
+\NewTableCommand \vline [1] [] { \SetVline [+] {-} {#1} }
+
+%% #1: the index of the vline (may be + or =)
+%% #2: which rows of the vline, separate by commas
+%% #3: key=value pairs
+\NewTableCommand \SetVline [3] [+]
+  {
+    \tblr_set_vline:nnn {#1} {#2} {#3}
+  }
+
+%% We need to check "text" key first
+%% If it does exist and has empty value, then do nothing
+\cs_new_protected:Npn \tblr_set_vline:nnn #1 #2 #3
+  {
+    \group_begin:
+    \keys_set_groups:nnn { tblr-vline } { text } {#3}
+    \tl_if_eq:NnF \l__tblr_vline_dash_tl { \exp_not:N \@tblr at text }
+      {
+        \__tblr_set_vline_num:n {#1}
+        \tl_clear:N \l__tblr_vline_dash_tl
+        \keys_set:nn { tblr-vline } { dash = solid, #3 }
+        \__tblr_set_vline_cmd:n {#2}
+      }
+    \group_end:
+  }
+
+\cs_new_protected:Npn \tblr_set_vline:nnnn #1 #2 #3 #4
+  {
+    \group_begin:
+    \__tblr_get_childs:nx {#1} { \int_eval:n { \c at colcount + 1} }
+    \clist_map_inline:Nn \l_tblr_childs_clist
+      {
+        \int_set:Nn \c at colnum {##1}
+        \tblr_set_vline:nnn {#2} {#3} {#4}
+      }
+    \group_end:
+  }
+
+%% Check the number of arguments and call \tblr_set_vline in different ways
+%% Note that #1 always includes an outer pair of braces
+%% This function is called when parsing table specifications
+\cs_new_protected:Npn \__tblr_set_vline_aux:nn #1 #2
+  {
+    \tl_if_head_is_group:nTF {#2}
+      {
+        \int_compare:nNnTF { \tl_count:n {#2} } = {3}
+          { \tblr_set_vline:nnnn #1 #2 }
+          { \tblr_set_vline:nnnn #1 {1} #2 }
+      }
+      { \tblr_set_vline:nnnn #1 {1} {-} {#2} }
+  }
+\cs_generate_variant:Nn \__tblr_set_vline_aux:nn { Vn }
+
+%% #1: the index of vline to set (may be + or =)
+\cs_new_protected:Npn \__tblr_set_vline_num:n #1
+  {
+    \tl_clear:N \l__tblr_vline_num_tl
+    \tl_set:Nx \l__tblr_vline_count_tl
+      { \__tblr_spec_item:ne { vline } { [\int_use:N \c at colnum] / @vline-count } }
+    %% \l__tblr_vline_count_tl may be empty when colspec has extra |'s
+    \int_compare:nNnTF { \l__tblr_vline_count_tl + 0 } = {0}
+      {
+        \tl_set:Nx \l__tblr_vline_num_tl { 1 }
+        \__tblr_spec_gput:nen { vline }
+          { [\int_use:N \c at colnum] / @vline-count } { 1 }
+      }
+      {
+        \tl_if_eq:nnTF {#1} {+}
+          { \__tblr_set_vline_num_incr: }
+          {
+            \tl_if_eq:nnTF {#1} {=}
+              { \tl_set_eq:NN \l__tblr_vline_num_tl \l__tblr_vline_count_tl }
+              {
+                \int_compare:nNnTF {#1} > { \l__tblr_vline_count_tl }
+                  { \__tblr_set_vline_num_incr: }
+                  { \tl_set:Nn \l__tblr_vline_num_tl {#1} }
+              }
+          }
+      }
+  }
+
+\cs_new_protected:Npn \__tblr_set_vline_num_incr:
+  {
+    \tl_set:Nx \l__tblr_vline_count_tl
+      { \int_eval:n { \l__tblr_vline_count_tl + 1 } }
+    \__tblr_spec_gput:nee { vline }
+      { [\int_use:N \c at colnum] / @vline-count } { \l__tblr_vline_count_tl }
+    \tl_set_eq:NN \l__tblr_vline_num_tl \l__tblr_vline_count_tl
+  }
+
+\keys_define:nn { tblr-vline }
+  {
+    dash .code:n = \tl_set:Nn \l__tblr_vline_dash_tl { \exp_not:N \@tblr at dash #1 },
+    text .code:n = \tl_set:Nn \l__tblr_vline_dash_tl { \exp_not:N \@tblr at text #1 },
+    text .groups:n = { text },
+    wd .code:n = \tl_set:Nn \l__tblr_vline_wd_tl { \dim_eval:n {#1} },
+    fg .code:n = \tl_set:Nn \l__tblr_vline_fg_tl {#1},
+    abovepos .code:n = \tl_set:Nx \l__tblr_vline_abovepos_tl {#1},
+    belowpos .code:n = \tl_set:Nx \l__tblr_vline_belowpos_tl {#1},
+    unknown .code:n = \__tblr_vline_unknown_key:V \l_keys_key_str,
+  }
+
+\cs_new_protected:Npn \__tblr_vline_unknown_key:n #1
+  {
+    \prop_if_in:NnTF \g__tblr_defined_vdash_styles_prop {#1}
+      { \tl_set:Nn \l__tblr_vline_dash_tl { \exp_not:N \@tblr at dash #1 } }
+      {
+        \regex_match:NnTF \c__tblr_is_color_key_regex {#1}
+          { \tl_set:Nn \l__tblr_vline_fg_tl {#1} }
+          {
+            \tl_set_rescan:Nnn \l__tblr_v_tl {} {#1}
+            \tl_set:Nn \l__tblr_vline_wd_tl { \dim_eval:n {\l__tblr_v_tl} }
+          }
+      }
+  }
+\cs_generate_variant:Nn \__tblr_vline_unknown_key:n { V }
+
+\cs_new_protected_nopar:Npn \__tblr_set_vline_cmd:n #1
+  {
+    \__tblr_get_childs:nx {#1} { \int_use:N \c at rowcount }
+    \clist_map_inline:Nn \l_tblr_childs_clist
+      {
+        \__tblr_spec_gput:nee { vline }
+          { [##1][\int_use:N \c at colnum](\l__tblr_vline_num_tl) / @dash }
+          { \l__tblr_vline_dash_tl }
+        \tl_if_empty:NF \l__tblr_vline_wd_tl
+          {
+            \__tblr_spec_gput:nee { vline }
+              { [##1][\int_use:N \c at colnum](\l__tblr_vline_num_tl) / wd }
+              { \l__tblr_vline_wd_tl }
+          }
+        \tl_if_empty:NF \l__tblr_vline_fg_tl
+          {
+            \__tblr_spec_gput:nee { vline }
+              { [##1][\int_use:N \c at colnum](\l__tblr_vline_num_tl) / fg }
+              { \l__tblr_vline_fg_tl }
+          }
+        \tl_if_empty:NF \l__tblr_vline_abovepos_tl
+          {
+            \__tblr_spec_gput:nee { vline }
+              { [##1][\int_use:N \c at colnum](\l__tblr_vline_num_tl) / abovepos }
+              { \l__tblr_vline_abovepos_tl }
+          }
+        \tl_if_empty:NF \l__tblr_vline_belowpos_tl
+          {
+            \__tblr_spec_gput:nee { vline }
+              { [##1][\int_use:N \c at colnum](\l__tblr_vline_num_tl) / belowpos }
+              { \l__tblr_vline_belowpos_tl }
+          }
+      }
+  }
+
+%%% --------------------------------------------------------
+%%>  \section{Set Hborders and Vborders}
+%%% --------------------------------------------------------
+
+%% Hborder holds keys not related to a specified hline
+\NewTableCommand \hborder [1] { \tblr_set_hborder:n {#1} }
+
+\cs_new_protected:Npn \tblr_set_hborder:n #1
+  {
+    \keys_set:nn { tblr-hborder } {#1}
+  }
+
+\cs_new_protected:Npn \tblr_set_hborder:nn #1 #2
+  {
+    \group_begin:
+    \__tblr_get_childs:nx {#1} { \int_eval:n { \c at rowcount + 1 } }
+    \clist_map_inline:Nn \l_tblr_childs_clist
+      {
+        \int_set:Nn \c at rownum {##1}
+        \tblr_set_hborder:n {#2}
+      }
+    \group_end:
+  }
+
+%% This function is called when parsing table specifications
+%% Note that #1 always includes an outer pair of braces
+\cs_new_protected:Npn \__tblr_set_hborder_aux:nn #1 #2
+  {
+    \tblr_set_hborder:nn #1 {#2}
+  }
+\cs_generate_variant:Nn \__tblr_set_hborder_aux:nn { Vn }
+
+\keys_define:nn { tblr-hborder }
+  {
+    abovespace .code:n = \__tblr_row_gput_above:ne
+                          { belowsep } { \dim_eval:n {#1} },
+    belowspace .code:n = \__tblr_row_gput:ne { abovesep } { \dim_eval:n {#1} },
+    abovespace+ .code:n = \__tblr_row_gadd_dimen_above:ne
+                          { belowsep } { \dim_eval:n {#1} },
+    belowspace+ .code:n = \__tblr_row_gadd_dimen:ne
+                          { abovesep } { \dim_eval:n {#1} },
+    pagebreak   .code:n = \__tblr_hborder_gput_pagebreak:n {#1},
+    pagebreak   .default:n = yes,
+    baseline    .code:n = \__tblr_outer_gput_spec:ne
+                          { baseline } { - \int_use:N \c at rownum },
+  }
+
+\tl_const:Nn \c__tblr_pagebreak_yes_tl  {  1 }
+\tl_const:Nn \c__tblr_pagebreak_auto_tl {  0 }
+\tl_const:Nn \c__tblr_pagebreak_no_tl   { -1 }
+
+\cs_new_protected:Npn \__tblr_hborder_gput_pagebreak:n #1
+  {
+    \tl_if_exist:cT { c__tblr_pagebreak_ #1 _tl }
+      {
+        \__tblr_spec_gput:nee { hline }
+          { [\int_use:N \c at rownum] / @pagebreak }
+          { \tl_use:c { c__tblr_pagebreak_ #1 _tl } }
+      }
+  }
+
+%% Vborder holds keys not related to a specified vline
+\NewTableCommand \vborder [1] { \tblr_set_vborder:n {#1} }
+
+\cs_new_protected:Npn \tblr_set_vborder:n #1
+  {
+    \keys_set:nn { tblr-vborder } {#1}
+  }
+
+\cs_new_protected:Npn \tblr_set_vborder:nn #1 #2
+  {
+    \group_begin:
+    \__tblr_get_childs:nx {#1} { \int_eval:n { \c at colcount + 1 } }
+    \clist_map_inline:Nn \l_tblr_childs_clist
+      {
+        \int_set:Nn \c at colnum {##1}
+        \tblr_set_vborder:n {#2}
+      }
+    \group_end:
+  }
+
+%% This function is called when parsing table specifications
+%% Note that #1 always includes an outer pair of braces
+\cs_new_protected:Npn \__tblr_set_vborder_aux:nn #1 #2
+  {
+    \tblr_set_vborder:nn #1 {#2}
+  }
+\cs_generate_variant:Nn \__tblr_set_vborder_aux:nn { Vn }
+
+\keys_define:nn { tblr-vborder }
+  {
+    leftspace .code:n = \__tblr_column_gput_left:ne
+                          { rightsep } { \dim_eval:n {#1} },
+    rightspace .code:n = \__tblr_column_gput:ne { leftsep } { \dim_eval:n {#1} },
+    leftspace+ .code:n = \__tblr_column_gadd_dimen_left:ne
+                          { rightsep } { \dim_eval:n {#1} },
+    rightspace+ .code:n = \__tblr_column_gadd_dimen:ne
+                          { leftsep } { \dim_eval:n {#1} },
+  }
+
+%%% --------------------------------------------------------
+%%> \section{Set Cells}
+%%% --------------------------------------------------------
+
+%% \SetCells command for setting every cell in the table
+\NewTableCommand \SetCells [2] []
+  {
+    \tblr_set_every_cell:nn {#1} {#2}
+  }
+
+%% We put all code inside a group to avoid affecting other table commands
+\cs_new_protected:Npn \tblr_set_every_cell:nn #1 #2
+  {
+    \group_begin:
+    \int_step_inline:nn { \c at rowcount }
+      {
+        \int_set:Nn \c at rownum {##1}
+        \int_step_inline:nn { \c at colcount }
+          {
+            \int_set:Nn \c at colnum {####1}
+            \tblr_set_cell:nn {#1} {#2}
+          }
+      }
+    \group_end:
+  }
+
+%% Check the number of arguments and call \tblr_set_every_cell in different ways
+%% This function is called when parsing table specifications
+\cs_new_protected:Npn \__tblr_set_every_cell_aux:n #1
+  {
+    \tl_if_head_is_group:nTF {#1}
+      { \tblr_set_every_cell:nn #1 }
+      { \tblr_set_every_cell:nn {} {#1} }
+  }
+
+%% \SetCell command for multirow and/or multicolumn cells
+
+\NewTableCommand \SetCell [2] []
+  {
+    \tblr_set_cell:nn { #1 } { #2 }
+  }
+
+\tl_new:N \l__tblr_row_span_num_tl
+\tl_new:N \l__tblr_col_span_num_tl
+
+\cs_new_protected:Npn \tblr_set_cell:nn #1 #2
+  {
+    \tl_set:Nn \l__tblr_row_span_num_tl { 1 }
+    \tl_set:Nn \l__tblr_col_span_num_tl { 1 }
+    \keys_set:nn { tblr-cell-span } { #1 }
+    \keys_set:nn { tblr-cell-spec } { #2 }
+    \__tblr_set_span_spec:VV \l__tblr_row_span_num_tl \l__tblr_col_span_num_tl
+  }
+\cs_generate_variant:Nn \tblr_set_cell:nn { nV }
+
+\cs_new_protected:Npn \tblr_set_cell:nnnn #1 #2 #3 #4
+  {
+    \group_begin:
+    \__tblr_get_childs:nx {#1} { \int_use:N \c at rowcount }
+    \clist_set_eq:NN \l_tmpa_clist \l_tblr_childs_clist
+    \__tblr_get_childs:nx {#2} { \int_use:N \c at colcount }
+    \clist_set_eq:NN \l_tmpb_clist \l_tblr_childs_clist
+    \clist_map_inline:Nn \l_tmpa_clist
+      {
+        \int_set:Nn \c at rownum {##1}
+        \clist_map_inline:Nn \l_tmpb_clist
+          {
+            \int_set:Nn \c at colnum {####1}
+            \tblr_set_cell:nn {#3} {#4}
+          }
+      }
+    \group_end:
+  }
+
+%% Check the number of arguments and call \tblr_set_cell in different ways
+%% Note that #1 is always of the type {<i>}{<j>}
+%% This function is called when parsing table specifications
+\cs_new_protected:Npn \__tblr_set_cell_aux:nn #1 #2
+  {
+    \tl_if_head_is_group:nTF {#2}
+      { \tblr_set_cell:nnnn #1 #2 }
+      { \tblr_set_cell:nnnn #1 {} {#2} }
+  }
+\cs_generate_variant:Nn \__tblr_set_cell_aux:nn { Vn }
+
+\keys_define:nn { tblr-cell-span }
+  {
+    r .tl_set:N = \l__tblr_row_span_num_tl,
+    c .tl_set:N = \l__tblr_col_span_num_tl,
+  }
+
+\keys_define:nn { tblr-cell-spec }
+  {
+    halign  .code:n = \__tblr_cell_gput:nn { halign } {#1},
+    valign  .code:n = \__tblr_cell_gput:nn { valign } {#1},
+    j       .meta:n = { halign = j },
+    l       .meta:n = { halign = l },
+    c       .meta:n = { halign = c },
+    r       .meta:n = { halign = r },
+    t       .meta:n = { valign = t },
+    p       .meta:n = { valign = t },
+    m       .meta:n = { valign = m },
+    b       .meta:n = { valign = b },
+    h       .meta:n = { valign = h },
+    f       .meta:n = { valign = f },
+    wd      .code:n = \__tblr_cell_gput:ne { width } {#1},
+    bg      .code:n = \__tblr_cell_gput:ne { background } {#1},
+    fg      .code:n = \__tblr_cell_gput:ne { foreground } {#1},
+    font    .code:n = \__tblr_cell_gput:nn { font } { #1 \selectfont },
+    mode    .code:n = \__tblr_cell_gput:nn { mode } {#1},
+    $       .meta:n = { mode = math },
+    $$      .meta:n = { mode = dmath },
+    cmd     .code:n = \__tblr_cell_gput:nn { cmd } {#1},
+    preto   .code:n = \__tblr_cell_preto_text:n {#1},
+    appto   .code:n = \__tblr_cell_appto_text:n {#1},
+    unknown .code:n = \__tblr_cell_unknown_key:V \l_keys_key_str,
+  }
+
+\cs_new_protected:Npn \__tblr_cell_gput:nn #1 #2
+  {
+    \__tblr_data_gput:neenn { cell }
+      { \int_use:N \c at rownum } { \int_use:N \c at colnum } {#1} {#2}
+  }
+\cs_generate_variant:Nn \__tblr_cell_gput:nn { ne }
+
+\cs_new_protected:Npn \__tblr_cell_gput:nnnn #1 #2 #3 #4
+  {
+    \__tblr_data_gput:nnnnn { cell } {#1} {#2} {#3} {#4}
+  }
+\cs_generate_variant:Nn \__tblr_cell_gput:nnnn
+  { nenn, ennn, eenn, nene, enne, eene }
+
+\tl_new:N \l__tblr_cell_text_tl
+
+\cs_new_protected:Npn \__tblr_cell_preto_text:n #1
+  {
+    \__tblr_cell_preto_text:een
+      { \int_use:N \c at rownum } { \int_use:N \c at colnum } {#1}
+  }
+
+\cs_new_protected:Npn \__tblr_cell_preto_text:nnn #1 #2 #3
+  {
+    \tl_set:Nx \l__tblr_cell_text_tl { \__tblr_spec_item:nn { text } { [#1][#2] } }
+    \tl_put_left:Nn \l__tblr_cell_text_tl {#3}
+    \__tblr_spec_gput:nnV { text } { [#1][#2] } \l__tblr_cell_text_tl
+  }
+\cs_generate_variant:Nn \__tblr_cell_preto_text:nnn { nen, enn, een }
+
+\cs_new_protected:Npn \__tblr_cell_appto_text:n #1
+  {
+    \__tblr_cell_appto_text:een
+      { \int_use:N \c at rownum } { \int_use:N \c at colnum } {#1}
+  }
+
+\cs_new_protected:Npn \__tblr_cell_appto_text:nnn #1 #2 #3
+  {
+    \tl_set:Nx \l__tblr_cell_text_tl { \__tblr_spec_item:ne { text } { [#1][#2] } }
+    \tl_put_right:Nn \l__tblr_cell_text_tl {#3}
+    \__tblr_spec_gput:neV { text } { [#1][#2] } \l__tblr_cell_text_tl
+  }
+\cs_generate_variant:Nn \__tblr_cell_appto_text:nnn { nen, enn, een }
+
+\cs_new_protected:Npn \__tblr_cell_unknown_key:n #1
+  {
+    \regex_match:NnTF \c__tblr_is_color_key_regex {#1}
+      {
+        \__tblr_data_gput:neene { cell }
+          { \int_use:N \c at rownum } { \int_use:N \c at colnum } { background } {#1}
+      }
+      {
+        \tl_set_rescan:Nnn \l__tblr_v_tl {} {#1}
+        \__tblr_data_gput:neene { cell }
+          { \int_use:N \c at rownum } { \int_use:N \c at colnum } { width }
+          { \dim_eval:n { \l__tblr_v_tl } }
+      }
+  }
+\cs_generate_variant:Nn \__tblr_cell_unknown_key:n { V }
+
+\cs_new_protected:Npn \__tblr_set_span_spec:nn #1 #2
+  {
+    \int_compare:nNnT { #1 } > { 1 }
+      {
+        \__tblr_prop_gput:nnn { inner } { rowspan } { true }
+        \__tblr_data_gput:neenn { cell }
+          { \int_use:N \c at rownum } { \int_use:N \c at colnum } { rowspan } {#1}
+      }
+    \int_compare:nNnT { #2 } > { 1 }
+      {
+        \__tblr_prop_gput:nnn { inner } { colspan } { true }
+        \__tblr_data_gput:neenn { cell }
+          { \int_use:N \c at rownum } { \int_use:N \c at colnum } { colspan } {#2}
+      }
+    \int_step_variable:nnNn
+      { \int_use:N \c at rownum } { \int_eval:n { \c at rownum + #1 - 1 } } \l__tblr_i_tl
+      {
+        \int_step_variable:nnNn
+          { \int_use:N \c at colnum } { \int_eval:n { \c at colnum + #2 - 1 } }
+          \l__tblr_j_tl
+          {
+            \bool_lazy_and:nnF
+              { \int_compare_p:nNn { \l__tblr_i_tl } = { \c at rownum } }
+              { \int_compare_p:nNn { \l__tblr_j_tl } = { \c at colnum } }
+              {
+                \__tblr_data_gput:neenn { cell }
+                  { \l__tblr_i_tl } { \l__tblr_j_tl } { omit } {1}
+              }
+            \int_compare:nNnF { \l__tblr_i_tl } = { \c at rownum }
+              {
+                \__tblr_spec_gput:nen { hline }
+                  { [\l__tblr_i_tl][\l__tblr_j_tl] / omit } {true}
+              }
+            \int_compare:nNnF { \l__tblr_j_tl } = { \c at colnum }
+              {
+                \__tblr_spec_gput:nee { vline }
+                  { [\l__tblr_i_tl][\l__tblr_j_tl] / omit } {true}
+              }
+          }
+      }
+    %% Make continuous borders for multirow cells
+    \tl_set:Nx \l__tblr_n_tl
+      {
+        \int_max:nn
+          {
+            \__tblr_spec_item:ne { vline } { [\int_use:N \c at colnum] / @vline-count }
+          }
+          { 1 }
+      }
+    \int_step_variable:nnNn
+      { \c at rownum } { \int_eval:n { \c at rownum + #1 - 2 } } \l__tblr_i_tl
+      {
+        \__tblr_spec_gput:nee { vline }
+          { [\l__tblr_i_tl][\int_use:N \c at colnum](\l__tblr_n_tl) / belowpos } {1}
+        \__tblr_spec_gput:nee { vline }
+          { [\l__tblr_i_tl][\int_eval:n {\c at colnum + #2}](1) / belowpos } {1}
+      }
+  }
+\cs_generate_variant:Nn \__tblr_set_span_spec:nn { VV }
+
+%% Obsolete \multicolumn and \multirow commands
+
+\msg_new:nnn { tabularray } { obsolete-multicolumn }
+  { \multicolumn ~ is ~ obsolete; ~ use ~ \SetCell ~ instead. }
+
+\msg_new:nnn { tabularray } { obsolete-multirow }
+  { \multirow ~ is ~ obsolete; ~ use ~ \SetCell ~ instead. }
+
+\NewTableCommand \multicolumn [2]
+  {
+    \msg_error:nn { tabularray } { obsolete-multicolumn }
+  }
+
+\NewTableCommand \multirow [3] [m]
+  {
+    \msg_error:nn { tabularray } { obsolete-multirow }
+  }
+
+%%% --------------------------------------------------------
+%%> \section{Set Columns and Rows}
+%%% --------------------------------------------------------
+
+%% \SetColumns command for setting every column in the table
+\NewTableCommand \SetColumns [2] []
+  {
+    \tblr_set_every_column:nn {#1} {#2}
+  }
+
+%% We put all code inside a group to avoid affecting other table commands
+\cs_new_protected:Npn \tblr_set_every_column:nn #1 #2
+  {
+    \group_begin:
+    \int_step_inline:nn { \c at colcount }
+      {
+        \int_set:Nn \c at colnum {##1}
+        \tblr_set_column:nn {#1} {#2}
+      }
+    \group_end:
+  }
+
+%% Check the number of arguments and call \tblr_set_every_column in different ways
+%% This function is called when parsing table specifications
+\cs_new_protected:Npn \__tblr_set_every_column_aux:n #1
+  {
+    \tl_if_head_is_group:nTF {#1}
+      { \tblr_set_every_column:nn #1 }
+      { \tblr_set_every_column:nn {} {#1} }
+  }
+
+%% \SetColumn command for current column or each cells in the column
+
+\NewTableCommand \SetColumn [2] []
+  {
+    \tblr_set_column:nn {#1} {#2}
+  }
+
+\cs_new_protected:Npn \tblr_set_column:nn #1 #2
+  {
+    \keys_set:nn { tblr-column } {#2}
+  }
+
+\cs_new_protected:Npn \tblr_set_column:nnn #1 #2 #3
+  {
+    \group_begin:
+    \__tblr_get_childs:nx {#1} { \int_use:N \c at colcount }
+    \clist_map_inline:Nn \l_tblr_childs_clist
+      {
+        \int_set:Nn \c at colnum {##1}
+        \tblr_set_column:nn {#2} {#3}
+      }
+    \group_end:
+  }
+
+%% Check the number of arguments and call \tblr_set_column in different ways
+%% Note that #1 always includes an outer pair of braces
+%% This function is called when parsing table specifications
+\cs_new_protected:Npn \__tblr_set_column_aux:nn #1 #2
+  {
+    \tl_if_head_is_group:nTF {#2}
+      { \tblr_set_column:nnn #1 #2 }
+      { \tblr_set_column:nnn #1 {} {#2} }
+  }
+\cs_generate_variant:Nn \__tblr_set_column_aux:nn { Vn }
+
+\keys_define:nn { tblr-column }
+  {
+    halign    .code:n = \__tblr_column_gput_cell:nn { halign } {#1},
+    valign    .code:n = \__tblr_column_gput_cell:nn { valign } {#1},
+    j         .meta:n = { halign = j },
+    l         .meta:n = { halign = l },
+    c         .meta:n = { halign = c },
+    r         .meta:n = { halign = r },
+    t         .meta:n = { valign = t },
+    p         .meta:n = { valign = t },
+    m         .meta:n = { valign = m },
+    b         .meta:n = { valign = b },
+    h         .meta:n = { valign = h },
+    f         .meta:n = { valign = f },
+    bg        .code:n = \__tblr_column_gput_cell:nn { background } {#1},
+    fg        .code:n = \__tblr_column_gput_cell:nn { foreground } {#1},
+    font      .code:n = \__tblr_column_gput_cell:nn { font } { #1 \selectfont },
+    mode      .code:n = \__tblr_column_gput_cell:nn { mode } {#1},
+    $         .meta:n = { mode = math },
+    $$        .meta:n = { mode = dmath },
+    cmd       .code:n = \__tblr_column_gput_cell:nn { cmd } {#1},
+    wd        .code:n = \__tblr_column_gput:ne { width } { \dim_eval:n {#1} },
+    co        .code:n = \__tblr_column_gput:ne { coefficient } {#1},
+    preto     .code:n = \__tblr_preto_text_for_every_column_cell:n {#1},
+    appto     .code:n = \__tblr_appto_text_for_every_column_cell:n {#1},
+    leftsep   .code:n = \__tblr_column_gput:ne { leftsep } { \dim_eval:n {#1} },
+    rightsep  .code:n = \__tblr_column_gput:ne { rightsep } { \dim_eval:n {#1} },
+    colsep    .meta:n = { leftsep = #1, rightsep = #1},
+    leftsep+  .code:n = \__tblr_column_gadd_dimen:ne
+                          { leftsep } { \dim_eval:n {#1} },
+    rightsep+ .code:n = \__tblr_column_gadd_dimen:ne
+                          { rightsep } { \dim_eval:n {#1} },
+    colsep+   .meta:n = { leftsep+ = #1, rightsep+ = #1},
+    unknown   .code:n = \__tblr_column_unknown_key:V \l_keys_key_str,
+  }
+
+%% #1: key; #2: value
+\cs_new_protected:Npn \__tblr_column_gput:nn #1 #2
+  {
+    \__tblr_data_gput:nenn { column } { \int_use:N \c at colnum } {#1} {#2}
+  }
+\cs_generate_variant:Nn \__tblr_column_gput:nn { ne }
+
+\cs_new_protected:Npn \__tblr_column_gput_left:nn #1 #2
+  {
+    \__tblr_data_gput:nenn { column } { \int_eval:n { \c at colnum - 1 } } {#1} {#2}
+  }
+\cs_generate_variant:Nn \__tblr_column_gput_left:nn { ne }
+
+\cs_new_protected:Npn \__tblr_column_gadd_dimen:nn #1 #2
+  {
+    \__tblr_data_gadd_dimen_value:nenn { column }
+      { \int_use:N \c at colnum } {#1} {#2}
+  }
+\cs_generate_variant:Nn \__tblr_column_gadd_dimen:nn { ne }
+
+\cs_new_protected:Npn \__tblr_column_gadd_dimen_left:nn #1 #2
+  {
+    \__tblr_data_gadd_dimen_value:nenn { column }
+      { \int_eval:n { \c at colnum - 1 } } {#1} {#2}
+  }
+\cs_generate_variant:Nn \__tblr_column_gadd_dimen_left:nn { ne }
+
+%% #1: key; #2: value
+\cs_new_protected:Npn \__tblr_column_gput_cell:nn #1 #2
+  {
+    \int_step_inline:nn { \c at rowcount }
+      {
+        \__tblr_cell_gput:nenn {##1} { \int_use:N \c at colnum } {#1} {#2}
+      }
+  }
+\cs_generate_variant:Nn \__tblr_column_gput_cell:nn { ne }
+
+\cs_new_protected:Npn \__tblr_preto_text_for_every_column_cell:n #1
+  {
+    \int_step_inline:nn { \c at rowcount }
+      {
+        \__tblr_cell_preto_text:nen {##1} { \int_use:N \c at colnum } {#1}
+      }
+  }
+
+\cs_new_protected:Npn \__tblr_appto_text_for_every_column_cell:n #1
+  {
+    \int_step_inline:nn { \c at rowcount }
+      {
+        \__tblr_cell_appto_text:nen {##1} { \int_use:N \c at colnum } {#1}
+      }
+  }
+
+\regex_const:Nn \c__tblr_is_number_key_regex { ^[\+\-]? (\d+|\d*\.\d+)$ }
+
+\cs_new_protected:Npn \__tblr_column_unknown_key:n #1
+  {
+    \regex_match:NnTF \c__tblr_is_number_key_regex {#1}
+      { \__tblr_column_gput:ne { coefficient } {#1} }
+      {
+        \regex_match:NnTF \c__tblr_is_color_key_regex {#1}
+          { \__tblr_column_gput_cell:nn { background } {#1} }
+          {
+            \tl_set_rescan:Nnn \l__tblr_v_tl {} {#1}
+            \__tblr_column_gput:ne { width } { \dim_eval:n { \l__tblr_v_tl } }
+          }
+      }
+  }
+\cs_generate_variant:Nn \__tblr_column_unknown_key:n { V }
+
+%% \SetRows command for setting every row in the table
+\NewTableCommand \SetRows [2] []
+  {
+    \tblr_set_every_row:nn {#1} {#2}
+  }
+
+%% We put all code inside a group to avoid affecting other table commands
+\cs_new_protected:Npn \tblr_set_every_row:nn #1 #2
+  {
+    \group_begin:
+    \int_step_inline:nn { \c at rowcount }
+      {
+        \int_set:Nn \c at rownum {##1}
+        \tblr_set_row:nn {#1} {#2}
+      }
+    \group_end:
+  }
+
+%% Check the number of arguments and call \tblr_set_every_row in different ways
+%% This function is called when parsing table specifications
+\cs_new_protected:Npn \__tblr_set_every_row_aux:n #1
+  {
+    \tl_if_head_is_group:nTF {#1}
+      { \tblr_set_every_row:nn #1 }
+      { \tblr_set_every_row:nn {} {#1} }
+  }
+
+%% \SetRow command for current row or each cells in the row
+
+\NewTableCommand \SetRow [2] []
+  {
+    \tblr_set_row:nn {#1} {#2}
+  }
+
+\cs_new_protected:Npn \tblr_set_row:nn #1 #2
+  {
+    \keys_set:nn { tblr-row } {#2}
+  }
+
+\cs_new_protected:Npn \tblr_set_row:nnn #1 #2 #3
+  {
+    \group_begin:
+    \__tblr_get_childs:nx {#1} { \int_use:N \c at rowcount }
+    \clist_map_inline:Nn \l_tblr_childs_clist
+      {
+        \int_set:Nn \c at rownum {##1}
+        \tblr_set_row:nn {#2} {#3}
+      }
+    \group_end:
+  }
+
+%% Check the number of arguments and call \tblr_set_row in different ways
+%% Note that #1 always includes an outer pair of braces
+%% This function is called when parsing table specifications
+\cs_new_protected:Npn \__tblr_set_row_aux:nn #1 #2
+  {
+    \tl_if_head_is_group:nTF {#2}
+      { \tblr_set_row:nnn #1 #2 }
+      { \tblr_set_row:nnn #1 {} {#2} }
+  }
+\cs_generate_variant:Nn \__tblr_set_row_aux:nn { Vn }
+
+\keys_define:nn { tblr-row }
+  {
+    halign    .code:n = \__tblr_row_gput_cell:nn { halign } {#1},
+    valign    .code:n = \__tblr_row_gput_cell:nn { valign } {#1},
+    j         .meta:n = { halign = j },
+    l         .meta:n = { halign = l },
+    c         .meta:n = { halign = c },
+    r         .meta:n = { halign = r },
+    t         .meta:n = { valign = t },
+    p         .meta:n = { valign = t },
+    m         .meta:n = { valign = m },
+    b         .meta:n = { valign = b },
+    h         .meta:n = { valign = h },
+    f         .meta:n = { valign = f },
+    bg        .code:n = \__tblr_row_gput_cell:nn { background } {#1},
+    fg        .code:n = \__tblr_row_gput_cell:nn { foreground } {#1},
+    font      .code:n = \__tblr_row_gput_cell:nn { font } { #1 \selectfont },
+    mode      .code:n = \__tblr_row_gput_cell:nn { mode } {#1},
+    $         .meta:n = { mode = math },
+    $$        .meta:n = { mode = dmath },
+    cmd       .code:n = \__tblr_row_gput_cell:nn { cmd } {#1},
+    ht        .code:n = \__tblr_row_gput:ne { height } { \dim_eval:n {#1} },
+    co        .code:n = \__tblr_row_gput:ne { coefficient } {#1},
+    preto     .code:n = \__tblr_preto_text_for_every_row_cell:n {#1},
+    appto     .code:n = \__tblr_appto_text_for_every_row_cell:n {#1},
+    abovesep  .code:n = \__tblr_row_gput:ne { abovesep } { \dim_eval:n {#1} },
+    belowsep  .code:n = \__tblr_row_gput:ne { belowsep } { \dim_eval:n {#1} },
+    rowsep    .meta:n = { abovesep = #1, belowsep = #1},
+    abovesep+ .code:n = \__tblr_row_gadd_dimen:ne { abovesep } { \dim_eval:n {#1} },
+    belowsep+ .code:n = \__tblr_row_gadd_dimen:ne { belowsep } { \dim_eval:n {#1} },
+    rowsep+   .meta:n = { abovesep+ = #1, belowsep+ = #1},
+    baseline  .code:n = \__tblr_outer_gput_spec:ne
+                          { baseline } { \int_use:N \c at rownum },
+    unknown   .code:n = \__tblr_row_unknown_key:V \l_keys_key_str,
+  }
+
+%% #1: key; #2: value
+\cs_new_protected:Npn \__tblr_row_gput:nn #1 #2
+  {
+    \__tblr_data_gput:nenn { row } { \int_use:N \c at rownum } {#1} {#2}
+  }
+\cs_generate_variant:Nn \__tblr_row_gput:nn { ne }
+
+\cs_new_protected:Npn \__tblr_row_gput_above:nn #1 #2
+  {
+    \__tblr_data_gput:nenn { row } { \int_eval:n { \c at rownum - 1 } } {#1} {#2}
+  }
+\cs_generate_variant:Nn \__tblr_row_gput_above:nn { ne }
+
+\cs_new_protected:Npn \__tblr_row_gadd_dimen:nn #1 #2
+  {
+    \__tblr_data_gadd_dimen_value:nenn { row } { \int_use:N \c at rownum } {#1} {#2}
+  }
+\cs_generate_variant:Nn \__tblr_row_gadd_dimen:nn { ne }
+
+\cs_new_protected:Npn \__tblr_row_gadd_dimen_above:nn #1 #2
+  {
+    \__tblr_data_gadd_dimen_value:nenn { row }
+      { \int_eval:n { \c at rownum - 1 } } {#1} {#2}
+  }
+\cs_generate_variant:Nn \__tblr_row_gadd_dimen_above:nn { ne }
+
+%% #1: key; #2: value
+\cs_new_protected:Npn \__tblr_row_gput_cell:nn #1 #2
+  {
+    \int_step_inline:nn { \c at colcount }
+      {
+        \__tblr_cell_gput:ennn { \int_use:N \c at rownum } {##1} {#1} {#2}
+      }
+  }
+\cs_generate_variant:Nn \__tblr_row_gput_cell:nn { ne }
+
+\cs_new_protected:Npn \__tblr_preto_text_for_every_row_cell:n #1
+  {
+    \int_step_inline:nn { \c at colcount }
+      {
+        \__tblr_cell_preto_text:enn { \int_use:N \c at rownum } {##1} {#1}
+      }
+  }
+
+\cs_new_protected:Npn \__tblr_appto_text_for_every_row_cell:n #1
+  {
+    \int_step_inline:nn { \c at colcount }
+      {
+        \__tblr_cell_appto_text:enn { \int_use:N \c at rownum } {##1} {#1}
+      }
+  }
+
+\cs_new_protected:Npn \__tblr_row_unknown_key:n #1
+  {
+    \regex_match:NnTF \c__tblr_is_number_key_regex {#1}
+      {
+        \__tblr_data_gput:nene { row } { \int_use:N \c at rownum }
+          { coefficient } {#1}
+      }
+      {
+        \regex_match:NnTF \c__tblr_is_color_key_regex {#1}
+          { \__tblr_row_gput_cell:nn { background } {#1} }
+          {
+            \tl_set_rescan:Nnn \l__tblr_v_tl {} {#1}
+            \__tblr_row_gput:ne { height } { \dim_eval:n { \l__tblr_v_tl } }
+          }
+      }
+  }
+\cs_generate_variant:Nn \__tblr_row_unknown_key:n { V }
+
+\NewTableCommand \pagebreak [1] [4]
+  {
+    \hborder { pagebreak = yes }
+  }
+
+\NewTableCommand \nopagebreak [1] [4]
+  {
+    \hborder { pagebreak = no }
+  }
+
+%%% --------------------------------------------------------
+%%> \section{Column Types and Row Types}
+%%% --------------------------------------------------------
+
+%% Some primitive column/row types
+
+\str_const:Nn \c_tblr_primitive_colrow_types_str { Q | < > }
+\tl_new:N \g__tblr_expanded_colrow_spec_tl
+
+\exp_args:Nc \NewDocumentCommand { tblr_primitive_column_type_ Q } { O{} }
+  {
+    \keys_set:nn { tblr-column } { #1 }
+    \int_incr:N \c at colnum
+    \__tblr_execute_colrow_spec_next:N
+  }
+\exp_args:Nc \NewDocumentCommand { tblr_column_type_ Q } { O{} }
+  {
+    \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { Q[#1] }
+    \__tblr_expand_colrow_spec_next:N
+  }
+
+\exp_args:Nc \NewDocumentCommand { tblr_primitive_row_type_ Q } { O{} }
+  {
+    \keys_set:nn { tblr-row } { #1 }
+    \int_incr:N \c at rownum
+    \__tblr_execute_colrow_spec_next:N
+  }
+\exp_args:Nc \NewDocumentCommand { tblr_row_type_ Q } { O{} }
+  {
+    \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { Q[#1] }
+    \__tblr_expand_colrow_spec_next:N
+  }
+
+\exp_args:Nc \NewDocumentCommand { tblr_primitive_column_type_ | } { O{} }
+  {
+    \vline [#1]
+    \__tblr_execute_colrow_spec_next:N
+  }
+\exp_args:Nc \NewDocumentCommand { tblr_column_type_ | } { O{} }
+  {
+    \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { |[#1] }
+    \__tblr_expand_colrow_spec_next:N
+  }
+
+\exp_args:Nc \NewDocumentCommand { tblr_primitive_row_type_ | } { O{} }
+  {
+    \hline [#1]
+    \__tblr_execute_colrow_spec_next:N
+  }
+\exp_args:Nc \NewDocumentCommand { tblr_row_type_ | } { O{} }
+  {
+    \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { |[#1] }
+    \__tblr_expand_colrow_spec_next:N
+  }
+
+\exp_args:Nc \NewDocumentCommand { tblr_primitive_column_type_ > } { O{} m }
+  {
+    \tl_if_blank:nF {#1}
+      {
+        \__tblr_data_gput:nene
+          { column }
+          { \int_use:N \c at colnum } { leftsep }
+          { \dim_eval:n {#1} }
+      }
+    \tl_if_blank:nF {#2}
+      {
+        \__tblr_preto_text_for_every_column_cell:n {#2}
+      }
+    \__tblr_execute_colrow_spec_next:N
+  }
+\exp_args:Nc \NewDocumentCommand { tblr_column_type_ > } { O{} m }
+  {
+    \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { >[#1]{#2} }
+    \__tblr_expand_colrow_spec_next:N
+  }
+
+\exp_args:Nc \NewDocumentCommand { tblr_primitive_row_type_ > } { O{} m }
+  {
+    \tl_if_blank:nF {#1}
+      {
+        \__tblr_data_gput:nene { row } { \int_use:N \c at rownum }
+          { abovesep } { \dim_eval:n { #1 } }
+      }
+    \tl_if_blank:nF {#2}
+      {
+        \__tblr_preto_text_for_every_row_cell:n {#2}
+      }
+    \__tblr_execute_colrow_spec_next:N
+  }
+\exp_args:Nc \NewDocumentCommand { tblr_row_type_ > } { O{} m }
+  {
+    \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { >[#1]{#2} }
+    \__tblr_expand_colrow_spec_next:N
+  }
+
+\exp_args:Nc \NewDocumentCommand { tblr_primitive_column_type_ < } { O{} m }
+  {
+    \tl_if_blank:nF {#1}
+      {
+        \__tblr_data_gput:nene { column }
+          { \int_eval:n {\c at colnum - 1} } { rightsep } { \dim_eval:n {#1} }
+      }
+    \tl_if_blank:nF {#2}
+      {
+        \group_begin:
+        \int_decr:N \c at colnum
+        \__tblr_appto_text_for_every_column_cell:n {#2}
+        \group_end:
+      }
+    \__tblr_execute_colrow_spec_next:N
+  }
+\exp_args:Nc \NewDocumentCommand { tblr_column_type_ < } { O{} m }
+  {
+    \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { <[#1]{#2} }
+    \__tblr_expand_colrow_spec_next:N
+  }
+
+\exp_args:Nc \NewDocumentCommand { tblr_primitive_row_type_ < } { O{} m }
+  {
+    \tl_if_blank:nF {#1}
+      {
+        \__tblr_data_gput:nene { row } { \int_eval:n {\c at rownum - 1} }
+          { belowsep } { \dim_eval:n {#1} }
+      }
+    \tl_if_blank:nF {#2}
+      {
+        \group_begin:
+        \int_decr:N \c at rownum
+        \__tblr_appto_text_for_every_row_cell:n {#2}
+        \group_end:
+      }
+    \__tblr_execute_colrow_spec_next:N
+  }
+\exp_args:Nc \NewDocumentCommand { tblr_row_type_ < } { O{} m }
+  {
+    \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { <[#1]{#2} }
+    \__tblr_expand_colrow_spec_next:N
+  }
+
+%% \NewColumnType/\NewRowType command and predefined column/row types
+
+\str_new:N \g_tblr_used_column_types_str
+\str_gset_eq:NN \g_tblr_used_column_types_str \c_tblr_primitive_colrow_types_str
+
+\str_new:N \g_tblr_used_row_types_str
+\str_gset_eq:NN \g_tblr_used_row_types_str \c_tblr_primitive_colrow_types_str
+
+\bool_new:N \g__tblr_colrow_spec_expand_stop_bool
+\tl_new:N \g__tblr_column_or_row_tl
+
+\msg_new:nnn { tabularray } { used-colrow-type }
+  { #1 ~ type ~ name ~ #2 ~ has ~ been ~ used! }
+
+\NewDocumentCommand \NewColumnType { m O{0} o m }
+  {
+    \tl_set:Nn \g__tblr_column_or_row_tl { column }
+    \__tblr_new_column_or_row_type:nnnn {#1} {#2} {#3} {#4}
+  }
+
+\NewDocumentCommand \NewRowType { m O{0} o m }
+  {
+    \tl_set:Nn \g__tblr_column_or_row_tl { row }
+    \__tblr_new_column_or_row_type:nnnn {#1} {#2} {#3} {#4}
+  }
+
+\NewDocumentCommand \NewColumnRowType { m O{0} o m }
+  {
+    \tl_set:Nn \g__tblr_column_or_row_tl { column }
+    \__tblr_new_column_or_row_type:nnnn {#1} {#2} {#3} {#4}
+    \tl_set:Nn \g__tblr_column_or_row_tl { row }
+    \__tblr_new_column_or_row_type:nnnn {#1} {#2} {#3} {#4}
+  }
+
+\cs_new_protected:Npn \__tblr_new_column_or_row_type:nnnn #1 #2 #3 #4
+  {
+    \str_if_in:cnTF { g_tblr_used_ \g__tblr_column_or_row_tl _types_str } {#1}
+      {
+        \tl_if_eq:NnTF \g__tblr_column_or_row_tl { row }
+          { \msg_error:nnnn { tabularray } { used-colrow-type } { Row } {#1} }
+          { \msg_error:nnnn { tabularray } { used-colrow-type } { Column } {#1} }
+        \str_log:c { g_tblr_used_ \g__tblr_column_or_row_tl _types_str }
+      }
+      {
+        \__tblr_make_xparse_arg_spec:nnN {#2} {#3} \l__tblr_a_tl
+        \exp_args:NcV \NewDocumentCommand
+          { tblr_ \g__tblr_column_or_row_tl _type_ #1 } \l__tblr_a_tl
+          {
+            \bool_gset_false:N \g__tblr_colrow_spec_expand_stop_bool
+            \tl_gput_right:Nf \g__tblr_expanded_colrow_spec_tl {#4}
+            \__tblr_expand_colrow_spec_next:N
+          }
+        \str_gput_right:cn
+          { g_tblr_used_ \g__tblr_column_or_row_tl _types_str } {#1}
+      }
+  }
+
+\NewColumnRowType { l } { Q[l] }
+\NewColumnRowType { c } { Q[c] }
+\NewColumnRowType { r } { Q[r] }
+\NewColumnRowType { j } { Q[j] }
+
+\NewColumnType { t } [1] { Q[t,wd=#1] }
+\NewColumnType { p } [1] { Q[p,wd=#1] }
+\NewColumnType { m } [1] { Q[m,wd=#1] }
+\NewColumnType { b } [1] { Q[b,wd=#1] }
+\NewColumnType { h } [1] { Q[h,wd=#1] }
+\NewColumnType { f } [1] { Q[f,wd=#1] }
+
+\NewRowType { t } [1] { Q[t,ht=#1] }
+\NewRowType { p } [1] { Q[p,ht=#1] }
+\NewRowType { m } [1] { Q[m,ht=#1] }
+\NewRowType { b } [1] { Q[b,ht=#1] }
+\NewRowType { h } [1] { Q[h,ht=#1] }
+\NewRowType { f } [1] { Q[f,ht=#1] }
+
+\NewColumnRowType { X } [1][] { Q[co=1,#1] }
+
+\NewColumnRowType { ! } [1] { |[text={#1}] }
+\NewColumnRowType { @ } [1] { <[0pt]{} |[text={#1}] >[0pt]{} }
+\NewColumnRowType { * } [2] { \prg_replicate:nn {#1} {#2} }
+
+\cs_new_protected:Npn \__tblr_parse_colrow_spec:nn #1 #2
+  {
+    \tl_gset:Nn \g__tblr_column_or_row_tl {#1}
+    \tl_gset:Nn \g__tblr_expanded_colrow_spec_tl {#2}
+    \__tblr_expand_colrow_spec:N \g__tblr_expanded_colrow_spec_tl
+    \__tblr_execute_colrow_spec:N \g__tblr_expanded_colrow_spec_tl
+  }
+
+%% Expand defined column/row types
+
+\cs_new_protected:Npn \__tblr_expand_colrow_spec:N #1
+  {
+    \bool_do_until:Nn \g__tblr_colrow_spec_expand_stop_bool
+      {
+        \LogTblrTracing { colspec, rowspec }
+        \bool_gset_true:N \g__tblr_colrow_spec_expand_stop_bool
+        \tl_set_eq:NN \l_tmpa_tl #1
+        \tl_gclear:N #1
+        \exp_last_unbraced:NV
+          \__tblr_expand_colrow_spec_next:N \l_tmpa_tl \scan_stop:
+      }
+  }
+
+\msg_new:nnn { tabularray } { unexpandable-colrow-type }
+  { Unexpandable ~ command ~ #2 inside ~ #1 ~ type! }
+
+\msg_new:nnn { tabularray } { unknown-colrow-type }
+  { Unknown ~ #1 ~ type ~ #2! }
+
+\cs_new_protected:Npn \__tblr_expand_colrow_spec_next:N #1
+  {
+    \token_if_eq_catcode:NNTF #1 \scan_stop:
+      {
+        \token_if_eq_meaning:NNF #1 \scan_stop:
+          {
+            \msg_error:nnVn { tabularray } { unexpandable-colrow-type }
+              \g__tblr_column_or_row_tl {#1}
+          }
+      }
+      {
+        \str_if_in:cnTF { g_tblr_used_ \g__tblr_column_or_row_tl _types_str } {#1}
+          {
+            %% Note that #1 may be an active character (see issue #58)
+            \cs:w tblr_ \g__tblr_column_or_row_tl _type_ \token_to_str:N #1 \cs_end:
+          }
+          {
+            \msg_error:nnVn { tabularray } { unknown-colrow-type }
+              \g__tblr_column_or_row_tl {#1}
+            \str_log:c { g_tblr_used_ \g__tblr_column_or_row_tl _types_str }
+          }
+      }
+  }
+
+%% Execute primitive column/row types
+
+\cs_new_protected:Npn \__tblr_execute_colrow_spec:N #1
+  {
+    \tl_if_eq:NnTF \g__tblr_column_or_row_tl { row }
+      { \int_set:Nn \c at rownum {1} }
+      { \int_set:Nn \c at colnum {1} }
+    \exp_last_unbraced:NV \__tblr_execute_colrow_spec_next:N #1 \scan_stop:
+  }
+
+\cs_new_protected:Npn \__tblr_execute_colrow_spec_next:N #1
+  {
+    \token_if_eq_meaning:NNF #1 \scan_stop:
+      { \cs:w tblr_primitive_ \g__tblr_column_or_row_tl _type_  #1 \cs_end: }
+  }
+
+%%% --------------------------------------------------------
+%%> \section{Set Environments and New Environments}
+%%% --------------------------------------------------------
+
+\tl_new:N \l__tblr_initial_tblr_outer_tl
+\tl_set:Nn \l__tblr_initial_tblr_outer_tl
+  {
+    halign = c, baseline = m, headsep = 6pt, footsep = 6pt,
+    presep = 1.5\bigskipamount, postsep = 1.5\bigskipamount,
+  }
+
+%% #1: env name; #2: specifications
+\NewDocumentCommand \SetTblrInner { O{tblr} m }
+  {
+    \clist_map_inline:nn {#1}
+      { \tl_put_right:cn { l__tblr_default_ ##1 _inner_tl } { , #2 } }
+    \ignorespaces
+  }
+\cs_new_eq:NN \SetTblrDefault \SetTblrInner
+
+%% #1: env name; #2: specifications
+\NewDocumentCommand \SetTblrOuter { O{tblr} m }
+  {
+    \clist_map_inline:nn {#1}
+      { \tl_put_right:cn { l__tblr_default_ ##1 _outer_tl } { , #2 } }
+    \ignorespaces
+  }
+
+%% #1: env name
+\NewDocumentCommand \NewTblrEnviron { m }
+  {
+    \NewDocumentEnvironment {#1} { O{c} m +b }
+      {
+        \__tblr_environ_code:nnnn {#1} {##1} {##2} {##3}
+      } { }
+    \tl_new:c { l__tblr_default_ #1 _inner_tl }
+    \tl_new:c { l__tblr_default_ #1 _outer_tl }
+    \tl_set_eq:cN { l__tblr_default_ #1 _outer_tl } \l__tblr_initial_tblr_outer_tl
+  }
+
+%% Create tblr and longtblr environments
+\NewTblrEnviron { tblr }
+\NewTblrEnviron { longtblr }
+\SetTblrOuter [ longtblr ] { long }
+\NewTblrEnviron { talltblr }
+\SetTblrOuter [ talltblr ] { tall }
+
+\tl_new:N \l__tblr_env_name_tl
+\bool_new:N \l__tblr_math_mode_bool
+
+%% Main environment code
+%% We need to add \group_align_safe_begin: and \group_align_safe_end:
+%% to make tabularray correctly nest in align environment (see issue #143)
+\cs_new_protected:Npn \__tblr_environ_code:nnnn #1 #2 #3 #4
+  {
+    \group_align_safe_begin:
+    \int_gincr:N \g__tblr_table_count_int
+    \tl_set:Nn \l__tblr_env_name_tl {#1}
+    \mode_if_math:TF
+      { \bool_set_true:N \l__tblr_math_mode_bool }
+      { \bool_set_false:N \l__tblr_math_mode_bool }
+    \__tblr_builder:nnn {#2} {#3} {#4}
+    \group_align_safe_end:
+  }
+
+\bool_new:N \lTblrMeasuringBool
+
+%% Read, split and build the table
+\cs_new_protected:Npn \__tblr_builder:nnn #1 #2 #3
+  {
+    \int_gincr:N \g_tblr_level_int
+    \__tblr_hook_use:n { tabularray/trial/before }
+    \bool_set_true:N \lTblrMeasuringBool
+    \__tblr_clear_prop_lists:
+    \__tblr_clear_spec_lists:
+    \LogTblrTracing { step = init ~ table ~ outer ~ spec}
+    \__tblr_init_table_outer_spec:
+    \LogTblrTracing { step = parse ~ table ~ options }
+    \__tblr_parse_table_option:n {#1}
+    \LogTblrTracing { outer }
+    \LogTblrTracing { option }
+    \__tblr_enable_table_commands:
+    \LogTblrTracing { step = split ~ table}
+    \__tblr_split_table:n {#3}
+    \LogTblrTracing { command }
+    \bool_if:NT \g__tblr_use_intarray_bool { \__tblr_init_table_data: }
+    \LogTblrTracing { step = init ~ table ~ inner ~ spec}
+    \__tblr_init_table_inner_spec:
+    \LogTblrTracing { inner }
+    \LogTblrTracing { step = parse ~ table ~ inner ~ spec}
+    \__tblr_parse_table_spec:n {#2}
+    \LogTblrTracing { step = execute ~ table ~ commands}
+    \__tblr_execute_table_commands:
+    \__tblr_disable_table_commands:
+    \__tblr_functional_calculation:
+    \LogTblrTracing { step = calculate ~ cell ~ and ~ line ~ sizes}
+    \__tblr_enable_content_commands:
+    \__tblr_calc_cell_and_line_sizes:
+    \bool_set_false:N \lTblrMeasuringBool
+    \__tblr_hook_use:n { tabularray/trial/after }
+    \LogTblrTracing { step = build ~ the ~ whole ~ table}
+    \__tblr_build_whole:
+    \int_gdecr:N \g_tblr_level_int
+  }
+
+%%% --------------------------------------------------------
+%%> \section{Split Table Contents}
+%%% --------------------------------------------------------
+
+%% Insert and remove braces for nesting environments inside cells
+%% These make line split and cell split workable
+%% We need to replace N times for N level nestings
+\regex_const:Nn \c__tblr_insert_braces_regex
+  {
+    \c{begin} \cB\{ (\c[^BE].*) \cE\} (.*?) \c{end} \cB\{ (\c[^BE].*) \cE\}
+  }
+\tl_const:Nn \c__tblr_insert_braces_tl
+  {
+    \c{begin} \cB\{ \cB\{ \1 \cE\} \2 \c{end} \cE\} \cB\{ \3 \cE\}
+  }
+\regex_const:Nn \c__tblr_remove_braces_regex
+  {
+    \c{begin} \cB\{ \cB\{ (.*?) \c{end} \cE\}
+  }
+\tl_const:Nn \c__tblr_remove_braces_tl
+  {
+    \c{begin} \cB\{ \1 \c{end}
+  }
+\cs_new_protected:Npn \__tblr_insert_braces:N #1
+  {
+    \regex_replace_all:NVN \c__tblr_insert_braces_regex \c__tblr_insert_braces_tl #1
+    \regex_replace_all:NVN \c__tblr_insert_braces_regex \c__tblr_insert_braces_tl #1
+  }
+\cs_new_protected:Npn \__tblr_remove_braces:N #1
+  {
+    \regex_replace_all:NVN \c__tblr_remove_braces_regex \c__tblr_remove_braces_tl #1
+    \regex_replace_all:NVN \c__tblr_remove_braces_regex \c__tblr_remove_braces_tl #1
+  }
+
+\tl_new:N \l__tblr_body_tl
+\seq_new:N \l__tblr_lines_seq
+
+%% Split table content to cells and store them
+%% #1: table content
+\cs_new_protected:Npn \__tblr_split_table:n #1
+  {
+    \tl_set:Nn \l__tblr_body_tl {#1}
+    \tblr_modify_table_body:
+    \int_zero:N \c at rowcount
+    \int_zero:N \c at colcount
+    \__tblr_split_table_to_lines:NN \l__tblr_body_tl \l__tblr_lines_seq
+    \__tblr_split_lines_to_cells:N \l__tblr_lines_seq
+  }
+
+\tl_new:N \l__tblr_expand_tl
+
+\cs_set_eq:NN \__tblr_hook_split_before: \prg_do_nothing:
+
+\cs_new_protected:Npn \tblr_modify_table_body:
+  {
+    \__tblr_hook_split_before:
+    \tl_set:Nx \l__tblr_expand_tl { \__tblr_spec_item:nn { outer } { expand } }
+    \tl_map_inline:Nn \l__tblr_expand_tl
+      {
+        \__tblr_expand_table_body:NN \l__tblr_body_tl ##1
+      }
+  }
+
+%% Expand every occurrence of the specified macro once
+%% #1: tl with table content; #2: macro to be expanded
+\cs_new_protected:Npn \__tblr_expand_table_body:NN #1 #2
+  {
+    \tl_set_eq:NN \l_tmpa_tl #1
+    \tl_clear:N #1
+    \cs_set_protected:Npn \__tblr_expand_table_body_aux:w ##1 #2
+      {
+        \tl_put_right:Nn #1 {##1}
+        \peek_meaning:NTF \q_stop
+          { \use_none:n }
+          { \exp_last_unbraced:NV \__tblr_expand_table_body_aux:w #2 }
+      }
+    \exp_last_unbraced:NV \__tblr_expand_table_body_aux:w \l_tmpa_tl #2 \q_stop
+  }
+
+%% Split table content to a sequence of lines
+%% #1: tl with table contents, #2: resulting sequence of lines
+\cs_new_protected:Npn \__tblr_split_table_to_lines:NN #1 #2
+  {
+    \__tblr_insert_braces:N #1
+    \seq_set_split:NnV \l_tmpa_seq { \\ } #1
+    \seq_clear:N #2
+    \seq_map_inline:Nn \l_tmpa_seq
+      {
+        \tl_if_head_eq_meaning:nNTF {##1} *
+          {
+            \tl_set:Nn \l__tblr_b_tl { \hborder { pagebreak = no } }
+            \tl_set:Nx \l__tblr_c_tl { \tl_tail:n {##1} }
+            \tl_trim_spaces:N \l__tblr_c_tl %% Ignore spaces between * and [dimen]
+            \tl_if_head_eq_meaning:VNT \l__tblr_c_tl [
+              {
+                \tl_put_right:Nn \l__tblr_b_tl { \RowBefore at AddBelowSep }
+              }
+            \tl_put_right:NV \l__tblr_b_tl \l__tblr_c_tl
+            \seq_put_right:NV #2 \l__tblr_b_tl
+          }
+          {
+            \tl_if_head_eq_meaning:nNTF { ##1 } [
+              { \seq_put_right:Nn #2 { \RowBefore at AddBelowSep ##1 } }
+              { \seq_put_right:Nn #2 { ##1 } }
+          }
+      }
+    \int_set:Nn \c at rowcount { \seq_count:N #2 }
+  }
+
+%% Treat \\[dimen] command
+\NewTableCommand \RowBefore at AddBelowSep [1] []
+  {
+    \IfValueT { #1 }
+      {
+        \__tblr_data_gadd_dimen_value:nene { row }
+          { \int_eval:n {\c at rownum - 1} } { belowsep } {#1}
+      }
+  }
+
+%% Split table lines to cells and store them
+%% #1: sequence of lines
+\cs_new_protected:Npn \__tblr_split_lines_to_cells:N #1
+  {
+    \seq_map_indexed_function:NN #1 \__tblr_split_one_line:nn
+    \LogTblrTracing { text }
+  }
+
+%% Split one line into cells and store them
+%% #1: row number, #2 the line text
+\cs_new_protected:Npn \__tblr_split_one_line:nn #1 #2
+  {
+    \seq_set_split:Nnn \l_tmpa_seq { & } { #2 }
+    \int_set:Nn \c at rownum {#1}
+    \int_zero:N \c at colnum
+    \seq_map_inline:Nn \l_tmpa_seq
+      {
+        \tl_set:Nn \l_tmpa_tl { ##1 }
+        \__tblr_remove_braces:N \l_tmpa_tl
+        \__tblr_trim_par_space_tokens:N \l_tmpa_tl
+        \int_incr:N \c at colnum
+        \__tblr_extract_table_commands:N \l_tmpa_tl
+        \__tblr_trim_par_space_tokens:N \l_tmpa_tl
+        \__tblr_spec_gput:neV { text } { [#1][\int_use:N \c at colnum] } \l_tmpa_tl
+      }
+    %% Decrease row count by 1 if the last row has only one empty cell text
+    %% We need to do it here since the > or < column type may add text to cells
+    \bool_lazy_all:nTF
+      {
+        { \int_compare_p:nNn {#1} = {\c at rowcount} }
+        { \int_compare_p:nNn {\c at colnum} = {1} }
+        { \tl_if_empty_p:N \l_tmpa_tl }
+      }
+      { \int_decr:N \c at rowcount }
+      {
+        \__tblr_prop_gput:nnx
+          {row} { [#1] / cell-number } { \int_use:N \c at colnum }
+        \int_compare:nT { \c at colnum > \c at colcount }
+          {
+            \int_set_eq:NN \c at colcount \c at colnum
+          }
+      }
+  }
+
+\regex_const:Nn \c__tblr_trim_left_par_space_regex { ^ \c{par} ? \s * }
+\regex_const:Nn \c__tblr_trim_right_space_par_regex { \s * \c{par} ? $ }
+
+\cs_new_protected:Npn \__tblr_trim_par_space_tokens:N #1
+  {
+    \regex_replace_once:NnN \c__tblr_trim_left_par_space_regex {} #1
+    \regex_replace_once:NnN \c__tblr_trim_right_space_par_regex {} #1
+  }
+
+%%% --------------------------------------------------------
+%%> \section{Extract Table Commands from Cell Text}
+%%% --------------------------------------------------------
+
+%% Extract table commands defined with \NewTableCommand from cell text
+
+\tl_new:N \l__tblr_saved_table_commands_before_cell_text_tl
+\tl_new:N \l__tblr_saved_cell_text_after_table_commands_tl
+
+\cs_new_protected:Npn \__tblr_extract_table_commands:N #1
+  {
+    \tl_clear:N \l__tblr_saved_table_commands_before_cell_text_tl
+    \tl_clear:N \l__tblr_saved_cell_text_after_table_commands_tl
+    \exp_last_unbraced:NV \__tblr_extract_table_commands_next:n #1 \q_stop
+    \tl_if_empty:NF \l__tblr_saved_table_commands_before_cell_text_tl
+      {
+        \__tblr_prop_gput:nxV { command }
+          {[\int_use:N \c at rownum][\int_use:N \c at colnum]}
+          \l__tblr_saved_table_commands_before_cell_text_tl
+      }
+    \tl_set_eq:NN #1 \l__tblr_saved_cell_text_after_table_commands_tl
+  }
+
+%% #1 maybe a single token or multiple tokens from a pair of braces
+\cs_new_protected:Npn \__tblr_extract_table_commands_next:n #1
+  {
+    \tl_if_single_token:nTF {#1}
+      {
+        \clist_if_in:NnTF \g__tblr_table_commands_clist { #1 }
+          { \__tblr_extract_one_table_command:N #1 }
+          {
+            \token_if_eq_meaning:NNF #1 \q_stop
+              { \__tblr_save_real_cell_text:w #1 }
+          }
+      }
+      { \__tblr_save_real_cell_text:w {#1} }
+  }
+
+\cs_new_protected:Npn \__tblr_extract_one_table_command:N #1
+  {
+    \int_set:Nn \l__tblr_a_int
+      { \cs:w g__tblr_table_cmd_ \cs_to_str:N #1 _arg_numb_tl \cs_end: }
+    \tl_put_right:Nn \l__tblr_saved_table_commands_before_cell_text_tl {#1}
+    \int_compare:nNnTF {\l__tblr_a_int} < {0}
+      {
+        \int_set:Nn \l__tblr_a_int { \int_abs:n {\l__tblr_a_int} - 1 }
+        \peek_charcode:NTF [
+          { \__tblr_extract_table_command_arg_o:w }
+          { \__tblr_extract_table_command_arg_next: }
+      }
+      { \__tblr_extract_table_command_arg_next: }
+  }
+
+\cs_new_protected:Npn \__tblr_extract_table_command_arg_o:w [#1]
+  {
+    \tl_put_right:Nn \l__tblr_saved_table_commands_before_cell_text_tl { [#1] }
+    \__tblr_extract_table_command_arg_next:
+  }
+
+\cs_new_protected:Npn \__tblr_extract_table_command_arg_m:n #1
+  {
+    \tl_put_right:Nn \l__tblr_saved_table_commands_before_cell_text_tl { {#1} }
+    \__tblr_extract_table_command_arg_next:
+  }
+
+\cs_new_protected:Npn \__tblr_extract_table_command_arg_next:
+  {
+    \int_compare:nNnTF {\l__tblr_a_int} > {0}
+      {
+        \int_decr:N \l__tblr_a_int
+        \__tblr_extract_table_command_arg_m:n
+      }
+      { \__tblr_extract_table_commands_next:n }
+  }
+
+%% The outermost set of braces of cell text #1 will be removed
+\cs_new_protected:Npn \__tblr_save_real_cell_text:w #1 \q_stop
+  {
+    \tl_set:Nn \l__tblr_saved_cell_text_after_table_commands_tl {#1}
+  }
+
+%%% --------------------------------------------------------
+%%> \section{Initialize Table Inner Specifications}
+%%% --------------------------------------------------------
+
+\prop_new:N \g__tblr_initial_table_prop
+\prop_new:N \g__tblr_initial_rows_prop
+\prop_new:N \g__tblr_initial_columns_prop
+\prop_new:N \g__tblr_initial_cells_prop
+\prop_new:N \g__tblr_initial_hlines_prop
+\prop_new:N \g__tblr_initial_vlines_prop
+
+\prop_gset_from_keyval:Nn \g__tblr_initial_table_prop
+  {
+    stretch = 1,
+    rulesep = 2pt,
+  }
+
+\prop_gset_from_keyval:Nn \g__tblr_initial_rows_prop
+  {
+    abovesep = 2pt,
+    belowsep = 2pt,
+    @row-height = 0pt,
+    @row-head = 0pt,
+    @row-foot = 0pt,
+    @row-upper = 0pt,
+    @row-lower = 0pt,
+  }
+
+\prop_gset_from_keyval:Nn \g__tblr_initial_columns_prop
+  {
+    leftsep = 6pt,
+    rightsep = 6pt,
+    width = -1pt, % column width unset
+    coefficient = 0, % column coefficient unset
+    @col-width = 0pt,
+  }
+
+\prop_gset_from_keyval:Nn \g__tblr_initial_cells_prop
+  {
+    halign = j,
+    valign = t,
+    width = -1pt, % cell width unset
+    rowspan = 1,
+    colspan = 1,
+    omit = 0,
+  }
+
+\prop_gset_from_keyval:Nn \g__tblr_initial_hlines_prop
+  {
+    @hline-count = 0,
+  }
+
+\prop_gset_from_keyval:Nn \g__tblr_initial_vlines_prop
+  {
+    @vline-count = 0,
+  }
+
+\tl_new:N \l__tblr_inner_spec_measure_tl
+\tl_new:N \l__tblr_inner_spec_verb_tl
+
+\cs_new_protected:Npn \__tblr_init_table_inner_spec:
+  {
+    \prop_map_inline:Nn \g__tblr_initial_table_prop
+      {
+        \__tblr_prop_gput:nxn { inner } { ##1 } {##2}
+      }
+    \int_step_variable:nNn { \c at rowcount } \l__tblr_i_tl
+      {
+        \prop_map_inline:Nn \g__tblr_initial_rows_prop
+          {
+            \__tblr_data_gput:nVnn { row } \l__tblr_i_tl {##1} {##2}
+          }
+        \prop_map_inline:Nn \g__tblr_initial_hlines_prop
+          {
+            \__tblr_spec_gput:nen { hline } { [\l__tblr_i_tl] / ##1 } {##2}
+          }
+        \int_step_variable:nNn { \c at colcount } \l__tblr_j_tl
+          {
+            \prop_map_inline:Nn \g__tblr_initial_cells_prop
+              {
+                \__tblr_data_gput:neeen { cell }
+                  { \l__tblr_i_tl } { \l__tblr_j_tl } {##1} {##2}
+              }
+          }
+      }
+    \prop_map_inline:Nn \g__tblr_initial_hlines_prop
+      {
+        \__tblr_spec_gput:nen { hline }
+          { [\int_eval:n { \c at rowcount + 1}] / ##1 } {##2}
+      }
+    \int_step_variable:nNn { \c at colcount } \l__tblr_j_tl
+      {
+        \prop_map_inline:Nn \g__tblr_initial_columns_prop
+          {
+            \__tblr_data_gput:nenn { column } { \l__tblr_j_tl } {##1} {##2}
+          }
+        \prop_map_inline:Nn \g__tblr_initial_vlines_prop
+          {
+            \__tblr_spec_gput:nen { vline } { [\l__tblr_j_tl] / ##1 } {##2}
+          }
+      }
+    \prop_map_inline:Nn \g__tblr_initial_vlines_prop
+      {
+        \__tblr_spec_gput:nen { vline }
+          { [\int_eval:n { \c at colcount + 1}] / ##1 } {##2}
+      }
+    \tl_clear:N \l__tblr_inner_spec_measure_tl
+    \tl_clear:N \l__tblr_inner_spec_verb_tl
+    \keys_set:nv { tblr } { l__tblr_default_ \l__tblr_env_name_tl _inner_tl }
+  }
+
+%%% --------------------------------------------------------
+%%> \section{Parse Table Inner Specifications}
+%%% --------------------------------------------------------
+
+\clist_new:N \g__tblr_table_known_keys_clist
+\clist_gset:Nn \g__tblr_table_known_keys_clist
+  {
+    colspec, rowspec, column, row, cell, hline, vline, hborder, vborder, width,
+    rowhead, rowfoot, columns, rows, cells, hlines, vlines, % hborders, vborders,
+    leftsep, rightsep, colsep, abovesep, belowsep, rowsep, rulesep,
+    baseline, hspan, vspan, stretch, verb, delimiter
+  }
+
+\keys_define:nn { tblr }
+  {
+    colspec .code:n = \__tblr_parse_colrow_spec:nn { column } {#1},
+    rowspec .code:n = \__tblr_parse_colrow_spec:nn { row } {#1},
+    width .code:n = \__tblr_keys_gput:nx { width } { \dim_eval:n {#1} },
+    hspan .code:n = \__tblr_keys_gput:nn { hspan } {#1},
+    vspan .code:n = \__tblr_keys_gput:nn { vspan } {#1},
+    stretch .code:n = \__tblr_keys_gput:nn { stretch } {#1},
+    verb   .tl_set:N = \l__tblr_inner_spec_verb_tl,
+    verb  .default:n = lite,
+    columns .code:n = \__tblr_set_every_column_aux:n {#1},
+    rows    .code:n = \__tblr_set_every_row_aux:n {#1},
+    cells   .code:n = \__tblr_set_every_cell_aux:n {#1},
+    hlines  .code:n = \__tblr_set_every_hline_aux:n {#1},
+    vlines  .code:n = \__tblr_set_every_vline_aux:n {#1},
+    leftsep .code:n = \tblr_set_every_column:nn { } { leftsep = #1 },
+    rightsep .code:n = \tblr_set_every_column:nn { } { rightsep = #1 },
+    colsep .meta:n = { leftsep = #1, rightsep = #1 },
+    abovesep .code:n = \tblr_set_every_row:nn { } { abovesep = #1 },
+    belowsep .code:n = \tblr_set_every_row:nn { } { belowsep = #1 },
+    rowsep .meta:n = { abovesep = #1, belowsep = #1 },
+    rulesep .code:n = \__tblr_keys_gput:nn { rulesep } {#1},
+    rowhead .code:n = \__tblr_keys_gput:nn { rowhead } {#1},
+    rowfoot .code:n = \__tblr_keys_gput:nn { rowfoot } {#1},
+    delimiter .code:n = \__tblr_set_delimiter:n {#1},
+    baseline .code:n = \__tblr_outer_gput_spec:nn { baseline } {#1},
+    unknown .code:n = \__tblr_table_special_key:Vn \l_keys_key_str {#1},
+  }
+
+\regex_const:Nn \c__tblr_split_key_name_regex { ^ ( [a-z] + ) ( . * ) }
+
+\cs_new_protected:Npn \__tblr_table_special_key:nn #1 #2
+  {
+    \regex_extract_once:NnNT \c__tblr_split_key_name_regex {#1} \l_tmpa_seq
+      {
+        \tl_set:Nx \l__tblr_a_tl { \seq_item:Nn \l_tmpa_seq {2} }
+        \tl_set_rescan:Nnx \l__tblr_b_tl {} { \seq_item:Nn \l_tmpa_seq {3} }
+        \cs:w __tblr_set_ \l__tblr_a_tl _aux:Vn \cs_end: \l__tblr_b_tl {#2}
+      }
+  }
+\cs_generate_variant:Nn \__tblr_table_special_key:nn { Vn }
+
+%% If the first key name is known, treat #1 is the table spec;
+%% otherwise, treat #1 as colspec.
+
+\regex_const:Nn \c__tblr_first_key_name_regex { ^ \s * ( [A-Za-z\-] + ) }
+
+\cs_new_protected:Npn \__tblr_parse_table_spec:n #1
+  {
+    \regex_extract_once:NnNTF \c__tblr_first_key_name_regex {#1} \l_tmpa_seq
+      {
+        \clist_if_in:NxTF \g__tblr_table_known_keys_clist
+          { \seq_item:Nn \l_tmpa_seq {2} }
+          { \keys_set:nn { tblr } {#1} }
+          { \__tblr_parse_colrow_spec:nn { column } {#1} }
+      }
+      { \__tblr_parse_colrow_spec:nn { column } {#1} }
+  }
+
+\cs_new_protected:Npn \__tblr_keys_gput:nn #1 #2
+  {
+    \__tblr_prop_gput:nnn { inner } {#1} {#2}
+  }
+\cs_generate_variant:Nn \__tblr_keys_gput:nn { nx }
+
+\keys_define:nn { tblr-delimiter }
+  {
+    left .code:n  = \__tblr_keys_gput:nn { delim-left } { \left #1 },
+    right .code:n = \__tblr_keys_gput:nn { delim-right } { \right #1 }
+  }
+
+\cs_new_protected:Npn \__tblr_set_delimiter:n #1
+  {
+    \keys_set:nn { tblr-delimiter } {#1}
+  }
+
+%%% --------------------------------------------------------
+%%> \section{Initialize and Parse Table Outer Specifications}
+%%% --------------------------------------------------------
+
+\msg_new:nnn { tabularray } { used-theme-name }
+  { theme ~ name ~ #1 ~ has ~ been ~ used! }
+
+%% #1: theme names; #2: template and style commands
+\NewDocumentCommand \NewTblrTheme { m +m }
+  {
+    \tl_if_exist:cTF { g__tblr_theme_ #1 _code_tl }
+      { \msg_error:nnn { tabularray } { used-theme-name } { #1 } }
+      {
+        \tl_set:cn { g__tblr_theme_ #1 _code_tl } {#2}
+        \ignorespaces
+      }
+  }
+
+\cs_new_protected:Npn \__tblr_use_theme:n #1
+  {
+    \ignorespaces
+    \tl_use:c { g__tblr_theme_ #1 _code_tl }
+  }
+
+\cs_new_protected:Npn \__tblr_init_table_outer_spec:
+  {
+    \keys_set:nv { tblr-outer } { l__tblr_default_ \l__tblr_env_name_tl _outer_tl }
+  }
+
+\cs_new_protected:Npn \__tblr_parse_table_option:n #1
+  {
+    \keys_set:nn { tblr-outer } {#1}
+  }
+
+\keys_define:nn { tblr-outer }
+  {
+    long    .code:n = \__tblr_outer_gput_spec:nn { long } { true },
+    tall    .code:n = \__tblr_outer_gput_spec:nn { tall } { true },
+    halign  .code:n = \__tblr_outer_gput_spec:nn { halign } {#1},
+    baseline .code:n = \__tblr_outer_gput_spec:nn { baseline } {#1},
+    l       .meta:n = { halign = l },
+    c       .meta:n = { halign = c },
+    r       .meta:n = { halign = r },
+    t       .meta:n = { baseline = t },
+    T       .meta:n = { baseline = T },
+    m       .meta:n = { baseline = m },
+    M       .meta:n = { baseline = M },
+    b       .meta:n = { baseline = b },
+    B       .meta:n = { baseline = B },
+    valign  .meta:n = { baseline = #1 }, % obsolete, will be removed some day
+    expand  .code:n = \__tblr_outer_gput_spec:nn { expand } {#1},
+    expand+ .code:n = \__tblr_outer_gconcat_spec:nn { expand } {#1},
+    headsep .code:n = \__tblr_outer_gput_spec:nn { headsep } {#1},
+    footsep .code:n = \__tblr_outer_gput_spec:nn { footsep } {#1},
+    presep  .code:n = \__tblr_outer_gput_spec:nn { presep }  {#1},
+    postsep .code:n = \__tblr_outer_gput_spec:nn { postsep } {#1},
+    theme   .code:n = \__tblr_use_theme:n {#1},
+    caption .code:n = \__tblr_outer_gput_spec:nn { caption } {#1},
+    entry   .code:n = \__tblr_outer_gput_spec:nn { entry } {#1},
+    label   .code:n = \__tblr_outer_gput_spec:nn { label } {#1},
+    unknown .code:n = \__tblr_table_option_key:Vn \l_keys_key_str {#1},
+  }
+
+\cs_new_protected:Npn \__tblr_outer_gput_spec:nn #1 #2
+  {
+    \__tblr_spec_gput:nen { outer } {#1} {#2}
+  }
+\cs_generate_variant:Nn \__tblr_outer_gput_spec:nn { ne }
+
+\cs_new_protected:Npn \__tblr_outer_gconcat_spec:nn #1 #2
+  {
+    \__tblr_outer_gput_spec:ne {#1} { \__tblr_spec_item:nn { outer } { #1 } \exp_not:n { #2 } }
+  }
+
+\regex_const:Nn \c__tblr_option_key_name_regex { ^ [A-Za-z\-] + $ }
+
+\msg_new:nnn { tabularray } { unknown-outer-key }
+  { Unknown ~ outer ~ key ~ name ~ #1! }
+
+\cs_new_protected:Npn \__tblr_table_option_key:nn #1 #2
+  {
+    \regex_match:NnTF \c__tblr_option_key_name_regex {#1}
+      { \msg_error:nnn { tabularray } { unknown-outer-key } {#1} }
+      {
+        \regex_extract_once:NnNT \c__tblr_split_key_name_regex {#1} \l_tmpa_seq
+          {
+            \tl_set:Nx \l__tblr_a_tl { \seq_item:Nn \l_tmpa_seq {2} }
+            \tl_set_rescan:Nnx \l__tblr_b_tl {} { \seq_item:Nn \l_tmpa_seq {3} }
+            \tl_set:Nx \l__tblr_c_tl { \tl_head:N \l__tblr_b_tl }
+            \use:c { __tblr_outer_gput_ \l__tblr_a_tl :Vn } \l__tblr_c_tl {#2}
+          }
+      }
+  }
+\cs_generate_variant:Nn \__tblr_table_option_key:nn { Vn }
+
+\cs_new_protected:Npn \__tblr_outer_gput_note:nn #1 #2
+  {
+    \__tblr_prop_gput:nnn { note } {#1} {#2}
+  }
+\cs_generate_variant:Nn \__tblr_outer_gput_note:nn { Vn }
+
+\cs_new_protected:Npn \__tblr_outer_gput_remark:nn #1 #2
+  {
+    \__tblr_prop_gput:nnn { remark } {#1} {#2}
+  }
+\cs_generate_variant:Nn \__tblr_outer_gput_remark:nn { Vn }
+
+\cs_new_protected:Npn \__tblr_outer_gput_more:nn #1 #2
+  {
+    \__tblr_prop_gput:nnn { more } {#1} {#2}
+  }
+\cs_generate_variant:Nn \__tblr_outer_gput_more:nn { Vn }
+
+%%% --------------------------------------------------------
+%%> \section{Typeset and Calculate Sizes}
+%%% --------------------------------------------------------
+
+%% Calculate the width and height for every cell and border
+
+\cs_new_protected:Npn \__tblr_calc_cell_and_line_sizes:
+  {
+    \__tblr_prepare_stretch:
+    \__tblr_calculate_line_sizes:
+    \__tblr_calculate_cell_sizes:
+    \LogTblrTracing { cell, row, column, hline, vline }
+    \__tblr_compute_extendable_column_width:
+    \__tblr_adjust_sizes_for_span_cells:
+  }
+
+%% prepare stretch option of the table
+\fp_new:N \l__tblr_stretch_fp
+\dim_new:N \l__tblr_strut_dp_dim
+\dim_new:N \l__tblr_strut_ht_dim
+\cs_new_protected:Npn \__tblr_prepare_stretch:
+  {
+    \fp_set:Nn \l__tblr_stretch_fp
+      { \__tblr_prop_item:nn { inner } { stretch } }
+    \fp_compare:nNnTF \l__tblr_stretch_fp > \c_zero_fp
+      {
+        \dim_set:Nn \l__tblr_strut_dp_dim
+          { \fp_use:N \l__tblr_stretch_fp \box_dp:N \strutbox }
+        \dim_set:Nn \l__tblr_strut_ht_dim
+          { \fp_use:N \l__tblr_stretch_fp \box_ht:N \strutbox }
+        \cs_set_eq:NN \__tblr_leave_vmode: \mode_leave_vertical:
+        \cs_set_eq:NN \__tblr_process_stretch: \__tblr_process_stretch_real:
+      }
+      {
+        \cs_set_eq:NN \__tblr_process_stretch: \prg_do_nothing:
+        \fp_compare:nNnTF \l__tblr_stretch_fp < \c_zero_fp
+          { \cs_set_eq:NN \__tblr_leave_vmode: \@setminipage } % for lists (see issue #99)
+          { \cs_set_eq:NN \__tblr_leave_vmode: \mode_leave_vertical: }
+      }
+  }
+\cs_new_eq:NN \__tblr_leave_vmode: \mode_leave_vertical:
+\cs_new_protected:Npn \__tblr_process_stretch_real:
+  {
+    \dim_compare:nNnT \l__tblr_strut_dp_dim > { \box_dp:N \l_tmpb_box }
+      {
+        \box_set_dp:Nn \l_tmpa_box
+          {
+              \box_dp:N \l_tmpa_box
+            - \box_dp:N \l_tmpb_box
+            + \l__tblr_strut_dp_dim
+          }
+        \box_set_dp:Nn \l_tmpb_box { \l__tblr_strut_dp_dim }
+      }
+    \dim_compare:nNnT \l__tblr_strut_ht_dim > { \box_ht:N \l_tmpa_box }
+      {
+        \hbox_set:Nn \l_tmpa_box { \box_use:N \l_tmpa_box }
+        \hbox_set:Nn \l_tmpb_box { \box_use:N \l_tmpb_box }
+        \box_set_ht:Nn \l_tmpb_box
+          {
+              \box_ht:N \l_tmpb_box
+            - \box_ht:N \l_tmpa_box
+            + \l__tblr_strut_ht_dim
+          }
+        \box_set_ht:Nn \l_tmpa_box { \l__tblr_strut_ht_dim }
+        %% return vbox for vertical-align: \c__tblr_middle_m_tl
+        \vbox_set_top:Nn \l_tmpa_box { \box_use:N \l_tmpa_box }
+        \vbox_set:Nn \l_tmpb_box { \box_use:N \l_tmpb_box }
+      }
+  }
+\cs_new_eq:NN \__tblr_process_stretch: \__tblr_process_stretch_real:
+
+%% Calculate the thickness for every hline and vline
+\cs_new_protected:Npn \__tblr_calculate_line_sizes:
+  {
+    %% We need these two counters in executing hline and vline commands
+    \int_zero:N \c at rownum
+    \int_zero:N \c at colnum
+    \int_step_inline:nn { \c at rowcount + 1 }
+      {
+        \int_incr:N \c at rownum
+        \int_zero:N \c at colnum
+        \int_step_inline:nn { \c at colcount + 1 }
+          {
+            \int_incr:N \c at colnum
+            \int_compare:nNnT { ##1 } < { \c at rowcount + 1 }
+              {
+                \__tblr_measure_and_update_vline_size:nn { ##1 } { ####1 }
+              }
+            \int_compare:nNnT { ####1 } < { \c at colcount + 1 }
+              {
+                \__tblr_measure_and_update_hline_size:nn { ##1 } { ####1 }
+              }
+          }
+      }
+  }
+
+%% Measure and update thickness of the vline
+%% #1: row number, #2 column number
+\cs_new_protected:Npn \__tblr_measure_and_update_vline_size:nn #1 #2
+  {
+    \dim_zero:N \l__tblr_w_dim
+    \tl_set:Nx \l__tblr_n_tl
+      { \__tblr_spec_item:ne { vline } { [#2] / @vline-count } }
+    \int_compare:nNnT { \l__tblr_n_tl } > {0}
+      {
+        \tl_set:Nx \l__tblr_s_tl
+          { \__tblr_prop_item:ne { inner } { rulesep } }
+        \int_step_inline:nn { \l__tblr_n_tl }
+          {
+            \vbox_set_to_ht:Nnn \l__tblr_b_box {1pt}
+              {
+                \__tblr_get_vline_segment_child:nnnnn
+                  {#1} {#2} {##1} {1pt} {1pt}
+              }
+            \tl_set:Nx \l__tblr_w_tl { \dim_eval:n { \box_wd:N \l__tblr_b_box } }
+            \__tblr_spec_gput_if_larger:nee { vline }
+              { [#2](##1) / @vline-width } { \l__tblr_w_tl }
+            \dim_add:Nn \l__tblr_w_dim
+              {
+                \__tblr_spec_item:nn  { vline } { [#2](##1) / @vline-width }
+              }
+            \dim_add:Nn \l__tblr_w_dim { \l__tblr_s_tl }
+          }
+        \dim_add:Nn \l__tblr_w_dim { - \l__tblr_s_tl }
+      }
+    \__tblr_spec_gput_if_larger:nee { vline }
+      { [#2]/ @vline-width } { \dim_use:N \l__tblr_w_dim }
+  }
+
+%% Get text of a vline segment
+%% #1: row number, #2: column number; #3: index number; #4: height; #5: depth
+%% We put all code inside a group to avoid conflicts of local variables
+\cs_new_protected:Npn \__tblr_get_vline_segment_child:nnnnn #1 #2 #3 #4 #5
+  {
+    \group_begin:
+    \tl_set:Nx \l__tblr_w_tl
+      { \__tblr_spec_item:ne { vline } { [#1][#2](#3) / wd } }
+    \tl_if_empty:NF \l__tblr_w_tl { \dim_set:Nn \rulewidth { \l__tblr_w_tl } }
+    \tl_set:Nx \l__tblr_d_tl
+      { \__tblr_spec_item:ne { vline } { [#1][#2](#3) / @dash } }
+    \tl_set:Nx \l__tblr_a_tl { \tl_head:N \l__tblr_d_tl }
+    \tl_set:Nx \l__tblr_b_tl { \tl_tail:N \l__tblr_d_tl }
+    \exp_args:NV \tl_if_eq:NNTF \l__tblr_a_tl \@tblr at dash
+      {
+        \__tblr_get_vline_dash_style:N \l__tblr_b_tl
+        \xleaders \l__tblr_b_tl \vfil
+      }
+      {
+        %% When using text as vline, we need to omit abovepos and belowpos.
+        \unskip
+        \hbox_set:Nn \l__tblr_d_box
+          {
+            \bool_if:NTF \l__tblr_math_mode_bool
+              { $ \l__tblr_b_tl $ } { \l__tblr_b_tl }
+          }
+        \box_set_ht:Nn \l__tblr_d_box {#4}
+        \box_set_dp:Nn \l__tblr_d_box {#5}
+        \box_use:N \l__tblr_d_box
+        \vss
+      }
+    \group_end:
+  }
+\cs_generate_variant:Nn \__tblr_get_vline_segment_child:nnnnn { nnnxx }
+
+%% Measure and update thickness of the hline
+%% #1: row number, #2 column number
+\cs_new_protected:Npn \__tblr_measure_and_update_hline_size:nn #1 #2
+  {
+    \dim_zero:N \l__tblr_h_dim
+    \tl_set:Nx \l__tblr_n_tl
+      { \__tblr_spec_item:ne { hline } { [#1] / @hline-count } }
+    \int_compare:nNnT { \l__tblr_n_tl } > {0}
+      {
+        \tl_set:Nx \l__tblr_s_tl
+          { \__tblr_prop_item:ne { inner } { rulesep } }
+        \int_step_inline:nn { \l__tblr_n_tl }
+          {
+            \hbox_set_to_wd:Nnn \l__tblr_b_box {1pt}
+              { \__tblr_get_hline_segment_child:nnn {#1} {#2} {##1} }
+            \tl_set:Nx \l__tblr_h_tl
+              {
+                \dim_eval:n
+                  { \box_ht:N \l__tblr_b_box + \box_dp:N \l__tblr_b_box }
+              }
+            \__tblr_spec_gput_if_larger:nee { hline }
+              { [#1](##1) / @hline-height } { \l__tblr_h_tl }
+            \dim_add:Nn \l__tblr_h_dim
+              {
+                \__tblr_spec_item:nn  { hline } { [#1](##1) / @hline-height }
+              }
+            \dim_add:Nn \l__tblr_h_dim { \l__tblr_s_tl }
+          }
+        \dim_add:Nn \l__tblr_h_dim { - \l__tblr_s_tl }
+      }
+    \__tblr_spec_gput_if_larger:nee { hline }
+      { [#1] / @hline-height } { \dim_use:N \l__tblr_h_dim }
+  }
+
+%% Get text of a hline segment
+%% #1: row number, #2: column number; #3: index number
+\cs_new_protected:Npn \__tblr_get_hline_segment_child:nnn #1 #2 #3
+  {
+    \group_begin:
+    \tl_set:Nx \l__tblr_w_tl
+      { \__tblr_spec_item:ne { hline } { [#1][#2](#3) / wd } }
+    \tl_if_empty:NF \l__tblr_w_tl { \dim_set:Nn \rulewidth { \l__tblr_w_tl } }
+    \tl_set:Nx \l__tblr_d_tl
+      { \__tblr_spec_item:ne { hline } { [#1][#2](#3) / @dash } }
+    \tl_set:Nx \l__tblr_a_tl { \tl_head:N \l__tblr_d_tl }
+    \tl_set:Nx \l__tblr_b_tl { \tl_tail:N \l__tblr_d_tl }
+    \exp_args:NV \tl_if_eq:NNTF \l__tblr_a_tl \@tblr at dash
+      {
+        \__tblr_get_hline_dash_style:N \l__tblr_b_tl
+        \xleaders \l__tblr_b_tl \hfil
+      }
+      {
+        \bool_if:NTF \l__tblr_math_mode_bool
+          { $ \l__tblr_b_tl $ } { \l__tblr_b_tl }
+        \hfil
+      }
+    \group_end:
+  }
+
+%% current cell alignments
+\tl_new:N \g__tblr_cell_halign_tl
+\tl_new:N \g__tblr_cell_valign_tl
+\tl_new:N \g__tblr_cell_middle_tl
+
+\tl_const:Nn \c__tblr_valign_h_tl { h }
+\tl_const:Nn \c__tblr_valign_m_tl { m }
+\tl_const:Nn \c__tblr_valign_f_tl { f }
+\tl_const:Nn \c__tblr_valign_t_tl { t }
+\tl_const:Nn \c__tblr_valign_b_tl { b }
+
+\tl_const:Nn \c__tblr_middle_t_tl { t }
+\tl_const:Nn \c__tblr_middle_m_tl { m }
+\tl_const:Nn \c__tblr_middle_b_tl { b }
+
+%% #1: row number; #2: column number
+\cs_new_protected:Npn \__tblr_get_cell_alignments:nn #1 #2
+  {
+    \group_begin:
+    \tl_gset:Nx \g__tblr_cell_halign_tl
+      { \__tblr_data_item:neen { cell } {#1} {#2} { halign } }
+    \tl_set:Nx \l__tblr_v_tl
+      { \__tblr_data_item:neen { cell } {#1} {#2} { valign } }
+    \tl_case:NnF \l__tblr_v_tl
+      {
+        \c__tblr_valign_t_tl
+          {
+            \tl_gset:Nn \g__tblr_cell_valign_tl {m}
+            \tl_gset:Nn \g__tblr_cell_middle_tl {t}
+          }
+        \c__tblr_valign_m_tl
+          {
+            \tl_gset:Nn \g__tblr_cell_valign_tl {m}
+            \tl_gset:Nn \g__tblr_cell_middle_tl {m}
+          }
+        \c__tblr_valign_b_tl
+          {
+            \tl_gset:Nn \g__tblr_cell_valign_tl {m}
+            \tl_gset:Nn \g__tblr_cell_middle_tl {b}
+          }
+      }
+      {
+        \tl_gset_eq:NN \g__tblr_cell_valign_tl \l__tblr_v_tl
+        \tl_gclear:N \g__tblr_cell_middle_tl
+      }
+    \group_end:
+  }
+
+%% current cell dimensions
+\dim_new:N \g__tblr_cell_wd_dim
+\dim_new:N \g__tblr_cell_ht_dim
+\dim_new:N \g__tblr_cell_head_dim
+\dim_new:N \g__tblr_cell_foot_dim
+
+%% Calculate the width and height for every cell
+\cs_new_protected:Npn \__tblr_calculate_cell_sizes:
+  {
+    %% You can use these two counters in cell text
+    \int_zero:N \c at rownum
+    \int_zero:N \c at colnum
+    \__tblr_save_counters:n { table }
+    \int_step_inline:nn { \c at rowcount }
+      {
+        \int_incr:N \c at rownum
+        \int_zero:N \c at colnum
+        \__tblr_update_rowsep_registers:
+        \tl_set:Nx \l__tblr_h_tl
+          { \__tblr_data_item:nen { row } { \int_use:N \c at rownum } { height } }
+        %% We didn't initialize row heights with -1pt
+        \dim_compare:nNnF { \l__tblr_h_tl } = { 0pt }
+          {
+            \__tblr_data_gput:nenV { row } { \int_use:N \c at rownum }
+              { @row-height } \l__tblr_h_tl
+          }
+        \int_step_inline:nn { \c at colcount }
+          {
+            \int_incr:N \c at colnum
+            \__tblr_update_colsep_registers:
+            \__tblr_measure_cell_update_sizes:nnNNNN
+              { \int_use:N \c at rownum }
+              { \int_use:N \c at colnum }
+              \g__tblr_cell_wd_dim
+              \g__tblr_cell_ht_dim
+              \g__tblr_cell_head_dim
+              \g__tblr_cell_foot_dim
+          }
+      }
+    \__tblr_restore_counters:n { table }
+    \int_step_inline:nn { \c at colcount }
+      {
+        \tl_set:Nx \l__tblr_w_tl
+          { \__tblr_data_item:nen { column } {##1} { width } }
+        \dim_compare:nNnF { \l__tblr_w_tl } < { 0pt }
+          {
+            \__tblr_data_gput:nenV { column } {##1} { @col-width } \l__tblr_w_tl
+          }
+      }
+  }
+
+\cs_new_protected:Npn \__tblr_update_rowsep_registers:
+  {
+    \dim_set:Nn \abovesep
+      { \__tblr_data_item:nen { row } { \int_use:N \c at rownum } { abovesep } }
+    \dim_set:Nn \belowsep
+      { \__tblr_data_item:nen { row } { \int_use:N \c at rownum } { belowsep } }
+  }
+
+\cs_new_protected:Npn \__tblr_update_colsep_registers:
+  {
+    \dim_set:Nn \leftsep
+      { \__tblr_data_item:nen { column } { \int_use:N \c at colnum } { leftsep } }
+    \dim_set:Nn \rightsep
+      { \__tblr_data_item:nen { column } { \int_use:N \c at colnum } { rightsep } }
+  }
+
+%% Measure and update natural dimensions of the row/column/cell
+%% #1: row number; #2 column number; #3: width dimension;
+%% #4: total height dimension; #5: head dimension; #6: foot dimension
+\cs_new_protected:Npn \__tblr_measure_cell_update_sizes:nnNNNN #1 #2 #3 #4 #5 #6
+  {
+    \__tblr_get_cell_alignments:nn {#1} {#2}
+    \hbox_set:Nn \l_tmpa_box { \__tblr_get_cell_text:nn {#1} {#2} }
+    \__tblr_update_cell_size:nnNNNN {#1} {#2} #3 #4 #5 #6
+    \__tblr_update_row_size:nnNNN {#1} {#2} #4 #5 #6
+    \__tblr_update_col_size:nN {#2} #3
+  }
+
+%% #1: row number, #2: column number
+\cs_new_protected:Npn \__tblr_get_cell_text:nn #1 #2
+  {
+    \int_compare:nNnTF { \__tblr_data_item:neen { cell } {#1} {#2} { omit } } > {0}
+      {
+        \dim_gzero:N \g__tblr_cell_wd_dim
+        \dim_gzero:N \g__tblr_cell_ht_dim
+        \dim_gzero:N \g__tblr_cell_head_dim
+        \dim_gzero:N \g__tblr_cell_foot_dim
+      }
+      { \__tblr_get_cell_text_real:nn { #1 } { #2 } }
+  }
+
+\tl_new:N \l__tblr_cell_fg_tl
+\tl_new:N \l__tblr_cell_cmd_tl
+\tl_new:N \l__tblr_cell_mode_tl
+\bool_new:N \l__tblr_cell_math_mode_bool
+\tl_const:Nn \l__tblr_cell_math_style_tl  { \relax }
+\tl_const:Nn \l__tblr_cell_imath_style_tl { \textstyle }
+\tl_const:Nn \l__tblr_cell_dmath_style_tl { \displaystyle }
+
+%% Get cell text, #1: row number, #2: column number
+%% If the width of the cell is not set, split it with \\ and compute the width
+%% Therefore we always get a vbox for any cell
+\cs_new_protected:Npn \__tblr_get_cell_text_real:nn #1 #2
+  {
+    \group_begin:
+    \tl_set:Nx \l__tblr_c_tl { \__tblr_spec_item:ne { text } {[#1][#2]} }
+    %% when the cell text is guarded by a pair of curly braces,
+    %% we unbrace it and ignore cmd option of the cell, see issue #90.
+    \bool_lazy_and:nnTF
+      { \tl_if_single_p:N \l__tblr_c_tl }
+      { \exp_args:NV \tl_if_head_is_group_p:n \l__tblr_c_tl }
+      { \exp_last_unbraced:NNV \tl_set:Nn \l__tblr_c_tl \l__tblr_c_tl }
+      {
+        \tl_set:Nx \l__tblr_cell_cmd_tl
+          { \__tblr_data_item:neen { cell } {#1} {#2} { cmd } }
+        \tl_if_empty:NF \l__tblr_cell_cmd_tl
+          {
+            \tl_set:Nx \l__tblr_c_tl
+              { \exp_not:V \l__tblr_cell_cmd_tl { \exp_not:V \l__tblr_c_tl } }
+          }
+      }
+    \tl_set:Nx \l__tblr_cell_mode_tl
+      { \__tblr_data_item:neen { cell } {#1} {#2} { mode } }
+    \tl_if_empty:NT \l__tblr_cell_mode_tl
+      {
+        \bool_if:NTF \l__tblr_math_mode_bool
+          { \tl_set:Nn \l__tblr_cell_mode_tl { math } }
+          { \tl_set:Nn \l__tblr_cell_mode_tl { text } }
+      }
+    \tl_if_eq:NnTF \l__tblr_cell_mode_tl { text }
+      { \bool_set_false:N \l__tblr_cell_math_mode_bool }
+      {
+        \bool_set_true:N \l__tblr_cell_math_mode_bool
+        \tl_put_left:Nv \l__tblr_c_tl
+          { l__tblr_cell_ \l__tblr_cell_mode_tl _style_tl }
+        \tl_put_left:Nn \l__tblr_c_tl { $ }
+        \tl_put_right:Nn \l__tblr_c_tl { $ }
+      }
+    \tl_set:Nx \l__tblr_f_tl { \__tblr_data_item:neen { cell } {#1} {#2} { font } }
+    \tl_set:Nx \l__tblr_w_tl
+      { \__tblr_data_item:neen { cell } {#1} {#2} { width } }
+    \dim_compare:nNnT { \l__tblr_w_tl } < { 0pt } % cell width unset
+      {
+        \int_compare:nNnT
+          { \__tblr_data_item:neen { cell } {#1} {#2} { colspan } } < {2}
+          {
+            \tl_set:Nx \l__tblr_w_tl
+              { \__tblr_data_item:nen { column } {#2} { width } }
+          }
+      }
+    \dim_compare:nNnT { \l__tblr_w_tl } < { 0pt } % column width unset
+      {
+        \__tblr_save_counters:n { cell }
+        \bool_if:NTF \l__tblr_cell_math_mode_bool
+          {
+            %% Note that font = \boldmath will increase cell width (issue #137)
+            \hbox_set:Nn \l_tmpa_box { \l__tblr_f_tl \l__tblr_c_tl }
+            \tl_set:Nx \l__tblr_w_tl { \box_wd:N \l_tmpa_box }
+          }
+          {
+            \__tblr_get_cell_size_with_box:
+          }
+        \__tblr_restore_counters:n { cell }
+      }
+    \tl_put_left:NV \l__tblr_c_tl \l__tblr_f_tl
+    \tl_set:Nx \l__tblr_cell_fg_tl
+      { \__tblr_data_item:neen { cell } {#1} {#2} { foreground } }
+    \tl_if_empty:NF \l__tblr_cell_fg_tl
+      { \exp_args:NV \color \l__tblr_cell_fg_tl }
+    \__tblr_get_vcell_and_sizes:NN \l__tblr_c_tl \l__tblr_w_tl
+    \group_end:
+  }
+
+\cs_new_protected:Npn \__tblr_get_cell_size_with_box:
+  {
+    \tl_if_eq:NnTF \l__tblr_inner_spec_measure_tl { vbox }
+      { \__tblr_get_cell_size_with_vbox: }
+      { \__tblr_get_cell_size_with_hbox: }
+  }
+
+%% Varwidth won't work as expected when \color command occurs in it,
+%% and we can not fix this problem with \leavevmode command.
+%% See https://tex.stackexchange.com/q/460489.
+%% But we need to use \color command for fg option,
+%% or users may use it in the middle of the cell text,
+%% so we have redefine \color command and disable it before measuring cell.
+
+%% In order to correctly measure an enumerate environment,
+%% we need to enclose varwidth with NoHyper environment (see issue #196).
+
+\NewDocumentCommand \__tblr_fake_color_command:w { o m } { }
+
+\cs_new_protected:Npn \__tblr_get_cell_size_with_vbox:
+  {
+    \hbox_set:Nn \l_tmpa_box
+      {
+        \cs_set_eq:NN \color \__tblr_fake_color_command:w
+        \begin{tblrNoHyper}
+        \begin{varwidth}{\paperwidth}
+           \l__tblr_f_tl
+           \__tblr_rescan_cell_tokens:N \l__tblr_c_tl
+        \end{varwidth}
+        \end{tblrNoHyper}
+      }
+    \tl_set:Nx \l__tblr_w_tl { \box_wd:N \l_tmpa_box }
+  }
+
+\cs_new_protected:Npn \__tblr_get_cell_size_with_hbox:
+  {
+    \tl_set_eq:NN \l_tmpb_tl \l__tblr_c_tl
+    \__tblr_insert_braces:N \l_tmpb_tl
+    \seq_set_split:NnV \l_tmpa_seq { \\ } \l_tmpb_tl
+    \tl_set:Nn \l__tblr_w_tl { 0pt }
+    \seq_map_variable:NNn \l_tmpa_seq \l_tmpa_tl
+      {
+        \__tblr_remove_braces:N \l_tmpa_tl
+        \hbox_set:Nn \l_tmpa_box
+          {
+            \l__tblr_f_tl
+            \__tblr_rescan_cell_tokens:N \l_tmpa_tl
+          }
+        \tl_set:Nx \l__tblr_w_tl
+          { \dim_max:nn { \l__tblr_w_tl } { \box_wd:N \l_tmpa_box } }
+      }
+  }
+
+%% #1: cell text; #2: box width
+\cs_new_protected:Npn \__tblr_get_vcell_and_sizes:NN #1 #2
+  {
+    \group_begin:
+    \vbox_set:Nn \l_tmpb_box { \__tblr_make_vcell_text:NN #1 #2 }
+    \vbox_set_top:Nn \l_tmpa_box { \vbox_unpack:N \l_tmpb_box }
+    \__tblr_process_stretch:
+    \dim_gset:Nn \g__tblr_cell_wd_dim { \box_wd:N \l_tmpb_box }
+    \dim_gset:Nn \g__tblr_cell_ht_dim
+      { \box_ht:N \l_tmpb_box + \box_dp:N \l_tmpb_box }
+    \dim_gset:Nn \g__tblr_cell_head_dim { \box_ht:N \l_tmpa_box }
+    \dim_gset:Nn \g__tblr_cell_foot_dim { \box_dp:N \l_tmpb_box }
+    \tl_case:Nn \g__tblr_cell_valign_tl
+      {
+        \c__tblr_valign_h_tl
+          { \box_use:N \l_tmpa_box }
+        \c__tblr_valign_m_tl
+          {
+            \tl_case:Nn \g__tblr_cell_middle_tl
+              {
+                \c__tblr_middle_t_tl
+                  { \box_use:N \l_tmpa_box }
+                \c__tblr_middle_m_tl
+                  {
+                    \tl_set:Nx \l__tblr_b_tl
+                      {
+                        \dim_eval:n
+                          {
+                            ( \g__tblr_cell_ht_dim - \g__tblr_cell_head_dim
+                                                   - \g__tblr_cell_foot_dim ) / 2
+                          }
+                      }
+                    \box_set_ht:Nn \l_tmpb_box
+                      { \g__tblr_cell_head_dim + \l__tblr_b_tl }
+                    \box_set_dp:Nn \l_tmpb_box
+                      { \g__tblr_cell_foot_dim + \l__tblr_b_tl }
+                    \box_use:N \l_tmpb_box
+                  }
+                \c__tblr_middle_b_tl
+                  { \box_use:N \l_tmpb_box }
+              }
+          }
+        \c__tblr_valign_f_tl
+          { \box_use:N \l_tmpb_box }
+      }
+    \group_end:
+  }
+
+%% #1: cell text; #2: box width
+%% All halign commands are defined at the beginning of the file
+\cs_new_protected:Npn \__tblr_make_vcell_text:NN #1 #2
+  {
+    \dim_set:Nn \tex_hsize:D { #2 }
+    \TblrParboxRestore
+    \cs:w __tblr_halign_command_ \g__tblr_cell_halign_tl : \cs_end:
+    \__tblr_leave_vmode:
+    \bool_if:NTF \l__tblr_cell_math_mode_bool
+      { #1 }
+      { \__tblr_rescan_cell_tokens:N #1 }
+  }
+
+%% When using verb option, there is an end-of-line character at the end.
+%% This character causes extra horizontal space at the end when "measure=hbox",
+%% or causes extra vertical space at the end with "measure=vbox".
+%% Therefore we have to use an \empty to remove it.
+%% See https://tex.stackexchange.com/q/213659
+\cs_new_protected:Npn \__tblr_rescan_cell_tokens:N #1
+  {
+    \tl_if_empty:NTF \l__tblr_inner_spec_verb_tl
+      { #1 }
+      {
+        %% insert space characters after some control sequences first (issue #112)
+        \regex_replace_all:nnN { (\c{[A-Za-z]*}) ([A-Za-z]) } { \1 \  \2 } #1
+        \regex_replace_all:nnN { . } { \c{string} \0 } #1
+        \tl_set:Nx #1 { #1 \noexpand \empty }
+        \exp_args:NV \tex_scantokens:D #1
+      }
+  }
+
+%% #1: total height dimension; #2: head dimension; #3: foot dimension;
+%% #4: tl for resulting upper size; #5: tl for resulting lower size
+
+\tl_new:N \l__tblr_middle_body_tl
+
+\cs_new_protected:Npn \__tblr_get_middle_cell_upper_lower:NNNNN #1 #2 #3 #4 #5
+  {
+    \tl_case:Nn \g__tblr_cell_middle_tl
+      {
+        \c__tblr_middle_t_tl
+          {
+            \tl_set:Nx #4 { \dim_use:N #2 }
+            \tl_set:Nx #5 { \dim_eval:n { #1 - #2 } }
+          }
+        \c__tblr_middle_m_tl
+          {
+            \tl_set:Nx \l__tblr_middle_body_tl { \dim_eval:n { #1 - #2 - #3 } }
+            \tl_set:Nx #4 { \dim_eval:n { #2 + \l__tblr_middle_body_tl / 2 } }
+            \tl_set:Nx #5 { \dim_eval:n { #3 + \l__tblr_middle_body_tl / 2 } }
+          }
+        \c__tblr_middle_b_tl
+          {
+            \tl_set:Nx #4 { \dim_eval:n { #1 - #3 } }
+            \tl_set:Nx #5 { \dim_use:N #3 }
+          }
+      }
+  }
+
+%% Update natural dimensions of the cell
+%% #1: row number; #2 column number; #3: width dimension;
+%% #4: total height dimension; #5: head dimension; #6: foot dimension
+\cs_new_protected:Npn \__tblr_update_cell_size:nnNNNN #1 #2 #3 #4 #5 #6
+  {
+    \group_begin:
+    \tl_set:Nx \l__tblr_c_tl
+      { \__tblr_data_item:neen { cell } {#1} {#2} { colspan } }
+    \int_compare:nNnT { \l__tblr_c_tl } > {1}
+      {
+        \__tblr_data_gput:neene { cell } {#1} {#2} { @cell-width } {\dim_use:N #3}
+        \dim_gzero:N #3 % don't affect column width
+      }
+    \tl_set:Nx \l__tblr_r_tl
+      { \__tblr_data_item:neen { cell } {#1} {#2} { rowspan } }
+    \int_compare:nNnT { \l__tblr_r_tl } > {1}
+      {
+        \tl_case:Nn \g__tblr_cell_valign_tl
+          {
+            \c__tblr_valign_h_tl
+              {
+                \tl_set:Nx \l__tblr_u_tl { \dim_use:N #5 }
+                \tl_set:Nx \l__tblr_v_tl { \dim_eval:n { #4 - #5 } }
+                %% Update the head size of the first span row here
+                \__tblr_data_gput_if_larger:nene
+                  { row } {#1} { @row-head } { \dim_use:N #5 }
+              }
+            \c__tblr_valign_f_tl
+              {
+                \tl_set:Nx \l__tblr_u_tl { \dim_eval:n { #4 - #6 } }
+                \tl_set:Nx \l__tblr_v_tl { \dim_use:N #6 }
+                %% Update the foot size of the last span row here
+                \__tblr_data_gput_if_larger:nene
+                  { row }
+                  { \int_eval:n { #1 + \l__tblr_r_tl - 1 } }
+                  { @row-foot }
+                  { \dim_use:N #6 }
+              }
+            \c__tblr_valign_m_tl
+              {
+                \__tblr_get_middle_cell_upper_lower:NNNNN
+                  #4 #5 #6 \l__tblr_u_tl \l__tblr_v_tl
+              }
+          }
+        \__tblr_data_gput:neenV { cell } {#1} {#2} { @cell-height } \l__tblr_u_tl
+        \__tblr_data_gput:neenV { cell } {#1} {#2} { @cell-depth } \l__tblr_v_tl
+        %% Don't affect row sizes
+        \dim_gzero:N #4
+        \dim_gzero:N #5
+        \dim_gzero:N #6
+      }
+    \group_end:
+  }
+
+
+%% Update size of the row. #1: row number; #2: column number;
+%% #3: total height dimension; #4: head dimension; #5: foot dimension
+\cs_new_protected:Npn \__tblr_update_row_size:nnNNN #1 #2 #3 #4 #5
+  {
+    \group_begin:
+    %% Note that \l__tblr_h_tl may be empty
+    \tl_set:Nx \l__tblr_h_tl
+      { \__tblr_data_item:nen { row } {#1} { @row-height } }
+    \tl_if_eq:NNTF \g__tblr_cell_valign_tl \c__tblr_valign_m_tl
+      {
+        \tl_set:Nx \l__tblr_a_tl
+          { \__tblr_data_item:nen { row } {#1} { @row-upper } }
+        \tl_set:Nx \l__tblr_b_tl
+          { \__tblr_data_item:nen { row } {#1} { @row-lower } }
+        \__tblr_get_middle_cell_upper_lower:NNNNN
+          #3 #4 #5 \l__tblr_u_tl \l__tblr_v_tl
+        \dim_compare:nNnT { \l__tblr_u_tl } > { \l__tblr_a_tl }
+          {
+            \tl_set_eq:NN \l__tblr_a_tl \l__tblr_u_tl
+            \__tblr_data_gput:nenV { row } {#1} { @row-upper } \l__tblr_a_tl
+          }
+        \dim_compare:nNnT { \l__tblr_v_tl } > { \l__tblr_b_tl }
+          {
+            \tl_set_eq:NN \l__tblr_b_tl \l__tblr_v_tl
+            \__tblr_data_gput:nenV { row } {#1} { @row-lower } \l__tblr_b_tl
+          }
+        \dim_compare:nNnT
+          { \l__tblr_a_tl + \l__tblr_b_tl } > { \l__tblr_h_tl + 0pt }
+          {
+            \__tblr_data_gput:nene { row } {#1} { @row-height }
+              { \dim_eval:n { \l__tblr_a_tl + \l__tblr_b_tl } }
+          }
+      }
+      {
+        \tl_set:Nx \l__tblr_e_tl
+          { \__tblr_data_item:nen { row } {#1} { @row-head } }
+        \tl_set:Nx \l__tblr_f_tl
+          { \__tblr_data_item:nen { row } {#1} { @row-foot } }
+        \dim_compare:nNnT {#4} > {\l__tblr_e_tl}
+          {
+            \__tblr_data_gput:nene { row } {#1} { @row-head } { \dim_use:N #4 }
+          }
+        \dim_compare:nNnT {#5} > {\l__tblr_f_tl}
+          {
+            \__tblr_data_gput:nene { row } {#1} { @row-foot } { \dim_use:N #5 }
+          }
+        \tl_set:Nx \l__tblr_x_tl { \dim_max:nn {#4} { \l__tblr_e_tl } }
+        \tl_set:Nx \l__tblr_y_tl { \dim_max:nn {#5} { \l__tblr_f_tl } }
+        \dim_compare:nNnT
+          { #3 - #4 - #5 } > { \l__tblr_h_tl - \l__tblr_x_tl - \l__tblr_y_tl }
+          {
+            \__tblr_data_gput:nene { row } {#1} { @row-height }
+              {
+                \dim_eval:n
+                  {
+                    \l__tblr_x_tl
+                    + \dim_use:N #3 - \dim_use:N #4 - \dim_use:N #5
+                    + \l__tblr_y_tl
+                  }
+              }
+          }
+      }
+    \group_end:
+  }
+
+
+%% Update size of the column. #1: column number; #2: width dimension
+
+\cs_new_protected:Npn \__tblr_update_col_size:nN #1 #2
+  {
+    \tl_set:Nx \l_tmpb_tl
+      { \__tblr_data_item:nen { column } {#1} { @col-width } }
+    \bool_lazy_or:nnT
+      { \tl_if_empty_p:N \l_tmpb_tl }
+      { \dim_compare_p:nNn { \dim_use:N #2 } > { \l_tmpb_tl } }
+      {
+        \__tblr_data_gput:nene { column } {#1} { @col-width } { \dim_use:N #2 }
+      }
+  }
+
+%%% --------------------------------------------------------
+%%> \section{Calculate and Adjust Extendable Columns}
+%%% --------------------------------------------------------
+
+%% Compute column widths when there are some extendable columns
+
+\dim_new:N \l__column_target_dim
+\prop_new:N \l__column_coefficient_prop
+\prop_new:N \l__column_natural_width_prop
+\prop_new:N \l__column_computed_width_prop
+
+\msg_new:nnn { tabularray } { table-width-too-small }
+  { Table ~ width ~ is ~ too ~ small, ~ need ~ #1 ~ more! }
+
+\cs_new_protected:Npn \__tblr_compute_extendable_column_width:
+  {
+    \__tblr_collect_extendable_column_width:
+    \dim_compare:nNnTF { \l__column_target_dim } < { 0pt }
+      {
+        \msg_warning:nnx { tabularray } { table-width-too-small }
+          { \dim_abs:n { \l__column_target_dim } }
+      }
+      {
+        \prop_if_empty:NF \l__column_coefficient_prop
+          { \__tblr_adjust_extendable_column_width: }
+      }
+  }
+
+\cs_new_protected:Npn \__tblr_collect_extendable_column_width:
+  {
+    \tl_set:Nx \l_tmpa_tl { \__tblr_prop_item:nn { inner } { width } }
+    \tl_if_empty:NTF \l_tmpa_tl
+      { \dim_set_eq:NN \l__column_target_dim \linewidth }
+      { \dim_set:Nn \l__column_target_dim { \l_tmpa_tl } }
+    \prop_clear:N \l__column_coefficient_prop
+    \prop_clear:N \l__column_natural_width_prop
+    \prop_clear:N \l__column_computed_width_prop
+    \int_step_variable:nNn { \c at colcount } \l__tblr_j_tl
+      {
+        \tl_set:Nx \l__tblr_a_tl
+          { \__tblr_data_item:nen { column } { \l__tblr_j_tl } { width } }
+        \tl_set:Nx \l__tblr_b_tl
+          { \__tblr_data_item:nen { column } { \l__tblr_j_tl } { coefficient } }
+        \tl_set:Nx \l__tblr_c_tl
+          { \__tblr_data_item:nen  { column } { \l__tblr_j_tl } { @col-width } }
+        \dim_compare:nNnTF { \l__tblr_a_tl } < { 0pt } % column width unset
+          {
+            \dim_compare:nNnTF { \l__tblr_b_tl pt } = { 0pt }
+              { \dim_sub:Nn \l__column_target_dim { \l__tblr_c_tl } }
+              {
+                \prop_put:Nxx \l__column_coefficient_prop
+                  { \l__tblr_j_tl } { \l__tblr_b_tl }
+                \prop_put:Nxn \l__column_computed_width_prop
+                  { \l__tblr_j_tl } { 0pt }
+                \dim_compare:nNnF { \l__tblr_b_tl pt } > { 0pt }
+                  {
+                    \prop_put:Nxx \l__column_natural_width_prop
+                      { \l__tblr_j_tl } { \l__tblr_c_tl }
+                  }
+              }
+          }
+          { \dim_sub:Nn \l__column_target_dim { \l__tblr_a_tl } }
+        \tl_set:Nx \l__tblr_a_tl
+          { \__tblr_spec_item:ne { vline } { [\l__tblr_j_tl] / @vline-width } }
+        \tl_set:Nx \l__tblr_b_tl
+          { \__tblr_data_item:nen { column } { \l__tblr_j_tl } { leftsep } }
+        \tl_set:Nx \l__tblr_c_tl
+          { \__tblr_data_item:nen { column } { \l__tblr_j_tl } { rightsep } }
+        \dim_set:Nn \l__column_target_dim
+          { \l__column_target_dim - \l__tblr_a_tl - \l__tblr_b_tl - \l__tblr_c_tl }
+      }
+    \tl_set:Nx \l__tblr_a_tl
+      {
+        \__tblr_spec_item:ne { vline }
+          { [\int_eval:n {\c at colcount + 1}] / @vline-width }
+      }
+    \tl_if_empty:NF \l__tblr_a_tl
+      { \dim_sub:Nn \l__column_target_dim { \l__tblr_a_tl } }
+    \LogTblrTracing { target }
+  }
+
+%% If all columns have negative coefficients and small natural widths,
+%% \l__column_coefficient_prop will be empty after one or more rounds.
+%% We reset @row-height, etc for \linewidth graphics in X columns (issue #80)
+\cs_new_protected:Npn \__tblr_adjust_extendable_column_width:
+  {
+    \bool_while_do:nn
+      { \dim_compare_p:nNn { \l__column_target_dim } > { \hfuzz } }
+      {
+        \prop_if_empty:NTF \l__column_coefficient_prop
+          { \__tblr_adjust_extendable_column_width_negative: }
+          { \__tblr_adjust_extendable_column_width_once: }
+      }
+    \prop_map_inline:Nn \l__column_computed_width_prop
+      {
+        \__tblr_data_gput:nnne { column } {##1} { width } {##2}
+        \__tblr_data_gput:nnnn { column } {##1} { @col-width } { 0pt }
+      }
+    \int_step_inline:nn { \c at rowcount }
+      {
+        \__tblr_data_gput:nnnn { row } {##1} { @row-height } { 0pt }
+        \__tblr_data_gput:nnnn { row } {##1} { @row-head } { 0pt }
+        \__tblr_data_gput:nnnn { row } {##1} { @row-foot } { 0pt }
+        \__tblr_data_gput:nnnn { row } {##1} { @row-upper } { 0pt }
+        \__tblr_data_gput:nnnn { row } {##1} { @row-lower } { 0pt }
+      }
+    \__tblr_calculate_cell_sizes:
+  }
+
+%% We use dimen register, since the coefficient may be a decimal number
+\cs_new_protected:Npn \__tblr_adjust_extendable_column_width_once:
+  {
+    \dim_zero:N \l_tmpa_dim
+    \prop_map_inline:Nn \l__column_coefficient_prop
+      {
+        \dim_add:Nn \l_tmpa_dim { \dim_abs:n { ##2 pt } }
+      }
+    \tl_set:Nx \l__tblr_w_tl
+      { \dim_ratio:nn { \l__column_target_dim } { \l_tmpa_dim } }
+    \dim_zero:N \l__column_target_dim
+    \prop_map_inline:Nn \l__column_coefficient_prop
+      {
+        \tl_set:Nx \l__tblr_a_tl
+          { \dim_eval:n { \dim_abs:n { ##2 pt } * \l__tblr_w_tl } }
+        \dim_compare:nNnTF { ##2 pt } > { 0pt }
+          {
+            \__tblr_add_dimen_value:Nnn
+              \l__column_computed_width_prop { ##1 } { \l__tblr_a_tl }
+          }
+          {
+            \tl_set:Nx \l__tblr_b_tl
+              { \prop_item:Nn \l__column_natural_width_prop { ##1 } }
+            \tl_set:Nx \l__tblr_c_tl
+              { \prop_item:Nn \l__column_computed_width_prop { ##1 } }
+            \dim_compare:nNnTF { \l__tblr_a_tl + \l__tblr_c_tl } > { \l__tblr_b_tl }
+              {
+                \prop_put:Nnx \l__column_computed_width_prop
+                  { ##1 } { \l__tblr_b_tl }
+                \dim_add:Nn \l__column_target_dim
+                  { \l__tblr_a_tl + \l__tblr_c_tl - \l__tblr_b_tl }
+                \prop_remove:Nn \l__column_coefficient_prop { ##1 }
+              }
+              {
+                \__tblr_add_dimen_value:Nnn
+                  \l__column_computed_width_prop { ##1 } { \l__tblr_a_tl }
+              }
+          }
+      }
+    \LogTblrTracing { target }
+  }
+
+\cs_new_protected:Npn \__tblr_adjust_extendable_column_width_negative:
+  {
+    \dim_zero:N \l_tmpa_dim
+    \prop_map_inline:Nn \l__column_natural_width_prop
+      { \dim_add:Nn \l_tmpa_dim { ##2 } }
+    \tl_set:Nx \l_tmpa_tl
+      { \dim_ratio:nn { \l__column_target_dim } { \l_tmpa_dim } }
+    \dim_zero:N \l__column_target_dim
+    \prop_map_inline:Nn \l__column_natural_width_prop
+      {
+        \tl_set:Nx \l_tmpb_tl { \dim_eval:n { ##2 * \l_tmpa_tl } }
+        \__tblr_add_dimen_value:Nnn
+          \l__column_computed_width_prop { ##1 } { \l_tmpb_tl }
+      }
+    \LogTblrTracing { target }
+  }
+
+%%% --------------------------------------------------------
+%%> \section{Calculate and Adjust Multispan Cells}
+%%% --------------------------------------------------------
+
+%% Compute and adjust widths when there are some span cells.
+%% By default, we will compute column widths from span widths;
+%% but if we set table option "hspan = minimal",
+%% we will compute span widths from column widths.
+
+\cs_new_protected:Npn \__tblr_adjust_sizes_for_span_cells:
+  {
+    \__tblr_prop_if_in:nnT { inner } { colspan }
+      {
+        \__tblr_collect_column_widths_skips:
+        \str_if_eq:xnTF
+          { \__tblr_prop_item:ne { inner } { hspan } } { minimal }
+          {
+            \__tblr_set_span_widths_from_column_widths:
+          }
+          {
+            \__tblr_collect_span_widths:
+            \__tblr_set_column_widths_from_span_widths:
+          }
+        \LogTblrTracing { column }
+        \__tblr_calculate_cell_sizes:
+      }
+    \__tblr_prop_if_in:nnT { inner } { rowspan }
+      {
+        \__tblr_collect_row_heights_skips:
+        \__tblr_collect_span_heights:
+        \__tblr_set_row_heights_from_span_heights:
+        \LogTblrTracing { row }
+      }
+  }
+
+\prop_new:N \l__tblr_col_item_skip_size_prop
+\prop_new:N \l__tblr_col_span_size_prop
+\prop_new:N \l__tblr_row_item_skip_size_prop
+\prop_new:N \l__tblr_row_span_size_prop
+
+\cs_new_protected:Npn \__tblr_collect_column_widths_skips:
+  {
+    \prop_clear:N \l__tblr_col_item_skip_size_prop
+    \int_step_variable:nNn { \c at colcount } \l__tblr_j_tl
+      {
+        \int_compare:nNnTF { \l__tblr_j_tl } > { 1 }
+          {
+            \prop_put:Nxx \l__tblr_col_item_skip_size_prop { skip[\l__tblr_j_tl] }
+              {
+                \dim_eval:n
+                  {
+                    \__tblr_data_item:nen { column }
+                      { \int_eval:n { \l__tblr_j_tl - 1 } } { rightsep }
+                    +
+                    \__tblr_spec_item:ne { vline }
+                      { [\l__tblr_j_tl] / @vline-width }
+                    +
+                    \__tblr_data_item:nen { column } { \l__tblr_j_tl } { leftsep }
+                  }
+              }
+          }
+          {
+            \prop_put:Nxn \l__tblr_col_item_skip_size_prop { skip[\l__tblr_j_tl] }
+              { 0pt }
+          }
+        \prop_put:Nxx \l__tblr_col_item_skip_size_prop { item[\l__tblr_j_tl] }
+          { \__tblr_data_item:nen { column } { \l__tblr_j_tl } { @col-width } }
+      }
+    \__tblr_do_if_tracing:nn { cellspan }
+      { \prop_log:N \l__tblr_col_item_skip_size_prop }
+  }
+
+\cs_new_protected:Npn \__tblr_collect_row_heights_skips:
+  {
+    \prop_clear:N \l__tblr_row_item_skip_size_prop
+    \int_step_variable:nNn { \c at rowcount } \l__tblr_i_tl
+      {
+        \int_compare:nNnTF { \l__tblr_i_tl } > { 1 }
+          {
+            \prop_put:Nxx \l__tblr_row_item_skip_size_prop { skip[\l__tblr_i_tl] }
+              {
+                \dim_eval:n
+                  {
+                    \__tblr_data_item:nen { row }
+                      { \int_eval:n {\l__tblr_i_tl - 1} } { belowsep }
+                    +
+                    \__tblr_spec_item:ne { hline }
+                      { [\l__tblr_i_tl] / @hline-height }
+                    +
+                    \__tblr_data_item:nen { row } { \l__tblr_i_tl } { abovesep }
+                  }
+              }
+          }
+          {
+            \prop_put:Nxn \l__tblr_row_item_skip_size_prop { skip[\l__tblr_i_tl] }
+              { 0pt }
+          }
+        \__tblr_collect_one_row_height:NN \l__tblr_i_tl \l__tblr_h_tl
+        \prop_put:Nxx \l__tblr_row_item_skip_size_prop
+          { item[\l__tblr_i_tl] } { \l__tblr_h_tl }
+      }
+    \__tblr_do_if_tracing:nn { cellspan }
+      { \prop_log:N \l__tblr_row_item_skip_size_prop }
+  }
+
+%% #1: row number; #2: tl with result
+\cs_new_protected:Npn \__tblr_collect_one_row_height:NN #1 #2
+  {
+    \tl_set:Nx #2 { \__tblr_data_item:nen { row } {#1} { @row-height } }
+  }
+
+\cs_new_protected:Npn \__tblr_collect_span_widths:
+  {
+    \prop_clear:N \l__tblr_col_span_size_prop
+    \int_step_variable:nNn { \c at colcount } \l__tblr_j_tl
+      {
+        \int_step_variable:nNn { \c at rowcount } \l__tblr_i_tl
+          {
+            \tl_set:Nx \l__tblr_a_tl
+              {
+                \__tblr_data_item:neen { cell }
+                  { \l__tblr_i_tl } { \l__tblr_j_tl } { colspan }
+              }
+            \int_compare:nNnT { \l__tblr_a_tl } > {1}
+              {
+                \__tblr_put_if_larger:Nxx \l__tblr_col_span_size_prop
+                  {
+                    ( \l__tblr_j_tl -
+                      \int_eval:n {\l__tblr_j_tl + \l__tblr_a_tl - 1} )
+                  }
+                  {
+                    \__tblr_data_item:neen { cell }
+                      { \l__tblr_i_tl } { \l__tblr_j_tl } { @cell-width }
+                  }
+              }
+          }
+      }
+    \__tblr_do_if_tracing:nn { cellspan }
+      { \prop_log:N \l__tblr_col_span_size_prop }
+  }
+
+\prop_new:N \l__tblr_row_span_to_row_prop
+
+\cs_new_protected:Npn \__tblr_collect_span_heights:
+  {
+    \prop_clear:N \l__tblr_row_span_to_row_prop
+    \prop_clear:N \l__tblr_row_span_size_prop
+    \int_step_variable:nNn { \c at rowcount } \l__tblr_i_tl
+      {
+        \int_step_variable:nNn { \c at colcount } \l__tblr_j_tl
+          {
+            \tl_set:Nx \l__tblr_a_tl
+              {
+                \__tblr_data_item:neen { cell }
+                  { \l__tblr_i_tl } { \l__tblr_j_tl } { rowspan }
+              }
+            \int_compare:nNnT { \l__tblr_a_tl } > {1}
+              {
+                \tl_set:Nx \l__tblr_v_tl
+                  {
+                    \__tblr_data_item:neen { cell }
+                      { \l__tblr_i_tl } { \l__tblr_j_tl } { valign }
+                  }
+                \tl_if_eq:NnT \l__tblr_v_tl { h }
+                  {
+                    \tl_set:Nx \l__tblr_h_tl
+                      {
+                        \__tblr_data_item:nen { row }
+                          { \l__tblr_i_tl } { @row-head }
+                      }
+                    \__tblr_data_gput:neenV { cell }
+                      { \l__tblr_i_tl } { \l__tblr_j_tl } { @cell-height }
+                      \l__tblr_h_tl
+                  }
+                \tl_if_eq:NnT \l__tblr_v_tl { f }
+                  {
+                    \tl_set:Nx \l__tblr_d_tl
+                      {
+                        \__tblr_data_item:nen
+                          { row }
+                          { \int_eval:n { \l__tblr_i_tl + \l__tblr_a_tl - 1 } }
+                          { @row-foot }
+                      }
+                    \__tblr_data_gput:neenV { cell }
+                      { \l__tblr_i_tl } { \l__tblr_j_tl } { @cell-depth }
+                      \l__tblr_d_tl
+                  }
+                \__tblr_put_if_larger:Nxx \l__tblr_row_span_size_prop
+                  {
+                    ( \l__tblr_i_tl -
+                      \int_eval:n {\l__tblr_i_tl + \l__tblr_a_tl - 1} )
+                  }
+                  {
+                    \dim_eval:n
+                      {
+                        \__tblr_data_item:neen { cell }
+                          { \l__tblr_i_tl } { \l__tblr_j_tl } { @cell-height }
+                        +
+                        \__tblr_data_item:neen { cell }
+                          { \l__tblr_i_tl } { \l__tblr_j_tl } { @cell-depth }
+                      }
+                  }
+                \prop_put:Nxx \l__tblr_row_span_to_row_prop
+                  { [\l__tblr_i_tl][\l__tblr_j_tl] }
+                  { \int_eval:n {\l__tblr_i_tl + \l__tblr_a_tl - 1} }
+              }
+          }
+      }
+    \__tblr_do_if_tracing:nn { cellspan }
+      {
+        \prop_log:N \l__tblr_row_span_to_row_prop
+        \prop_log:N \l__tblr_row_span_size_prop
+      }
+  }
+
+%% Compute and set column widths from span widths
+\cs_new_protected:Npn \__tblr_set_column_widths_from_span_widths:
+  {
+    \str_if_eq:xnTF
+      { \__tblr_prop_item:ne { inner } { hspan } }
+      { even }
+      {
+        \__tblr_distribute_span_sizes_even:xNN
+          { \int_use:N \c at colcount }
+          \l__tblr_col_item_skip_size_prop
+          \l__tblr_col_span_size_prop
+      }
+      {
+        \__tblr_distribute_span_sizes_default:xNN
+          { \int_use:N \c at colcount }
+          \l__tblr_col_item_skip_size_prop
+          \l__tblr_col_span_size_prop
+      }
+    \__tblr_set_all_column_widths:
+  }
+
+%% Compute and set row heights from span heights
+\cs_new_protected:Npn \__tblr_set_row_heights_from_span_heights:
+  {
+    \str_if_eq:xnTF
+      { \__tblr_prop_item:ne { inner } { vspan } }
+      { even }
+      {
+        \__tblr_distribute_span_sizes_even:nNN
+          { \int_use:N \c at rowcount }
+          \l__tblr_row_item_skip_size_prop
+          \l__tblr_row_span_size_prop
+      }
+      {
+        \__tblr_distribute_span_sizes_default:xNN
+          { \int_use:N \c at rowcount }
+          \l__tblr_row_item_skip_size_prop
+          \l__tblr_row_span_size_prop
+      }
+    \__tblr_set_all_row_heights:
+  }
+
+%% See page 245 in Chapter 22 of TeXbook
+%% #1: total number of items
+%% #2: prop list with item sizes and skip sizes; #3: prop list with span sizes
+\cs_new_protected:Npn \__tblr_distribute_span_sizes_default:nNN #1 #2 #3
+  {
+    \int_step_variable:nNn { #1 } \l__tblr_j_tl
+      {
+        \dim_set:Nn \l__tblr_w_dim
+          {
+            \prop_item:Ne #2 { item[\l__tblr_j_tl] }
+          }
+        \int_step_variable:nNn { \l__tblr_j_tl - 1 } \l__tblr_i_tl
+          {
+            \tl_set:Nx \l__tblr_a_tl
+              { \prop_item:Ne #3 { (\l__tblr_i_tl-\l__tblr_j_tl) } }
+            \tl_if_empty:NF \l__tblr_a_tl
+              {
+                \int_step_variable:nnNn
+                  { \l__tblr_i_tl } { \l__tblr_j_tl - 1 } \l__tblr_k_tl
+                  {
+                    \__tblr_do_if_tracing:nn { cellspan }
+                      {
+                        \tl_log:x
+                          { \l__tblr_j_tl : \l__tblr_i_tl -> \l__tblr_k_tl }
+                      }
+                    \tl_set:Nx \l_tmpa_tl
+                      {
+                        \prop_item:Ne #2 { itemskip[\l__tblr_k_tl] }
+                      }
+                    \tl_set:Nx \l__tblr_a_tl
+                      { \dim_eval:n { \l__tblr_a_tl - \l_tmpa_tl } }
+                  }
+                \dim_compare:nNnT { \l__tblr_a_tl } > { \l__tblr_w_dim }
+                  {
+                    \dim_set:Nn \l__tblr_w_dim { \l__tblr_a_tl }
+                  }
+              }
+          }
+        \prop_put:Nxx #2
+          { item[\l__tblr_j_tl] } { \dim_use:N \l__tblr_w_dim }
+        \int_compare:nNnT { \l__tblr_j_tl } < { #1 }
+          {
+            \tl_set:Nx \l_tmpb_tl
+              {
+                \prop_item:Ne #2
+                  { skip[\int_eval:n { \l__tblr_j_tl + 1} ] }
+              }
+            \dim_add:Nn \l__tblr_w_dim { \l_tmpb_tl }
+            \prop_put:Nxx #2
+              { itemskip[\l__tblr_j_tl] } { \dim_use:N \l__tblr_w_dim }
+          }
+      }
+    \__tblr_do_if_tracing:nn { cellspan } { \prop_log:N #2 }
+  }
+\cs_generate_variant:Nn \__tblr_distribute_span_sizes_default:nNN { x }
+
+%% #1: total number of items
+%% #2: prop list with item sizes and skip sizes; #3: prop list with span sizes
+\cs_new_protected:Npn \__tblr_distribute_span_sizes_even:nNN #1 #2 #3
+  {
+    \prop_clear:N \l_tmpa_prop
+    \prop_map_inline:Nn #3
+      {
+        \__tblr_get_span_from_to:w ##1
+        \dim_set:Nn \l_tmpa_dim {##2}
+        \dim_sub:Nn \l_tmpa_dim { \prop_item:Ne #2 { item[\l__tblr_a_tl] } }
+        \int_step_inline:nnn { \l__tblr_a_tl + 1 } { \l__tblr_b_tl }
+          {
+            \dim_sub:Nn \l_tmpa_dim
+              {
+                \prop_item:Ne #2 { skip[####1] } + \prop_item:Nn #2 { item[####1] }
+              }
+          }
+        \__tblr_do_if_tracing:nn { cellspan }
+          {
+            \tl_log:x { \l__tblr_a_tl -> \l__tblr_b_tl : ~ \dim_use:N \l_tmpa_dim }
+          }
+        \dim_compare:nNnT {\l_tmpa_dim} > {0pt}
+          {
+            \tl_set:Nx \l_tmpa_tl
+              { \dim_eval:n { \l_tmpa_dim / (\l__tblr_b_tl - \l__tblr_a_tl + 1) } }
+            \int_step_inline:nnn { \l__tblr_a_tl } { \l__tblr_b_tl }
+              {
+                \__tblr_put_if_larger:NnV \l_tmpa_prop {####1} \l_tmpa_tl
+              }
+          }
+      }
+    \__tblr_do_if_tracing:nn { cellspan } { \prop_log:N \l_tmpa_prop }
+    \prop_map_inline:Nn \l_tmpa_prop
+      {
+        \__tblr_add_dimen_value:Nnn #2 {item[##1]} {##2}
+      }
+    \__tblr_do_if_tracing:nn { cellspan } { \prop_log:N #2 }
+  }
+\cs_generate_variant:Nn \__tblr_distribute_span_sizes_even:nNN { x }
+
+\cs_new_protected:Npn \__tblr_get_span_from_to:w (#1-#2)
+  {
+    \tl_set:Nn \l__tblr_a_tl {#1}
+    \tl_set:Nn \l__tblr_b_tl {#2}
+  }
+
+\cs_new_protected:Npn \__tblr_set_all_column_widths:
+  {
+    \int_step_variable:nNn { \c at colcount } \l__tblr_j_tl
+      {
+        \__tblr_data_gput:nene { column }
+          { \l__tblr_j_tl } { width }
+          { \prop_item:Ne \l__tblr_col_item_skip_size_prop { item[\l__tblr_j_tl] } }
+      }
+  }
+
+\cs_new_protected:Npn \__tblr_set_all_row_heights:
+  {
+    \int_step_variable:nNn { \c at rowcount } \l__tblr_i_tl
+      {
+        \tl_set:Nx \l__tblr_h_tl
+          {
+            \__tblr_data_item:nen { row } { \l__tblr_i_tl } { @row-head }
+          }
+        \tl_set:Nx \l__tblr_d_tl
+          {
+            \__tblr_data_item:nen { row } { \l__tblr_i_tl } { @row-foot }
+          }
+        \tl_set:Nx \l__tblr_a_tl
+          {
+            \prop_item:Ne \l__tblr_row_item_skip_size_prop { item[\l__tblr_i_tl] }
+          }
+        \__tblr_collect_one_row_height:NN \l__tblr_i_tl \l__tblr_t_tl
+        \__tblr_data_gput:nene { row }
+          { \l__tblr_i_tl } { @row-height } { \l__tblr_a_tl }
+      }
+  }
+
+%% Compute and set span widths from column widths
+\cs_new_protected:Npn \__tblr_set_span_widths_from_column_widths:
+  {
+    \int_step_variable:nNn { \c at colcount } \l__tblr_j_tl
+      {
+        \int_step_variable:nNn { \c at rowcount } \l__tblr_i_tl
+          {
+            \tl_set:Nx \l__tblr_a_tl
+              {
+                \__tblr_data_item:neen { cell }
+                  { \l__tblr_i_tl } { \l__tblr_j_tl } { colspan }
+              }
+            \int_compare:nNnT { \l__tblr_a_tl } > {1}
+              {
+                \__tblr_calc_span_widths:xxN
+                  { \l__tblr_j_tl }
+                  { \int_eval:n { \l__tblr_j_tl + \l__tblr_a_tl - 1 } }
+                  \l__tblr_w_dim
+                \__tblr_data_gput:neene { cell }
+                  { \l__tblr_i_tl } { \l__tblr_j_tl } { width }
+                  { \dim_use:N \l__tblr_w_dim }
+              }
+          }
+      }
+  }
+
+%% Cell is spanned from col #1 to col #2, #3 is the return dim
+\cs_new_protected:Npn \__tblr_calc_span_widths:nnN #1 #2 #3
+  {
+    \dim_set:Nn #3 { \prop_item:Ne \l__tblr_col_item_skip_size_prop { item[#1] } }
+    \int_step_inline:nnn { #1 + 1 } { #2 }
+      {
+        \tl_set:Nx \l_tmpa_tl
+          { \prop_item:Ne \l__tblr_col_item_skip_size_prop { skip[##1] } }
+        \tl_set:Nx \l_tmpb_tl
+          { \prop_item:Ne \l__tblr_col_item_skip_size_prop { item[##1] } }
+        \dim_add:Nn #3 { \dim_eval:n { \l_tmpa_tl + \l_tmpb_tl } }
+      }
+  }
+\cs_generate_variant:Nn \__tblr_calc_span_widths:nnN { xxN }
+
+%%% --------------------------------------------------------
+%%> \section{Header and Footer Styles}
+%%% --------------------------------------------------------
+
+\prop_new:N \l__tblr_element_styles_prop
+
+\cs_new_protected:Npn \__tblr_style_put:nn #1 #2
+  {
+    \prop_put:Nnn \l__tblr_element_styles_prop {#1} {#2}
+  }
+\cs_generate_variant:Nn \__tblr_style_put:nn { nV, ne, en, eV }
+
+\cs_new:Npn \__tblr_style_item:n #1
+  {
+    \prop_item:Nn \l__tblr_element_styles_prop {#1}
+  }
+
+\cs_new_protected:Npn \__tblr_style_log:
+  {
+    \prop_log:N \l__tblr_element_styles_prop
+  }
+
+\tl_new:N \l__tblr_element_name_tl
+\tl_new:N \l__tblr_element_styles_tl
+
+%% #1: list of element names; #2: element styles
+\NewDocumentCommand \SetTblrStyle { m +m }
+  {
+    \tl_set:Nn \l__tblr_element_styles_tl {#2}
+    \keys_set:nn { tblr-element } {#1}
+    \ignorespaces
+  }
+
+\keys_define:nn { tblr-element }
+  {
+    head    .meta:n = { firsthead, middlehead, lasthead },
+    foot    .meta:n = { firstfoot, middlefoot, lastfoot },
+    unknown .code:n = \__tblr_set_element_styles:V \l_keys_key_str,
+  }
+
+\cs_new_protected:Npn \__tblr_set_element_styles:n #1
+  {
+    \tl_set:Nn \l__tblr_element_name_tl {#1}
+    \keys_set:nV { tblr-style } \l__tblr_element_styles_tl
+  }
+\cs_generate_variant:Nn \__tblr_set_element_styles:n { V }
+
+\keys_define:nn { tblr-style }
+  {
+    halign  .code:n = \__tblr_element_gput_style:nn { halign } {#1},
+    l       .meta:n = { halign = l },
+    c       .meta:n = { halign = c },
+    r       .meta:n = { halign = r },
+    j       .meta:n = { halign = j },
+    fg      .code:n = \__tblr_element_gput_style:nn { fg } {#1},
+    font    .code:n = \__tblr_element_gput_style:nn { font } {#1},
+    hang    .code:n = \__tblr_element_gput_style:nn { hang } {#1},
+    indent  .code:n = \__tblr_element_gput_style:nn { indent } {#1},
+    unknown .code:n = \__tblr_element_unknown_key:Vn \l_keys_key_str {#1},
+  }
+
+\cs_new_protected:Npn \__tblr_element_gput_style:nn #1 #2
+  {
+    \__tblr_style_put:en { \l__tblr_element_name_tl / #1 } {#2}
+  }
+
+\cs_new_protected:Npn \__tblr_element_unknown_key:nn #1 #2
+  {
+    \regex_match:NnTF \c__tblr_is_color_key_regex {#1}
+      { \__tblr_style_put:en { \l__tblr_element_name_tl / fg } {#1} }
+      {
+        %% unknown key name has been changed to string in \l_keys_key_str
+        \tl_set_rescan:Nnn \l__tblr_f_tl {} {#1}
+        \tl_if_head_eq_catcode:VNTF \l__tblr_f_tl \scan_stop:
+          {
+            \__tblr_style_put:eV { \l__tblr_element_name_tl / font } \l__tblr_f_tl
+          }
+          {
+            \__tblr_style_put:en { \l__tblr_element_name_tl / #1 } {#2}
+          }
+      }
+  }
+\cs_generate_variant:Nn \__tblr_element_unknown_key:nn { Vn }
+
+%%% --------------------------------------------------------
+%%> \section{Helper Functions for Templates}
+%%% --------------------------------------------------------
+
+\tl_new:N \l__tblr_template_name_tl
+\tl_new:N \l__tblr_template_code_tl
+
+\keys_define:nn { tblr-def-template }
+  {
+    unknown .code:n = \__tblr_def_template:V \l_keys_key_str,
+  }
+
+%% #1: head/foot element; #2: template name; #3: template code
+%% If the template name = default, we enable the template at once
+%% Otherwise, we may enable the template by using \SetTblrTemplate command
+\NewDocumentCommand \DefTblrTemplate { m m +m }
+  {
+    \tl_set:Nn \l__tblr_template_name_tl {#2}
+    \tl_set:Nn \l__tblr_template_code_tl {#3}
+    \keys_set:nn { tblr-def-template } {#1}
+    \ignorespaces
+  }
+\cs_new_eq:NN \DeclareTblrTemplate \DefTblrTemplate
+
+\cs_new_protected:Npn \__tblr_def_template:n #1
+  {
+    \tl_set_eq:cN { l__tblr_template_ #1 _ \l__tblr_template_name_tl _tl }
+      \l__tblr_template_code_tl
+  }
+\cs_generate_variant:Nn \__tblr_def_template:n { V }
+
+\keys_define:nn { tblr-set-template }
+  {
+    unknown .code:n = \__tblr_set_template:V \l_keys_key_str,
+  }
+
+%% #1: head/foot element; #2: template name
+\NewDocumentCommand \SetTblrTemplate { m m }
+  {
+    \tl_set:Nn \l__tblr_template_name_tl {#2}
+    \keys_set:nn { tblr-set-template } {#1}
+    \ignorespaces
+  }
+
+\cs_new_protected:Npn \__tblr_set_template:n #1
+  {
+    \tl_set_eq:cc { l__tblr_template_ #1 _default_tl }
+      { l__tblr_template_ #1 _ \l__tblr_template_name_tl _tl }
+  }
+\cs_generate_variant:Nn \__tblr_set_template:n { V }
+
+\NewExpandableDocumentCommand \GetTblrStyle { m m }
+  {
+    \__tblr_style_item:n { #1 / #2 }
+  }
+
+\NewDocumentCommand \UseTblrFont { m }
+  {
+    \GetTblrStyle {#1} { font } \selectfont
+  }
+
+\tl_new:N \l__tblr_use_color_tl
+
+\NewDocumentCommand \UseTblrColor { m }
+  {
+    \tl_set:Nx \l__tblr_use_color_tl { \GetTblrStyle {#1} { fg } }
+    \tl_if_empty:NF \l__tblr_use_color_tl { \color { \l__tblr_use_color_tl } }
+  }
+
+%% All halign commands are defined at the beginning of the file
+\NewDocumentCommand \UseTblrAlign { m }
+  {
+    \use:c { __tblr_halign_command_ \GetTblrStyle {#1} { halign } : }
+  }
+
+\tl_new:N \l__tblr_use_hang_tl
+
+\NewDocumentCommand \UseTblrHang { m }
+  {
+    \tl_set:Nx \l__tblr_use_hang_tl { \GetTblrStyle {#1} { hang } }
+    \tl_if_empty:NF \l__tblr_use_hang_tl
+      {
+        \tl_put_left:Nn \l__tblr_use_hang_tl
+          { \hangafter = 1 \relax \hangindent = }
+        \tl_put_right:Nn \l__tblr_use_hang_tl { \relax }
+        \exp_args:NV \everypar \l__tblr_use_hang_tl
+      }
+  }
+
+\tl_new:N \l__tblr_use_indent_tl
+
+\NewDocumentCommand \UseTblrIndent { m }
+  {
+    \tl_set:Nx \l__tblr_use_indent_tl { \GetTblrStyle {#1} { indent } }
+    \tl_if_empty:NF \l__tblr_use_indent_tl
+      { \exp_args:NNV \setlength \parindent \l__tblr_use_indent_tl }
+  }
+
+\AtBeginDocument
+  {
+    \@ifpackageloaded{xcolor}{}{\RenewDocumentCommand \UseTblrColor {m} {}}
+  }
+
+%% #1: head/foot element; #2: template name
+\NewExpandableDocumentCommand \ExpTblrTemplate { m m }
+  {
+    \tl_use:c { l__tblr_template_ #1 _ #2 _tl }
+  }
+
+%% #1: head/foot element; #2: template name
+\NewDocumentCommand \UseTblrTemplate { m m }
+  {
+    \group_begin:
+    \UseTblrFont {#1}
+    \UseTblrColor {#1}
+    \tl_use:c { l__tblr_template_ #1 _ #2 _tl }
+    \group_end:
+  }
+
+\NewDocumentCommand \MapTblrNotes { +m }
+  {
+    \__tblr_prop_map_inline:nn { note }
+      {
+        \tl_set_rescan:Nnn \InsertTblrNoteTag {} {##1}
+        \tl_set:Nn \InsertTblrNoteText {##2}
+        #1
+      }
+  }
+
+\NewDocumentCommand \MapTblrRemarks { +m }
+  {
+    \__tblr_prop_map_inline:nn { remark }
+      {
+        \tl_set_rescan:Nnn \InsertTblrRemarkTag {} {##1}
+        \tl_set:Nn \InsertTblrRemarkText {##2}
+        #1
+      }
+  }
+
+\NewExpandableDocumentCommand \InsertTblrText { m }
+  {
+    \__tblr_spec_item:nn { outer } {#1}
+  }
+
+\NewExpandableDocumentCommand \InsertTblrMore { m }
+  {
+    \__tblr_prop_item:nn { more } {#1}
+  }
+
+%%% --------------------------------------------------------
+%%> \section{Table Continuation Templates}
+%%% --------------------------------------------------------
+
+\tl_if_exist:NF \tblrcontfootname
+  {
+    \tl_set:Nn \tblrcontfootname { Continued ~ on ~ next ~ page }
+  }
+
+\tl_if_exist:NF \tblrcontheadname
+  {
+    \tl_set:Nn \tblrcontheadname { ( Continued ) }
+  }
+
+\DefTblrTemplate { contfoot-text } { normal } { \tblrcontfootname }
+\SetTblrTemplate { contfoot-text } { normal }
+
+\DefTblrTemplate { contfoot } { empty } { }
+\DefTblrTemplate { contfoot } { plain }
+  {
+    \noindent
+    \raggedleft
+    \UseTblrTemplate { contfoot-text } { default }
+    \par
+  }
+\DefTblrTemplate { contfoot } { normal }
+  {
+    %% need to set parindent after alignment
+    \raggedleft
+    \UseTblrAlign { contfoot }
+    \UseTblrIndent { contfoot }
+    \UseTblrHang { contfoot }
+    \leavevmode
+    \UseTblrTemplate { contfoot-text } { default }
+    \par
+  }
+\SetTblrTemplate { contfoot } { normal }
+
+\DefTblrTemplate { conthead-pre } { empty } { }
+\DefTblrTemplate { conthead-pre } { normal } { \space }
+\SetTblrTemplate { conthead-pre } { normal }
+
+\DefTblrTemplate { conthead-text } { normal } { \tblrcontheadname }
+\SetTblrTemplate { conthead-text } { normal }
+
+\DefTblrTemplate { conthead } { empty } { }
+\DefTblrTemplate { conthead } { plain }
+  {
+    \noindent
+    \raggedright
+    \UseTblrTemplate { conthead-text } { default }
+    \par
+  }
+\DefTblrTemplate { conthead } { normal }
+  {
+    %% need to set parindent after alignment
+    \raggedright
+    \UseTblrAlign { conthead }
+    \UseTblrIndent { conthead }
+    \UseTblrHang { conthead }
+    \leavevmode
+    \UseTblrTemplate { conthead-text } { default }
+    \par
+  }
+\SetTblrTemplate { conthead } { normal }
+
+%%% --------------------------------------------------------
+%%> \section{Table Caption Templates}
+%%% --------------------------------------------------------
+
+\tl_new:N \l__tblr_caption_short_tl
+
+\DefTblrTemplate { caption-lot } { empty } { }
+\DefTblrTemplate { caption-lot } { normal }
+  {
+    \tl_if_empty:NTF \lTblrEntryTl
+      { \tl_set_eq:NN \l__tblr_caption_short_tl \lTblrCaptionTl }
+      { \tl_set_eq:NN \l__tblr_caption_short_tl \lTblrEntryTl }
+    \addcontentsline { lot } { table }
+      { \protect\numberline { \thetable } { \l__tblr_caption_short_tl } }
+  }
+\SetTblrTemplate { caption-lot } { normal }
+
+%% We need to use \hspace and \enskip, but not ~ or \space,
+%% since we want a correct hangindent caption paragraph.
+
+\DefTblrTemplate { caption-tag } { empty } { }
+\DefTblrTemplate { caption-tag } { normal } { \tablename\hspace{0.25em}\thetable }
+\SetTblrTemplate { caption-tag } { normal }
+
+\DefTblrTemplate { caption-sep } { empty } { }
+\DefTblrTemplate { caption-sep } { normal } { : \enskip }
+\SetTblrTemplate { caption-sep } { normal }
+
+\DefTblrTemplate { caption-text } { empty } { }
+\DefTblrTemplate { caption-text } { normal } { \InsertTblrText { caption } }
+\SetTblrTemplate { caption-text } { normal }
+
+\box_new:N \l__tblr_caption_box
+\box_new:N \l__tblr_caption_left_box
+
+\DefTblrTemplate { caption } { empty } { }
+\DefTblrTemplate { caption } { plain }
+  {
+    \hbox_set:Nn \l__tblr_caption_box
+      {
+        \UseTblrTemplate { caption-tag } { default }
+        \UseTblrTemplate { caption-sep } { default }
+        \UseTblrTemplate { caption-text } { default }
+      }
+    \dim_compare:nNnTF { \box_wd:N \l__tblr_caption_box } > { \hsize }
+      {
+        \noindent
+        \hbox_unpack:N \l__tblr_caption_box
+        \par
+      }
+      {
+        \centering
+        \makebox [\hsize] [c] { \box_use:N \l__tblr_caption_box }
+        \par
+      }
+  }
+\DefTblrTemplate { caption } { normal }
+  {
+    \hbox_set:Nn \l__tblr_caption_box
+      {
+        \UseTblrTemplate { caption-tag } { default }
+        \UseTblrTemplate { caption-sep } { default }
+        \UseTblrTemplate { caption-text } { default }
+      }
+    \dim_compare:nNnTF { \box_wd:N \l__tblr_caption_box } > { \hsize }
+      {
+        \UseTblrAlign { caption }
+        \UseTblrIndent { caption }
+        \hbox_set:Nn \l__tblr_caption_left_box
+          {
+            \UseTblrTemplate { caption-tag } { default }
+            \UseTblrTemplate { caption-sep } { default }
+          }
+        \hangindent = \box_wd:N \l__tblr_caption_left_box
+        \hangafter = 1
+        \UseTblrHang { caption }
+        \leavevmode
+        \hbox_unpack:N \l__tblr_caption_box
+        \par
+      }
+      {
+        \centering
+        \makebox [\hsize] [c] { \box_use:N \l__tblr_caption_box }
+        \par
+      }
+  }
+\DefTblrTemplate { caption } { simple }
+  {
+    \UseTblrAlign { caption }
+    \UseTblrIndent { caption }
+    \UseTblrHang { caption }
+    \leavevmode
+    \UseTblrTemplate { caption-tag } { default }
+    \UseTblrTemplate { caption-sep } { default }
+    \UseTblrTemplate { caption-text } { default }
+    \par
+  }
+\SetTblrTemplate { caption } { normal }
+
+\DefTblrTemplate { capcont } { empty } { }
+\DefTblrTemplate { capcont } { plain }
+  {
+    \hbox_set:Nn \l__tblr_caption_box
+      {
+        \UseTblrTemplate { caption-tag } { default }
+        \UseTblrTemplate { caption-sep } { default }
+        \UseTblrTemplate { caption-text } { default }
+        \UseTblrTemplate { conthead-pre } { default }
+        \UseTblrTemplate { conthead-text } { default }
+      }
+    \dim_compare:nNnTF { \box_wd:N \l__tblr_caption_box } > { \hsize }
+      {
+        \noindent
+        \hbox_unpack:N \l__tblr_caption_box
+        \par
+      }
+      {
+        \centering
+        \makebox [\hsize] [c] { \box_use:N \l__tblr_caption_box }
+        \par
+      }
+  }
+\DefTblrTemplate { capcont } { normal }
+  {
+    \hbox_set:Nn \l__tblr_caption_box
+      {
+        \UseTblrTemplate { caption-tag } { default }
+        \UseTblrTemplate { caption-sep } { default }
+        \UseTblrTemplate { caption-text } { default }
+        \UseTblrTemplate { conthead-pre } { default }
+        \UseTblrTemplate { conthead-text } { default }
+      }
+    \dim_compare:nNnTF { \box_wd:N \l__tblr_caption_box } > { \hsize }
+      {
+        \UseTblrAlign { capcont }
+        \UseTblrIndent { capcont }
+        \hbox_set:Nn \l__tblr_caption_left_box
+          {
+            \UseTblrTemplate { caption-tag } { default }
+            \UseTblrTemplate { caption-sep } { default }
+          }
+        \hangindent = \box_wd:N \l__tblr_caption_left_box
+        \hangafter = 1
+        \UseTblrHang { capcont }
+        \leavevmode
+        \hbox_unpack:N \l__tblr_caption_box
+        \par
+      }
+      {
+        \centering
+        \makebox [\hsize] [c] { \box_use:N \l__tblr_caption_box }
+        \par
+      }
+  }
+\DefTblrTemplate { capcont } { simple }
+  {
+    \UseTblrAlign { caption }
+    \UseTblrIndent { caption }
+    \UseTblrHang { caption }
+    \leavevmode
+    \UseTblrTemplate { caption-tag } { default }
+    \UseTblrTemplate { caption-sep } { default }
+    \UseTblrTemplate { caption-text } { default }
+    \UseTblrTemplate { conthead-pre } { default }
+    \UseTblrTemplate { conthead-text } { default }
+    \par
+  }
+\SetTblrTemplate { capcont} { normal }
+
+%%% --------------------------------------------------------
+%%> \section{Table Notes Templates}
+%%% --------------------------------------------------------
+
+%% By default the targets generated by \hypertarget are too low
+%% Therefore we need to use \Hy at raisedlink command to fix this problem
+%% See https://tex.stackexchange.com/questions/17057
+%% We also use \use:c in case the private command \Hy at raisedlink is removed
+\cs_new_protected:Npn \__tblr_hyper_target:n #1
+  {
+    \cs_if_exist:NT \hypertarget
+      {
+        \use:c { Hy at raisedlink }
+          {
+            \hypertarget
+              { tblr / \int_use:N \g__tblr_table_count_int / \tl_to_str:n {#1} }
+              { }
+          }
+      }
+  }
+\cs_generate_variant:Nn \__tblr_hyper_target:n { V }
+
+\cs_new_protected:Npn \__tblr_hyper_link:nn #1 #2
+  {
+    \cs_if_exist:NTF \hyperlink
+      {
+        \hyperlink
+          { tblr / \int_use:N \g__tblr_table_count_int / \tl_to_str:n {#1} }
+          { #2 }
+      }
+      { #2 }
+  }
+
+\DefTblrTemplate { note-border } { empty }
+  {
+    \hypersetup { pdfborder = { 0 ~ 0 ~ 0 } }
+  }
+\DefTblrTemplate { note-border } { normal }
+  {
+    \hypersetup { pdfborder = { 0 ~ 0 ~ 1 } }
+  }
+\SetTblrTemplate { note-border } { empty }
+
+\cs_set_eq:NN \TblrOverlap \rlap
+
+\NewDocumentCommand \TblrNote { m }
+  {
+    \cs_if_exist:NT \hypersetup { \ExpTblrTemplate { note-border }{ default } }
+    \TblrOverlap
+      {
+        \__tblr_hyper_link:nn {#1}
+          { \textsuperscript { \sffamily \UseTblrFont { note-tag } #1 } }
+      }
+  }
+
+\DefTblrTemplate { note-tag } { empty } { }
+\DefTblrTemplate { note-tag } { normal }
+  {
+    \textsuperscript { \sffamily \UseTblrFont { note-tag } \InsertTblrNoteTag }
+  }
+\SetTblrTemplate { note-tag } { normal }
+
+\DefTblrTemplate { note-target } { normal }
+  {
+    \__tblr_hyper_target:V \InsertTblrNoteTag
+  }
+\SetTblrTemplate { note-target } { normal }
+
+\DefTblrTemplate { note-sep } { empty } { }
+\DefTblrTemplate { note-sep } { normal } { \space }
+\SetTblrTemplate { note-sep } { normal }
+
+\DefTblrTemplate { note-text } { empty } { }
+\DefTblrTemplate { note-text } { normal } { \InsertTblrNoteText }
+\SetTblrTemplate { note-text } { normal }
+
+\DefTblrTemplate { note } { empty } { }
+\DefTblrTemplate { note } { plain }
+  {
+    \MapTblrNotes
+      {
+        \noindent
+        \UseTblrTemplate { note-tag } { default }
+        \UseTblrTemplate { note-target } { default }
+        \UseTblrTemplate { note-sep } { default }
+        \UseTblrTemplate { note-text } { default }
+        \par
+      }
+  }
+\DefTblrTemplate { note } { normal }
+  {
+    \UseTblrAlign { note }
+    \UseTblrIndent { note }
+    \MapTblrNotes
+      {
+        \hangindent = 0.7em
+        \hangafter = 1
+        \UseTblrHang { note }
+        \leavevmode
+        \hbox_to_wd:nn { \the\hangindent }
+          {
+            \UseTblrTemplate { note-tag } { default }
+            \UseTblrTemplate { note-target } { default }
+            \hfil
+          }
+        \UseTblrTemplate { note-text } { default }
+        \par
+      }
+  }
+\DefTblrTemplate { note } { inline }
+  {
+    \UseTblrAlign { note }
+    \UseTblrIndent { note }
+    \UseTblrHang { note }
+    \leavevmode
+    \MapTblrNotes
+      {
+        \UseTblrTemplate { note-tag } { default }
+        \UseTblrTemplate { note-target } { default }
+        \UseTblrTemplate { note-sep } { default }
+        \UseTblrTemplate { note-text } { default }
+        \quad
+      }
+    \par
+  }
+\SetTblrTemplate { note } { normal }
+
+%%% --------------------------------------------------------
+%%> \section{Table Remarks Templates}
+%%% --------------------------------------------------------
+
+\DefTblrTemplate { remark-tag } { empty } { }
+\DefTblrTemplate { remark-tag } { normal }
+  {
+    \itshape \UseTblrFont { remark-tag } \InsertTblrRemarkTag
+  }
+\SetTblrTemplate { remark-tag } { normal }
+
+\DefTblrTemplate { remark-sep } { empty } { }
+\DefTblrTemplate { remark-sep } { normal } { : \space }
+\SetTblrTemplate { remark-sep } { normal }
+
+\DefTblrTemplate { remark-text } { empty } { }
+\DefTblrTemplate { remark-text } { normal } { \InsertTblrRemarkText }
+\SetTblrTemplate { remark-text } { normal }
+
+\DefTblrTemplate { remark } { empty } { }
+\DefTblrTemplate { remark } { plain }
+  {
+    \MapTblrRemarks
+      {
+        \noindent
+        \UseTblrTemplate { remark-tag } { default }
+        \UseTblrTemplate { remark-sep } { default }
+        \UseTblrTemplate { remark-text } { default }
+        \par
+      }
+  }
+\DefTblrTemplate { remark } { normal }
+  {
+    \UseTblrAlign { remark }
+    \UseTblrIndent { remark }
+    \MapTblrRemarks
+      {
+        \hangindent = 0.7em
+        \hangafter = 1
+        \UseTblrHang { remark }
+        \leavevmode
+        \UseTblrTemplate { remark-tag } { default }
+        \UseTblrTemplate { remark-sep } { default }
+        \UseTblrTemplate { remark-text } { default }
+        \par
+      }
+  }
+\DefTblrTemplate { remark } { inline }
+  {
+    \UseTblrAlign { remark }
+    \UseTblrIndent { remark }
+    \UseTblrHang { remark }
+    \leavevmode
+    \MapTblrRemarks
+      {
+        \UseTblrTemplate { remark-tag } { default }
+        \UseTblrTemplate { remark-sep } { default }
+        \UseTblrTemplate { remark-text } { default }
+        \quad
+      }
+    \par
+  }
+\SetTblrTemplate { remark } { normal }
+
+%%% --------------------------------------------------------
+%%> \section{Header and Footer Templates}
+%%% --------------------------------------------------------
+
+\tl_new:N \g__tblr_template_firsthead_default_tl
+\tl_new:N \g__tblr_template_middlehead_default_tl
+\tl_new:N \g__tblr_template_lasthead_default_tl
+\tl_new:N \g__tblr_template_firstfoot_default_tl
+\tl_new:N \g__tblr_template_middlefoot_default_tl
+\tl_new:N \g__tblr_template_lastfoot_default_tl
+
+\keys_define:nn { tblr-def-template }
+  {
+    head .meta:n = { firsthead, middlehead, lasthead },
+    foot .meta:n = { firstfoot, middlefoot, lastfoot },
+  }
+
+\keys_define:nn { tblr-set-template }
+  {
+    head .meta:n = { firsthead, middlehead, lasthead },
+    foot .meta:n = { firstfoot, middlefoot, lastfoot },
+  }
+
+\DefTblrTemplate { head } { empty } { }
+\DefTblrTemplate { foot } { empty } { }
+
+\DefTblrTemplate { firsthead } { normal }
+  {
+    \UseTblrTemplate { caption } { default }
+  }
+
+\DefTblrTemplate { middlehead, lasthead } { normal }
+  {
+    \UseTblrTemplate { capcont } { default }
+  }
+
+\DefTblrTemplate { firstfoot, middlefoot } { normal }
+  {
+    \UseTblrTemplate { contfoot } { default }
+  }
+
+\DefTblrTemplate { lastfoot } { normal }
+  {
+    \UseTblrTemplate { note } { default }
+    \UseTblrTemplate { remark } { default }
+  }
+
+\SetTblrTemplate { head } { normal }
+\SetTblrTemplate { foot } { normal }
+
+%%% --------------------------------------------------------
+%%> \section{Build the Whole Table}
+%%% --------------------------------------------------------
+
+\cs_new:Npn \__tblr_box_height:N #1
+  {
+    \dim_eval:n { \box_ht:N #1 + \box_dp:N #1 }
+  }
+
+\cs_new_protected:Npn \__tblr_build_head_foot:
+  {
+    \__tblr_build_row_head_foot:
+    \__tblr_build_table_head_foot:
+  }
+
+\tl_new:N \l__tblr_row_head_tl
+\tl_new:N \l__tblr_row_foot_tl
+\box_new:N \l__tblr_row_head_box
+\box_new:N \l__tblr_row_foot_box
+\dim_new:N \l__tblr_row_head_foot_dim
+
+\cs_new_protected:Npn \__tblr_build_row_head_foot:
+  {
+    %% \l__tblr_row_head_tl and \l__tblr_row_foot_tl may be empty
+    \tl_set:Nx \l__tblr_row_head_tl { \__tblr_prop_item:ne { inner } { rowhead } }
+    \int_compare:nNnTF { \l__tblr_row_head_tl + 0 } > { 0 }
+      {
+        \__tblr_build_one_table:nnNN {1} { \l__tblr_row_head_tl }
+          \c_true_bool \c_true_bool
+      }
+      { \__tblr_build_one_hline:n {1} }
+    \box_set_eq:NN \l__tblr_row_head_box \l__tblr_table_box
+    \tl_set:Nx \l__tblr_row_foot_tl { \__tblr_prop_item:ne { inner } { rowfoot } }
+    \int_compare:nNnTF { \l__tblr_row_foot_tl + 0 } > { 0 }
+      {
+        \__tblr_build_one_table:nnNN
+          { \c at rowcount - \l__tblr_row_foot_tl + 1 } { \c at rowcount }
+          \c_true_bool \c_true_bool
+      }
+      { \__tblr_build_one_hline:n { \int_eval:n { \c at rowcount + 1 } } }
+    \box_set_eq:NN \l__tblr_row_foot_box \l__tblr_table_box
+    \dim_set:Nn \l__tblr_row_head_foot_dim
+      {
+        \__tblr_box_height:N \l__tblr_row_head_box
+          + \__tblr_box_height:N \l__tblr_row_foot_box
+      }
+  }
+
+\dim_new:N \tablewidth
+
+\cs_new_protected:Npn \__tblr_get_table_width:
+  {
+    \dim_zero:N \tablewidth
+    \int_step_inline:nn { \c at colcount }
+      {
+        \dim_add:Nn \tablewidth
+          {
+            \__tblr_spec_item:nn { vline } { [##1] / @vline-width }
+            +
+            \__tblr_data_item:nnn { column } {##1} { leftsep }
+            +
+            \__tblr_data_item:nnn { column } {##1} { @col-width }
+            +
+            \__tblr_data_item:nnn { column } {##1} { rightsep }
+          }
+      }
+    \dim_add:Nn \tablewidth
+      {
+        \__tblr_spec_item:ne { vline }
+          { [\int_eval:n { \c at colcount + 1 }] / @vline-width }
+      }
+  }
+
+\box_new:N \l__tblr_table_firsthead_box
+\box_new:N \l__tblr_table_middlehead_box
+\box_new:N \l__tblr_table_lasthead_box
+\box_new:N \l__tblr_table_firstfoot_box
+\box_new:N \l__tblr_table_middlefoot_box
+\box_new:N \l__tblr_table_lastfoot_box
+
+\cs_new_protected:Npn \__tblr_build_table_head_foot:
+  {
+    \__tblr_get_table_width:
+    % make each of \lTblrCaptionTl, \lTblrEntryTl, \lTblrLabelTl and the
+    % three corresponding booleans available in all head-foot templates
+    \__tblr_set_table_label_entry:
+    \__tblr_build_table_head_aux:Nn \l__tblr_table_firsthead_box
+      {
+        \__tblr_build_table_label_entry:
+        \UseTblrTemplate { firsthead } { default }
+      }
+    \__tblr_build_table_head_aux:Nn \l__tblr_table_middlehead_box
+      {
+        \UseTblrTemplate { middlehead } { default }
+      }
+    \__tblr_build_table_head_aux:Nn \l__tblr_table_lasthead_box
+      {
+        \UseTblrTemplate { lasthead } { default }
+      }
+    \__tblr_build_table_foot_aux:Nn \l__tblr_table_firstfoot_box
+      {
+        \UseTblrTemplate { firstfoot } { default }
+      }
+    \__tblr_build_table_foot_aux:Nn \l__tblr_table_middlefoot_box
+      {
+        \UseTblrTemplate { middlefoot } { default }
+      }
+    \__tblr_build_table_foot_aux:Nn \l__tblr_table_lastfoot_box
+      {
+        \UseTblrTemplate { lastfoot } { default }
+      }
+  }
+
+\bool_new:N \l__tblr_table_no_title_bool
+\bool_new:N \l__tblr_table_no_entry_bool
+\bool_new:N \l__tblr_table_no_label_bool
+\tl_const:Nn \c_tblr_none_tl { none }
+
+\cs_new_protected:Npn \__tblr_set_table_label_entry:
+  {
+    \tl_set:Nx \lTblrCaptionTl { \InsertTblrText { caption } }
+    \tl_set:Nx \lTblrEntryTl { \InsertTblrText { entry } }
+    \tl_set:Nx \lTblrLabelTl { \InsertTblrText { label } }
+    \bool_set:Nn \l__tblr_table_no_title_bool
+      { \tl_if_empty_p:N \lTblrCaptionTl }
+    \bool_set:Nn \l__tblr_table_no_entry_bool
+      { \tl_if_eq_p:NN \lTblrEntryTl \c_tblr_none_tl }
+    \bool_set:Nn \l__tblr_table_no_label_bool
+      { \tl_if_eq_p:NN \lTblrLabelTl \c_tblr_none_tl }
+    \bool_if:NT \l__tblr_table_no_title_bool
+      {
+        \SetTblrTemplate { conthead-pre } { empty }
+      }
+    \bool_if:NT \l__tblr_table_no_label_bool
+      {
+        \SetTblrTemplate { caption-tag }{ empty }
+        \SetTblrTemplate { caption-sep }{ empty }
+      }
+  }
+
+\cs_new_protected:Npn \__tblr_build_tall_table_head_foot:
+  {
+    \__tblr_get_table_width:
+    \__tblr_set_table_label_entry:
+    \__tblr_build_table_head_aux:Nn \l__tblr_table_firsthead_box
+      {
+        \__tblr_build_table_label_entry:
+        \UseTblrTemplate { firsthead } { default }
+      }
+    \__tblr_build_table_foot_aux:Nn
+      \l__tblr_table_lastfoot_box  { \UseTblrTemplate { lastfoot } { default } }
+  }
+
+\tl_new:N \lTblrCaptionTl
+\tl_new:N \lTblrEntryTl
+\tl_new:N \lTblrLabelTl
+\clist_new:N \lTblrRefMoreClist
+
+\cs_new_protected:Npn \__tblr_build_table_label_entry:
+  {
+    \bool_if:NF \l__tblr_table_no_label_bool
+      {
+        \refstepcounter { table }
+        \tl_if_empty:NF \lTblrLabelTl
+          {
+            \clist_map_inline:Nn \lTblrRefMoreClist
+              { \ExpTblrTemplate { caption-ref } { ##1 } }
+            \exp_args:NV \label \lTblrLabelTl
+          }
+      }
+    %% We put caption-lot code at last, so that a user can modify \lTblrEntryTl
+    %% in a caption-label template. For example, a user may want to use
+    %% short caption in nameref, but at the same time not to add LoT entry.
+    \bool_if:NF \l__tblr_table_no_entry_bool
+      { \UseTblrTemplate { caption-lot } { default } }
+  }
+
+\cs_new_protected:Npn \__tblr_build_table_head_aux:Nn #1 #2
+  {
+    \vbox_set:Nn #1
+      {
+        \hsize = \tablewidth
+        \TblrParboxRestore % it will set \linewidth = \hsize
+        \vbox_set:Nn \l_tmpa_box {#2}
+        \box_use:N \l_tmpa_box
+        \dim_compare:nNnT
+          { \box_ht:N \l_tmpa_box + \box_dp:N \l_tmpa_box } > { 0pt }
+          { \skip_vertical:n { \__tblr_spec_item:nn { outer } { headsep } } }
+      }
+  }
+
+\cs_new_protected:Npn \__tblr_build_table_foot_aux:Nn #1 #2
+  {
+    \vbox_set:Nn #1
+      {
+        \hsize = \tablewidth
+        \TblrParboxRestore % it will set \linewidth = \hsize
+        \vbox_set:Nn \l_tmpb_box {#2}
+        \dim_compare:nNnT
+          { \box_ht:N \l_tmpb_box + \box_dp:N \l_tmpb_box } > { 0pt }
+          { \skip_vertical:n { \__tblr_spec_item:nn { outer } { footsep } } }
+        \box_use:N \l_tmpb_box
+      }
+  }
+
+\cs_new_protected:Npn \__tblr_build_whole:
+  {
+    \__tblr_hook_use:n { tabularray/table/before }
+    \tl_if_eq:enTF { \__tblr_spec_item:nn { outer } { long } } { true }
+      { \__tblr_build_long_table:e { \__tblr_spec_item:nn { outer } { halign } } }
+      {
+        \tl_if_eq:enTF { \__tblr_spec_item:nn { outer } { tall } } { true }
+          {
+            \__tblr_build_tall_table:e
+              { \__tblr_spec_item:nn { outer } { baseline } }
+          }
+          {
+            \__tblr_build_short_table:e
+              { \__tblr_spec_item:nn { outer } { baseline } }
+          }
+      }
+    \__tblr_hook_use:n { tabularray/table/after }
+  }
+
+\dim_new:N \l__tblr_remain_height_dim
+\int_new:N \l__tblr_long_from_int
+\int_new:N \l__tblr_long_to_int
+\int_new:N \l__tblr_curr_i_int
+\int_new:N \l__tblr_prev_i_int
+\int_new:N \l__tblr_table_page_int
+\bool_new:N \l__tblr_page_break_curr_bool
+\bool_new:N \l__tblr_page_break_prev_bool
+
+%% #1: table alignment
+%% For long table, we need to leave hmode first to get correct \pagetotal
+%% Also remove topskip and presep if we are at the beginning of the page
+\cs_new_protected:Npn \__tblr_build_long_table:n #1
+  {
+    \LogTblrTracing { page }
+    \par
+    \skip_zero:N \parskip % see issue #203
+    \LogTblrTracing { page }
+    \dim_compare:nNnTF { \pagegoal } = { \maxdimen }
+      { \hbox{}\kern-\topskip\nobreak }
+      { \skip_vertical:n { \__tblr_spec_item:nn { outer } { presep } } }
+    \LogTblrTracing { page }
+    \nointerlineskip
+    \mode_leave_vertical: % enter horizontal mode to update \pagetotal
+    \LogTblrTracing { page }
+    \hrule height ~ 0pt
+    \nobreak % prevent page break after \hrule (see issue #42)
+    \LogTblrTracing { page }
+    \int_set:Nn \l__tblr_table_page_int {1}
+    \__tblr_build_head_foot:
+    \dim_set:Nn \l__tblr_remain_height_dim
+      { \pagegoal - \pagetotal - \l__tblr_row_head_foot_dim }
+    \int_set:Nn \l__tblr_long_from_int { \l__tblr_row_head_tl + 1 }
+    \int_set:Nn \l__tblr_long_to_int { \c at rowcount - ( \l__tblr_row_foot_tl + 0 ) }
+    \int_set:Nn \l__tblr_curr_i_int { \l__tblr_long_from_int - 1 }
+    \int_do_while:nNnn { \l__tblr_curr_i_int } < { \l__tblr_long_to_int }
+      {
+        \int_set_eq:NN \l__tblr_prev_i_int \l__tblr_curr_i_int
+        \__tblr_get_next_table_rows:NNNN
+          \l__tblr_long_to_int \l__tblr_curr_i_int
+          \l_tmpa_dim \l__tblr_page_break_curr_bool
+        \__tblr_check_table_page_break:NNN
+          \l__tblr_remain_height_dim \l_tmpa_dim \l__tblr_page_break_prev_bool
+        \__tblr_do_if_tracing:nn { page } { \int_log:N \l__tblr_curr_i_int }
+        \bool_if:NTF \l__tblr_page_break_prev_bool
+          {
+            \int_compare:nNnTF
+              { \l__tblr_long_from_int } > { \l__tblr_prev_i_int }
+              {
+                % See issue #42: if longtblr starts at the bottom of a page,
+                % \pagetotal maybe exceed \pagegoal after adding presep,
+                % or after adding rowhead or rowfoot of the table.
+                % In these cases, we will not typeset table in this page,
+                % but rather force a page break.
+                \group_begin:
+                  \dim_set:Nn \l_tmpb_dim
+                    {
+                      % Enough to overfill the page (including shrink).
+                      \pagegoal - \pagetotal + \l_tmpa_dim
+                      + \__tblr_box_height:N \l__tblr_table_firsthead_box
+                      + \__tblr_box_height:N \l__tblr_table_firstfoot_box
+                    }
+                  \skip_vertical:n { \l_tmpb_dim }
+                  \tex_penalty:D 9999
+                  \skip_vertical:n { -\l_tmpb_dim }
+                \group_end:
+              }
+              {
+                \__tblr_build_page_table:nnx {#1}
+                  { \int_use:N \l__tblr_long_from_int }
+                  { \int_use:N \l__tblr_prev_i_int }
+                \int_incr:N \l__tblr_table_page_int
+                \int_set:Nn \l__tblr_long_from_int { \l__tblr_prev_i_int + 1 }
+                \TblrNewPage
+              }
+            \hbox{}\kern-\topskip\nobreak
+            \noindent
+            \LogTblrTracing { page }
+            \dim_set:Nn \l__tblr_remain_height_dim
+              { \pagegoal - \pagetotal - \l__tblr_row_head_foot_dim - \l_tmpa_dim }
+          }
+          {
+            \bool_if:NTF \l__tblr_page_break_curr_bool
+              {
+                \__tblr_build_page_table:nnx {#1}
+                  { \int_use:N \l__tblr_long_from_int }
+                  { \int_use:N \l__tblr_curr_i_int }
+                \int_incr:N \l__tblr_table_page_int
+                \TblrNewPage
+                \hbox{}\kern-\topskip\nobreak
+                \noindent
+                \LogTblrTracing { page }
+                \dim_set:Nn \l__tblr_remain_height_dim
+                  { \pagegoal - \pagetotal - \l__tblr_row_head_foot_dim }
+                \int_set:Nn \l__tblr_long_from_int { \l__tblr_curr_i_int + 1 }
+              }
+              { \dim_add:Nn \l__tblr_remain_height_dim { -\l_tmpa_dim } }
+          }
+      }
+    \int_compare:nNnTF { \l__tblr_table_page_int } = {1}
+      {
+        \box_set_eq:NN \l__tblr_table_head_box \l__tblr_table_firsthead_box
+        \box_set_eq:NN \l__tblr_table_foot_box \l__tblr_table_lastfoot_box
+      }
+      {
+        \box_set_eq:NN \l__tblr_table_head_box \l__tblr_table_lasthead_box
+        \box_set_eq:NN \l__tblr_table_foot_box \l__tblr_table_lastfoot_box
+      }
+    \__tblr_build_page_table:nnn {#1}
+      { \int_use:N \l__tblr_long_from_int } { \int_use:N \l__tblr_long_to_int }
+    \skip_vertical:n { \__tblr_spec_item:nn { outer } { postsep } }
+    % In the past we used "\hrule height ~ 0pt" to get strict postsep,
+    % but the postsep was not discarded when page breaks, see issue #39.
+    % Therefore we use \nointerlineskip here.
+    \nointerlineskip
+  }
+\cs_generate_variant:Nn \__tblr_build_long_table:n { e }
+
+%% #1: int with index of the last row; #2: int with index of current row;
+%% #3: row dimension; #4: break page or not.
+\cs_new_protected:Npn \__tblr_get_next_table_rows:NNNN #1 #2 #3 #4
+  {
+    \bool_set_true:N \l_tmpa_bool
+    \dim_zero:N #3
+    \bool_set_false:N #4
+    \bool_while_do:Nn \l_tmpa_bool
+      {
+        \int_incr:N #2
+        \dim_add:Nn #3
+          {
+            \__tblr_data_item:nen { row } { \int_use:N #2 } { abovesep }
+            +
+            \__tblr_data_item:nen { row } { \int_use:N #2 } { @row-height }
+            +
+            \__tblr_data_item:nen { row } { \int_use:N #2 } { belowsep }
+            +
+            \__tblr_spec_item:ne { hline }
+              { [ \int_eval:n { #2 + 1 } ] / @hline-height }
+          }
+        \int_compare:nNnTF {#2} < {#1}
+          {
+            \tl_set:Nx \l__tblr_b_tl
+              {
+                \__tblr_spec_item:ne { hline }
+                  { [ \int_eval:n { #2 + 1 } ] / @pagebreak }
+              }
+            % Note that \l__tblr_b_tl may be empty
+            \int_compare:nNnTF { \l__tblr_b_tl + 0 } < { 0 }
+              { \bool_set_true:N \l_tmpa_bool }
+              {
+                \bool_set_false:N \l_tmpa_bool
+                \int_compare:nNnT { \l__tblr_b_tl + 0 } > { 0 }
+                  { \bool_set_true:N #4 }
+              }
+          }
+          { \bool_set_false:N \l_tmpa_bool }
+      }
+  }
+
+\box_new:N \l__tblr_table_head_box
+\box_new:N \l__tblr_table_foot_box
+\dim_new:N \l__tblr_table_head_foot_dim
+\dim_new:N \l__tblr_table_head_body_foot_dim
+
+%% #1: remain dimension; #2: row dimension; #3: break page or not
+\cs_new_protected:Npn \__tblr_check_table_page_break:NNN #1 #2 #3
+  {
+    \int_compare:nNnTF { \l__tblr_table_page_int } = {1}
+      {
+        \dim_set:Nn \l__tblr_table_head_body_foot_dim
+          {
+            \__tblr_box_height:N \l__tblr_table_firsthead_box
+              + #2 + \__tblr_box_height:N \l__tblr_table_firstfoot_box
+          }
+        \box_set_eq:NN \l__tblr_table_head_box \l__tblr_table_firsthead_box
+        \dim_compare:nNnTF
+          { \l__tblr_table_head_body_foot_dim } > {#1}
+          {
+            \bool_set_true:N #3
+            \box_set_eq:NN \l__tblr_table_foot_box \l__tblr_table_firstfoot_box
+          }
+          { \bool_set_false:N #3 }
+      }
+      {
+        \dim_set:Nn \l__tblr_table_head_body_foot_dim
+          {
+            \__tblr_box_height:N \l__tblr_table_middlehead_box
+              + #2 + \__tblr_box_height:N \l__tblr_table_middlefoot_box
+          }
+        \box_set_eq:NN \l__tblr_table_head_box \l__tblr_table_middlehead_box
+        \dim_compare:nNnTF
+          { \l__tblr_table_head_body_foot_dim } > {#1}
+          {
+            \bool_set_true:N #3
+            \box_set_eq:NN \l__tblr_table_foot_box \l__tblr_table_middlefoot_box
+          }
+          { \bool_set_false:N #3 }
+      }
+  }
+
+\box_new:N \l__tblr_table_box
+
+%% #1: table alignment; #2: row from; #3: row to
+\cs_new_protected:Npn \__tblr_build_page_table:nnn #1 #2 #3
+  {
+    \__tblr_build_one_table:nnNN {#2} {#3} \c_false_bool \c_false_bool
+    \vbox_set:Nn \l__tblr_table_box
+      {
+        \box_use:N \l__tblr_table_head_box
+        \__tblr_cover_two_vboxes:NN \l__tblr_row_head_box \l__tblr_table_box
+        \box_use:N \l__tblr_row_foot_box
+        \hrule height ~ 0pt
+        \box_use:N \l__tblr_table_foot_box
+      }
+    \__tblr_halign_whole:Nn \l__tblr_table_box {#1}
+  }
+\cs_generate_variant:Nn \__tblr_build_page_table:nnn { nnx }
+
+%% To solve the problem of missing hlines of long tables in some PDF readers,
+%% We need to draw body rows before head rows (see issue #88).
+\cs_new_protected:Npn \__tblr_cover_two_vboxes:NN #1 #2
+  {
+    \dim_set:Nn \l_tmpa_dim { \box_ht:N #1 + \box_dp:N #1 }
+    \dim_set:Nn \l_tmpb_dim { \box_ht:N #2 + \box_dp:N #2 }
+    \skip_vertical:N \l_tmpa_dim
+    \hrule height ~ 0pt
+    \box_use:N #2
+    \skip_vertical:n { - \l_tmpa_dim - \l_tmpb_dim }
+    \hrule height ~ 0pt
+    \box_use:N #1
+    \skip_vertical:N \l_tmpb_dim
+    \hrule height ~ 0pt
+  }
+
+\cs_new_protected:Npn \__tblr_halign_whole:Nn #1 #2
+  {
+    \noindent
+    \hbox_to_wd:nn { \linewidth }
+      {
+        \tl_if_eq:nnF {#2} {l} { \hfil }
+        \box_use:N #1
+        \tl_if_eq:nnF {#2} {r} { \hfil }
+      }
+  }
+
+%% #1: table alignment
+%% For tall table, we need to leave vmode first.
+%% Since there may be \centering in table environment,
+%% We use \raggedright to reset alignement for table head/foot.
+\cs_new_protected:Npn \__tblr_build_tall_table:n #1
+  {
+    \mode_leave_vertical:
+    \__tblr_build_tall_table_head_foot:
+    \__tblr_build_one_table:nnNN {1} {\c at rowcount} \c_true_bool \c_true_bool
+    \vbox_set:Nn \l__tblr_table_box
+      {
+        \box_use:N \l__tblr_table_firsthead_box
+        \hrule height ~ 0pt
+        \box_use:N \l__tblr_table_box
+        \hrule height ~ 0pt
+        \box_use:N \l__tblr_table_lastfoot_box
+      }
+    \__tblr_valign_whole:Nn \l__tblr_table_box {#1}
+  }
+\cs_generate_variant:Nn \__tblr_build_tall_table:n { e }
+
+%% #1: table alignment
+%% For short table, we need to leave vmode first
+\cs_new_protected:Npn \__tblr_build_short_table:n #1
+  {
+    \mode_leave_vertical:
+    \__tblr_build_one_table:nnNN {1} {\c at rowcount} \c_true_bool \c_true_bool
+    \__tblr_valign_whole:Nn \l__tblr_table_box {#1}
+  }
+\cs_generate_variant:Nn \__tblr_build_short_table:n { e }
+
+\box_new:N \l__tblr_table_hlines_box
+\box_new:N \l__tblr_hline_box
+\box_new:N \l__tblr_row_box
+
+%% #1: row from; #2: row to
+%% #3: whether build first hline or not; #4: whether build last hline or not
+%% To fix disappeared hlines with colorful tables in Adobe Reader (see #76),
+%% we collect all hlines and draw them at the end of the table.
+\cs_new_protected:Npn \__tblr_build_one_table:nnNN #1 #2 #3 #4
+  {
+    \box_clear:N \l__tblr_table_hlines_box
+    \tblr_vbox_set:Nn \l__tblr_table_box
+      {
+        \int_step_variable:nnNn {#1} {#2} \l__tblr_i_tl
+          {
+            \bool_lazy_or:nnT
+              { \int_compare_p:nNn { \l__tblr_i_tl } > {#1} }
+              { \bool_if_p:N #3 }
+              { \__tblr_put_one_hline:n { \__tblr_build_hline:V \l__tblr_i_tl } }
+            \tblr_hrule_ht:n { 0pt } % remove lineskip between hlines and rows
+            \__tblr_put_one_row:n { \__tblr_build_row:N \l__tblr_i_tl }
+            \tblr_hrule_ht:n { 0pt }
+          }
+        \bool_if:NT #4
+          {
+            \__tblr_put_one_hline:n
+              { \__tblr_build_hline:n { \int_eval:n {#2 + 1} } }
+          }
+        \skip_vertical:n
+          {
+            - \box_ht:N \l__tblr_table_hlines_box
+            - \box_dp:N \l__tblr_table_hlines_box
+          }
+        \tblr_box_use:N \l__tblr_table_hlines_box
+      }
+  }
+
+\cs_new_protected:Npn \__tblr_put_one_hline:n #1
+  {
+    \hbox_set:Nn \l__tblr_hline_box {#1}
+    \skip_vertical:n { \box_ht:N \l__tblr_hline_box + \box_dp:N \l__tblr_hline_box }
+    \vbox_set:Nn \l__tblr_table_hlines_box
+      {
+        \vbox_unpack:N \l__tblr_table_hlines_box
+        \box_use:N \l__tblr_hline_box
+      }
+  }
+
+\cs_new_protected:Npn \__tblr_put_one_row:n #1
+  {
+    \hbox_set:Nn \l__tblr_row_box {#1}
+    \vbox_set:Nn \l__tblr_table_hlines_box
+      {
+        \vbox_unpack:N \l__tblr_table_hlines_box
+        \skip_vertical:n
+          { \box_ht:N \l__tblr_row_box + \box_dp:N \l__tblr_row_box }
+      }
+    \box_use:N \l__tblr_row_box
+  }
+
+%% #1: hline number
+\cs_new_protected:Npn \__tblr_build_one_hline:n #1
+  {
+    \vbox_set:Nn \l__tblr_table_box { \hbox:n { \__tblr_build_hline:n { #1 } } }
+  }
+
+\tl_new:N \__tblr_vbox_align_tl
+\tl_const:Nn \__tblr_vbox_t_tl {t}
+\tl_const:Nn \__tblr_vbox_T_tl {T}
+\tl_const:Nn \__tblr_vbox_m_tl {m}
+\tl_const:Nn \__tblr_vbox_M_tl {M}
+\tl_const:Nn \__tblr_vbox_c_tl {c}
+\tl_const:Nn \__tblr_vbox_b_tl {b}
+\tl_const:Nn \__tblr_vbox_B_tl {B}
+
+\regex_const:Nn \c__tblr_is_positive_integer_regex { ^ \d+ $ }
+\regex_const:Nn \c__tblr_is_negative_integer_regex { ^ - \d+ $ }
+
+\tl_new:N \l__tblr_delim_left_tl
+\tl_new:N \l__tblr_delim_right_tl
+
+\cs_new_protected:Npn \__tblr_valign_whole:Nn #1 #2
+  {
+    \group_begin:
+    \tl_set:Nx \l__tblr_delim_left_tl
+      { \__tblr_prop_item:nn { inner } { delim-left } }
+    \tl_set:Nx \l__tblr_delim_right_tl
+      { \__tblr_prop_item:nn { inner } { delim-right } }
+    \tl_set:Nn \__tblr_vbox_align_tl {#2}
+    \dim_set:Nn \l__tblr_t_dim { \box_ht:N #1 + \box_dp:N #1 }
+    \tl_case:NnF \__tblr_vbox_align_tl
+      {
+        \__tblr_vbox_m_tl
+          { \__tblr_valign_whole_middle:N #1 }
+        \__tblr_vbox_c_tl
+          { \__tblr_valign_whole_middle:N #1 }
+        \__tblr_vbox_M_tl
+          { \__tblr_valign_whole_middle_row_or_border:N #1 }
+        \__tblr_vbox_t_tl
+          { \__tblr_valign_whole_top:N #1 }
+        \__tblr_vbox_T_tl
+          {
+            \tl_set:Nn \__tblr_vbox_align_tl {1}
+            \__tblr_valign_whole_at_row_from_above:N #1
+          }
+        \__tblr_vbox_b_tl
+          { \__tblr_valign_whole_bottom:N #1 }
+        \__tblr_vbox_B_tl
+          {
+            \tl_set:Nx \__tblr_vbox_align_tl { \int_use:N \c at rowcount }
+            \__tblr_valign_whole_at_row_from_below:N #1
+          }
+      }
+      {
+        \regex_match:NVTF \c__tblr_is_positive_integer_regex \__tblr_vbox_align_tl
+          { \__tblr_valign_whole_at_row:N #1 }
+          {
+            \regex_match:NVTF
+              \c__tblr_is_negative_integer_regex \__tblr_vbox_align_tl
+              { \__tblr_valign_whole_at_border:N #1 }
+              { \__tblr_valign_whole_middle:N #1 }
+          }
+      }
+    %% we have done the job when valign is m or c
+    \box_if_empty:NF #1 { \__tblr_add_delimiters_to_box:N #1 }
+    \group_end:
+  }
+
+%% We use the idea of delarray package to shift table box
+%% when there are delimiters around the table
+\cs_new_protected:Npn \__tblr_add_delimiters_to_box:N #1
+  {
+    \tl_if_empty:NTF \l__tblr_delim_left_tl
+      { \box_use_drop:N #1 }
+      {
+        \box_move_down:nn
+          {
+            ( \box_dp:N #1 - \box_ht:N #1 ) / 2
+            + \tex_fontdimen:D 22 \tex_textfont:D 2
+          }
+          { \__tblr_get_vcenter_box:N #1 }
+      }
+  }
+
+\cs_new_protected:Npn \__tblr_get_vcenter_box:N #1
+  {
+    \hbox:n
+      {
+        $ \m at th \l__tblr_delim_left_tl
+        \tex_vcenter:D { \vbox_unpack_drop:N #1 }
+        \l__tblr_delim_right_tl $
+      }
+  }
+
+\cs_new_protected:Npn \__tblr_valign_whole_middle:N #1
+  {
+    \__tblr_get_vcenter_box:N #1
+  }
+
+\cs_new_protected:Npn \__tblr_valign_whole_top:N #1
+  {
+    \dim_set:Nn \l__tblr_h_dim { \__tblr_valign_get_hline_total:n {1} }
+    \dim_compare:nNnT \l__tblr_h_dim = { 0pt }
+      { \dim_add:Nn \l__tblr_h_dim { \__tblr_valign_get_row_height:n {1} } }
+    \box_set_ht:Nn #1 { \l__tblr_h_dim }
+    \box_set_dp:Nn #1 { \l__tblr_t_dim - \l__tblr_h_dim }
+  }
+
+\cs_new_protected:Npn \__tblr_valign_whole_bottom:N #1
+  {
+    \dim_set:Nn \l__tblr_d_dim
+      { \__tblr_valign_get_hline_total:n { \int_eval:n { \c at rowcount + 1 } } }
+    \dim_compare:nNnTF \l__tblr_d_dim = { 0pt }
+      {
+        \dim_set:Nn \l__tblr_d_dim
+          { \__tblr_valign_get_row_depth:n { \int_use:N \c at rowcount } }
+      }
+      { \dim_zero:N \l__tblr_d_dim }
+    \box_set_ht:Nn #1 { \l__tblr_t_dim - \l__tblr_d_dim }
+    \box_set_dp:Nn #1 { \l__tblr_d_dim }
+  }
+
+\cs_new_protected:Npn \__tblr_valign_whole_middle_row_or_border:N #1
+  {
+    \int_if_odd:nTF { \c at rowcount }
+      {
+        \tl_set:Nx \__tblr_vbox_align_tl { \int_eval:n { (\c at rowcount + 1) / 2 } }
+        \__tblr_valign_whole_at_row_from_above:N #1
+      }
+      {
+        \tl_set:Nx \__tblr_vbox_align_tl { \int_eval:n { \c at rowcount / 2 + 1 } }
+        \__tblr_valign_whole_at_border_from_above:N #1
+      }
+  }
+
+\cs_new_protected:Npn \__tblr_valign_whole_at_row:N #1
+  {
+    \int_compare:nNnTF { 2 * \__tblr_vbox_align_tl } > { \c at rowcount }
+      { \__tblr_valign_whole_at_row_from_below:N #1 }
+      { \__tblr_valign_whole_at_row_from_above:N #1 }
+  }
+
+\cs_new_protected:Npn \__tblr_valign_whole_at_row_from_above:N #1
+  {
+    \dim_set:Nn \l__tblr_h_dim
+      { \__tblr_valign_get_hline_total:n { \__tblr_vbox_align_tl } }
+    \dim_add:Nn \l__tblr_h_dim
+      { \__tblr_valign_get_row_height:n { \__tblr_vbox_align_tl } }
+    \int_step_inline:nn { \__tblr_vbox_align_tl - 1 }
+      {
+        \dim_add:Nn \l__tblr_h_dim { \__tblr_valign_get_hline_total:n {##1} }
+        \dim_add:Nn \l__tblr_h_dim { \__tblr_valign_get_row_total:n {##1} }
+      }
+    \box_set_ht:Nn #1 { \l__tblr_h_dim }
+    \box_set_dp:Nn #1 { \l__tblr_t_dim - \l__tblr_h_dim }
+  }
+
+\cs_new_protected:Npn \__tblr_valign_whole_at_row_from_below:N #1
+  {
+    \dim_set:Nn \l__tblr_d_dim
+      { \__tblr_valign_get_hline_total:n { \int_eval:n {\c at rowcount + 1} } }
+    \dim_add:Nn \l__tblr_d_dim
+      { \__tblr_valign_get_row_depth:n { \__tblr_vbox_align_tl } }
+    \int_step_inline:nnn { \__tblr_vbox_align_tl + 1 } { \c at rowcount }
+      {
+        \dim_add:Nn \l__tblr_d_dim { \__tblr_valign_get_hline_total:n {##1} }
+        \dim_add:Nn \l__tblr_d_dim { \__tblr_valign_get_row_total:n {##1} }
+      }
+    \box_set_dp:Nn #1 { \l__tblr_d_dim }
+    \box_set_ht:Nn #1 { \l__tblr_t_dim - \l__tblr_d_dim }
+  }
+
+\cs_new_protected:Npn \__tblr_valign_whole_at_border:N #1
+  {
+    \tl_set:Nx \__tblr_vbox_align_tl { \int_eval:n { - \__tblr_vbox_align_tl } }
+    \int_compare:nNnTF { 2 * \__tblr_vbox_align_tl - 2 } > { \c at rowcount }
+      { \__tblr_valign_whole_at_border_from_below:N #1 }
+      { \__tblr_valign_whole_at_border_from_above:N #1 }
+  }
+
+\cs_new_protected:Npn \__tblr_valign_whole_at_border_from_above:N #1
+  {
+    \dim_set:Nn \l__tblr_h_dim
+      { \__tblr_valign_get_hline_total:n { \__tblr_vbox_align_tl } }
+    \int_step_inline:nn { \__tblr_vbox_align_tl - 1 }
+      {
+        \dim_add:Nn \l__tblr_h_dim { \__tblr_valign_get_hline_total:n {##1} }
+        \dim_add:Nn \l__tblr_h_dim { \__tblr_valign_get_row_total:n {##1} }
+      }
+    \box_set_ht:Nn #1 { \l__tblr_h_dim }
+    \box_set_dp:Nn #1 { \l__tblr_t_dim - \l__tblr_h_dim }
+  }
+
+\cs_new_protected:Npn \__tblr_valign_whole_at_border_from_below:N #1
+  {
+    \dim_zero:N \l__tblr_d_dim
+    \int_step_inline:nnn { \__tblr_vbox_align_tl } { \c at rowcount }
+      {
+        \dim_add:Nn \l__tblr_d_dim { \__tblr_valign_get_row_total:n {##1} }
+        \dim_add:Nn \l__tblr_d_dim
+          { \__tblr_valign_get_hline_total:n { \int_eval:n { ##1 + 1 } } }
+      }
+    \box_set_dp:Nn #1 { \l__tblr_d_dim }
+    \box_set_ht:Nn #1 { \l__tblr_t_dim - \l__tblr_d_dim }
+  }
+
+\cs_new_nopar:Npn \__tblr_valign_get_hline_total:n #1
+  {
+    \__tblr_spec_item:ne { hline } { [#1] / @hline-height }
+  }
+
+\cs_new_nopar:Npn \__tblr_valign_get_row_total:n #1
+  {
+    \__tblr_data_item:nnn { row } {#1} { abovesep }
+    +
+    \__tblr_data_item:nnn { row } {#1} { @row-height }
+    +
+    \__tblr_data_item:nnn { row } {#1} { belowsep }
+  }
+
+\cs_new_nopar:Npn \__tblr_valign_get_row_height:n #1
+  {
+    \__tblr_data_item:nnn { row } {#1} { abovesep }
+    +
+    ( \__tblr_data_item:nnn { row } {#1} { @row-height }
+      +
+      \__tblr_data_item:nnn { row } {#1} { @row-upper }
+      -
+      \__tblr_data_item:nnn { row } {#1} { @row-lower }
+    ) / 2
+  }
+
+\cs_new_nopar:Npn \__tblr_valign_get_row_depth:n #1
+  {
+    ( \__tblr_data_item:nen { row } {#1} { @row-height }
+      -
+      \__tblr_data_item:nen { row } {#1} { @row-upper }
+      +
+      \__tblr_data_item:nen { row } {#1} { @row-lower }
+    ) / 2
+    +
+    \__tblr_data_item:nnn { row } {#1} { belowsep }
+  }
+
+%%% --------------------------------------------------------
+%%> \section{Build Table Components}
+%%% --------------------------------------------------------
+
+\dim_new:N \l__tblr_col_o_wd_dim
+\dim_new:N \l__tblr_col_b_wd_dim
+
+%% Build hline. #1: row number
+\cs_new_protected:Npn \__tblr_build_hline:n #1
+  {
+    \int_step_inline:nn { \c at colcount }
+      { \__tblr_build_hline_segment:nn { #1 } { ##1 } }
+  }
+\cs_generate_variant:Nn \__tblr_build_hline:n { x, V }
+
+%% #1: row number, #2: column number
+\cs_new_protected:Npn \__tblr_build_hline_segment:nn #1 #2
+  {
+    \tl_set:Nx \l__tblr_n_tl
+      { \__tblr_spec_item:ne { hline } { [#1] / @hline-count } }
+    \tl_set:Nx \l__tblr_o_tl
+      { \__tblr_spec_item:ne { hline } { [#1][#2] / omit } }
+    \__tblr_get_col_outer_width_border_width:nNN {#2}
+      \l__tblr_col_o_wd_dim \l__tblr_col_b_wd_dim
+    \tl_if_empty:NTF \l__tblr_o_tl
+      {
+        \int_compare:nNnT { \l__tblr_n_tl } > {0}
+          { \__tblr_build_hline_segment_real:nn {#1} {#2} }
+      }
+      { \__tblr_build_hline_segment_omit:nn {#1} {#2} }
+  }
+
+%% #1: row number, #2: column number
+\cs_new_protected:Npn \__tblr_build_hline_segment_omit:nn #1 #2
+  {
+    \skip_horizontal:n { \l__tblr_col_o_wd_dim - \l__tblr_col_b_wd_dim }
+  }
+
+%% #1: row number, #2: column number
+\cs_new_protected:Npn \__tblr_build_hline_segment_real:nn #1 #2
+  {
+    \tl_set:Nx \l__tblr_s_tl
+      { \__tblr_prop_item:ne { inner } { rulesep } }
+    \vbox_set:Nn \l__tblr_c_box
+      {
+        %% add an empty hbox to support vbox width
+        \tex_hbox:D to \l__tblr_col_o_wd_dim {}
+        \int_step_inline:nn { \l__tblr_n_tl }
+          {
+            \tl_set:Nx \l__tblr_h_tl
+              { \__tblr_spec_item:ne { hline } { [#1](##1) / @hline-height } }
+            \hrule height ~ 0pt % remove lineskip
+            \hbox_set_to_wd:Nnn \l__tblr_b_box { \l__tblr_col_o_wd_dim }
+              {
+                \__tblr_get_hline_left_right_skips:nnn {#1} {#2} {##1}
+                \skip_horizontal:N \l__tblr_hline_leftskip_dim
+                \tl_set:Nx \l__tblr_f_tl
+                  { \__tblr_spec_item:ne { hline } { [#1][#2](##1) / fg } }
+                \tl_if_empty:NF \l__tblr_f_tl { \color{\l__tblr_f_tl} }
+                \__tblr_get_hline_segment_child:nnn {#1} {#2} {##1}
+                \skip_horizontal:N \l__tblr_hline_rightskip_dim
+              }
+            \box_set_ht:Nn \l__tblr_b_box { \l__tblr_h_tl }
+            \box_set_dp:Nn \l__tblr_b_box { 0pt }
+            \box_use:N \l__tblr_b_box
+            \skip_vertical:n { \l__tblr_s_tl }
+          }
+        \skip_vertical:n { - \l__tblr_s_tl }
+      }
+    \box_use:N \l__tblr_c_box
+    \skip_horizontal:n { - \l__tblr_col_b_wd_dim }
+  }
+
+%% Read from table specifications and calculate the widths of row and border
+%% column outer width = content width + colsep width + border width
+%% #1: the column number, #2: outer width, #3: border width
+\cs_new_protected:Npn \__tblr_get_col_outer_width_border_width:nNN #1 #2 #3
+  {
+    \dim_set:Nn #3
+      { \__tblr_spec_item:ne { vline } { [\int_eval:n {#1 + 1}] / @vline-width } }
+    \dim_set:Nn #2
+      {
+        \__tblr_spec_item:ne { vline } { [#1] / @vline-width }
+        +
+        \__tblr_data_item:nen { column } {#1} { leftsep }
+        +
+        \__tblr_data_item:nen { column } {#1} { @col-width }
+        +
+        \__tblr_data_item:nen { column } {#1} { rightsep }
+        +
+        #3
+      }
+  }
+
+\dim_new:N \l__tblr_hline_leftskip_dim
+\dim_new:N \l__tblr_hline_rightskip_dim
+
+%% Calculate left and right skips from leftpos and rightpos specifications
+%% #1: row number; #2: column number; #3: hline index;
+\cs_new_protected:Npn \__tblr_get_hline_left_right_skips:nnn #1 #2 #3
+  {
+    \tl_set:Nx \l__tblr_hline_leftpos_tl
+      { \__tblr_spec_item:ne { hline } { [#1][#2](#3) / leftpos } }
+    \tl_if_empty:NT \l__tblr_hline_leftpos_tl
+      { \tl_set:Nn \l__tblr_hline_leftpos_tl {1} } % default position
+    \tl_set:Nx \l__tblr_hline_rightpos_tl
+      { \__tblr_spec_item:ne { hline } { [#1][#2](#3) / rightpos } }
+    \tl_if_empty:NT \l__tblr_hline_rightpos_tl
+      { \tl_set:Nn \l__tblr_hline_rightpos_tl {1} } % default position
+    \fp_compare:nNnT { \l__tblr_hline_leftpos_tl } < {1}
+      {
+        \dim_set:Nn \l_tmpa_dim
+          { \__tblr_spec_item:ne { vline } { [#2] / @vline-width } }
+        \dim_set:Nn \l_tmpb_dim
+          { \__tblr_data_item:nen { column } {#2} { leftsep } }
+        \fp_compare:nNnTF { \l__tblr_hline_leftpos_tl } < {0}
+          {
+            \dim_set:Nn \l__tblr_hline_leftskip_dim
+              { \l_tmpa_dim - \l__tblr_hline_leftpos_tl \l_tmpb_dim }
+          }
+          {
+            \dim_set:Nn \l__tblr_hline_leftskip_dim
+              { \l_tmpa_dim - \l__tblr_hline_leftpos_tl \l_tmpa_dim }
+          }
+      }
+    \fp_compare:nNnT { \l__tblr_hline_rightpos_tl } < {1}
+      {
+        \dim_set:Nn \l_tmpa_dim
+          {
+            \__tblr_spec_item:ne { vline }
+              { [\int_eval:n { #2 + 1 }] / @vline-width }
+          }
+        \dim_set:Nn \l_tmpb_dim
+          { \__tblr_data_item:nen { column } {#2} { rightsep } }
+        \fp_compare:nNnTF { \l__tblr_hline_rightpos_tl } < {0}
+          {
+            \dim_set:Nn \l__tblr_hline_rightskip_dim
+              { \l_tmpa_dim - \l__tblr_hline_rightpos_tl \l_tmpb_dim }
+          }
+          {
+            \dim_set:Nn \l__tblr_hline_rightskip_dim
+              { \l_tmpa_dim - \l__tblr_hline_rightpos_tl \l_tmpa_dim }
+          }
+      }
+  }
+
+\dim_new:N \l__tblr_row_ht_dim
+\dim_new:N \l__tblr_row_dp_dim
+\dim_new:N \l__tblr_row_abovesep_dim
+\dim_new:N \l__tblr_row_belowsep_dim
+\box_new:N \l__tblr_row_vlines_box
+\box_new:N \l__tblr_vline_box
+\box_new:N \l__tblr_cell_box
+
+%% Build current row, #1: row number
+%% To fix disappeared vlines with colorful tables in Adobe Reader (see #76),
+%% we collect all vlines and draw them at the end of the row.
+\cs_new_protected:Npn \__tblr_build_row:N #1
+  {
+    \int_set:Nn \c at rownum {#1}
+    \__tblr_update_rowsep_registers:
+    \__tblr_get_row_inner_height_depth:VNNNN #1
+      \l__tblr_row_ht_dim \l__tblr_row_dp_dim
+      \l__tblr_row_abovesep_dim \l__tblr_row_belowsep_dim
+    \__tblr_hook_use:n { tabularray/row/before }
+    \tblr_vrule_wd_ht_dp:nnn {0pt} {\l__tblr_row_ht_dim} {\l__tblr_row_dp_dim}
+    \hbox_set:Nn \l__tblr_row_vlines_box
+      {
+        \tblr_vrule_wd_ht_dp:nnn {0pt} {\l__tblr_row_ht_dim} {\l__tblr_row_dp_dim}
+      }
+    \int_step_variable:nNn { \c at colcount } \l__tblr_j_tl
+      {
+        \__tblr_put_one_vline:n
+          { \__tblr_build_vline_segment:nn {#1} { \l__tblr_j_tl } }
+        \__tblr_put_one_cell:n { \__tblr_build_cell:NN #1 \l__tblr_j_tl }
+      }
+    \__tblr_put_one_vline:n
+      { \__tblr_build_vline_segment:nn {#1} { \int_eval:n {\c at colcount + 1} } }
+    \skip_horizontal:n { - \box_wd:N \l__tblr_row_vlines_box }
+    \box_use:N \l__tblr_row_vlines_box
+    \__tblr_hook_use:n { tabularray/row/after }
+  }
+
+%% Read from table specifications and calculate inner height/depth of the row
+%% inner height = abovesep + above vspace + row upper
+%% inner depth = row lower + below vspace + belowsep
+%% #1: the row number; #2: resulting inner height; #3: resulting inner depth;
+%% #4: restulting abovesep; #5: restulting belowsep.
+
+\dim_new:N \l__row_upper_dim
+\dim_new:N \l__row_lower_dim
+\dim_new:N \l__row_vpace_dim
+
+\cs_new_protected:Npn \__tblr_get_row_inner_height_depth:nNNNN #1 #2 #3 #4 #5
+  {
+    \dim_set:Nn #4
+      { \__tblr_data_item:nen { row } {#1} { abovesep } }
+    \dim_set:Nn #5
+      { \__tblr_data_item:nen { row } {#1} { belowsep } }
+    \dim_set:Nn \l__row_upper_dim
+      {  \__tblr_data_item:nen { row } {#1} { @row-upper } }
+    \dim_set:Nn \l__row_lower_dim
+      {  \__tblr_data_item:nen { row } {#1} { @row-lower } }
+    \dim_set:Nn \l__row_vpace_dim
+      {
+        ( \__tblr_data_item:nen { row } {#1} { @row-height }
+          - \l__row_upper_dim - \l__row_lower_dim ) / 2
+      }
+    \dim_set:Nn #2 { #4 + \l__row_vpace_dim + \l__row_upper_dim }
+    \dim_set:Nn #3 { \l__row_lower_dim + \l__row_vpace_dim + #5 }
+  }
+\cs_generate_variant:Nn \__tblr_get_row_inner_height_depth:nNNNN { V }
+
+\cs_new_protected:Npn \__tblr_put_one_vline:n #1
+  {
+    \hbox_set:Nn \l__tblr_vline_box {#1}
+    \skip_horizontal:n { \box_wd:N \l__tblr_vline_box }
+    \hbox_set:Nn \l__tblr_row_vlines_box
+      {
+        \hbox_unpack:N \l__tblr_row_vlines_box
+        \box_use:N \l__tblr_vline_box
+      }
+  }
+
+\cs_new_protected:Npn \__tblr_put_one_cell:n #1
+  {
+    \hbox_set:Nn \l__tblr_cell_box {#1}
+    \hbox_set:Nn \l__tblr_row_vlines_box
+      {
+        \hbox_unpack:N \l__tblr_row_vlines_box
+        \skip_horizontal:n { \box_wd:N \l__tblr_cell_box }
+      }
+    \box_use:N \l__tblr_cell_box
+  }
+
+%% #1: row number, #2: column number
+\cs_new_protected:Npn \__tblr_build_vline_segment:nn #1 #2
+  {
+    \tl_set:Nx \l__tblr_n_tl
+      { \__tblr_spec_item:ne { vline } { [#2] / @vline-count } }
+    \tl_set:Nx \l__tblr_o_tl
+      { \__tblr_spec_item:ne { vline } { [#1][#2] / omit } }
+    \tl_if_empty:NTF \l__tblr_o_tl
+      {
+        \int_compare:nNnT { \l__tblr_n_tl } > {0}
+          { \__tblr_build_vline_segment_real:nn {#1} {#2} }
+      }
+      { \__tblr_build_vline_segment_omit:nn {#1} {#2} }
+  }
+
+%% #1: row number, #2: column number
+\cs_new_protected:Npn \__tblr_build_vline_segment_omit:nn #1 #2
+  {
+    \tl_set:Nx \l__tblr_w_tl
+      { \__tblr_spec_item:ne { vline } { [#2] / @vline-width } }
+    \skip_horizontal:N \l__tblr_w_tl
+  }
+
+%% #1: row number, #2: column number
+%% We make every vline segment intersect with first hline below
+%% to remove gaps in vlines around multirow cells
+\cs_new_protected:Npn \__tblr_build_vline_segment_real:nn #1 #2
+  {
+    \tl_set:Nx \l__tblr_s_tl
+      { \__tblr_prop_item:ne { inner } { rulesep } }
+    \hbox_set:Nn \l__tblr_a_box
+      {
+        \int_step_inline:nn { \l__tblr_n_tl }
+          {
+            \tl_set:Nx \l__tblr_w_tl
+              { \__tblr_spec_item:ne { vline } { [#2](##1) / @vline-width } }
+            \vbox_set_to_ht:Nnn \l__tblr_b_box
+              { \dim_eval:n { \l__tblr_row_ht_dim + \l__tblr_row_dp_dim } }
+              {
+                \tl_set:Nx \l__tblr_f_tl
+                  { \__tblr_spec_item:ne { vline } { [#1][#2](##1) / fg } }
+                \tl_if_empty:NF \l__tblr_f_tl { \color{\l__tblr_f_tl} }
+                \__tblr_get_vline_above_below_skips:nnn {#1} {#2} {##1}
+                \skip_vertical:N \l__tblr_vline_aboveskip_dim
+                \__tblr_get_vline_segment_child:nnnxx {#1} {#2} {##1}
+                  { \dim_eval:n { \l__tblr_row_ht_dim } }
+                  { \dim_eval:n { \l__tblr_row_dp_dim } }
+                \skip_vertical:N \l__tblr_vline_belowskip_dim
+              }
+            \box_set_wd:Nn \l__tblr_b_box { \l__tblr_w_tl }
+            \box_use:N \l__tblr_b_box
+            \skip_horizontal:n { \l__tblr_s_tl }
+          }
+        \skip_horizontal:n { - \l__tblr_s_tl }
+      }
+    \vbox_set:Nn \l__tblr_c_box { \box_use:N \l__tblr_a_box }
+    \box_set_ht:Nn \l__tblr_c_box { \dim_use:N \l__tblr_row_ht_dim }
+    \box_set_dp:Nn \l__tblr_c_box { \dim_use:N \l__tblr_row_dp_dim }
+    \box_use:N \l__tblr_c_box
+  }
+
+\dim_new:N \l__tblr_vline_aboveskip_dim
+\dim_new:N \l__tblr_vline_belowskip_dim
+
+%% Calculate above and below skips from abovepos and belowpos specifications
+%% #1: row number; #2: column number; #3: vline index;
+\cs_new_protected:Npn \__tblr_get_vline_above_below_skips:nnn #1 #2 #3
+  {
+    \tl_set:Nx \l__tblr_vline_abovepos_tl
+      { \__tblr_spec_item:ne { vline } { [#1][#2](#3) / abovepos } }
+    \tl_if_empty:NT \l__tblr_vline_abovepos_tl
+      {
+        \tl_set:Nn \l__tblr_vline_abovepos_tl {0} % default position
+      }
+    \fp_compare:nNnF { \l__tblr_vline_abovepos_tl } = {0}
+      {
+        \dim_set:Nn \l_tmpa_dim
+          { \__tblr_spec_item:ne { hline } { [#1] / @hline-height } }
+        \fp_compare:nNnTF { \l__tblr_vline_abovepos_tl } < {0}
+          {
+            \dim_set:Nn \l__tblr_vline_aboveskip_dim
+              { - \l__tblr_vline_abovepos_tl \l__tblr_row_abovesep_dim }
+          }
+          {
+            \dim_set:Nn \l__tblr_vline_aboveskip_dim
+              { - \l__tblr_vline_abovepos_tl \l_tmpa_dim }
+          }
+      }
+    %% To join two vline segment above and below a cline,
+    %% we choose to extend every vline downwards a little (#55, #272).
+    \tl_set:Nx \l__tblr_vline_belowpos_tl
+      { \__tblr_spec_item:ne { vline } { [#1][#2](#3) / belowpos } }
+    \tl_if_empty:NTF \l__tblr_vline_belowpos_tl
+      {
+        \dim_set:Nn \l__tblr_vline_belowskip_dim
+          {
+            - \__tblr_spec_item:ne { hline }
+                { [\int_eval:n { #1 + 1 }](1) / @hline-height }
+            + 0pt
+          }
+      }
+      {
+        \dim_set:Nn \l_tmpa_dim
+          {
+            \__tblr_spec_item:ne { hline }
+              { [\int_eval:n { #1 + 1 }] / @hline-height }
+          }
+        \fp_compare:nNnTF { \l__tblr_vline_belowpos_tl } < {0}
+          {
+            \dim_set:Nn \l__tblr_vline_belowskip_dim
+              { - \l__tblr_vline_belowpos_tl \l__tblr_row_belowsep_dim }
+          }
+          {
+            \dim_set:Nn \l__tblr_vline_belowskip_dim
+              { - \l__tblr_vline_belowpos_tl \l_tmpa_dim }
+          }
+      }
+  }
+
+%% These public variables are updated by default before building a cell
+\tl_new:N \lTblrCellRowSpanTl
+\tl_new:N \lTblrCellColSpanTl
+\tl_new:N \lTblrCellBackgroundTl
+\bool_new:N \lTblrCellOmittedBool
+
+\dim_new:N \l__tblr_cell_wd_dim
+\dim_new:N \l__tblr_cell_ht_dim
+
+\cs_new_protected:Npn \__tblr_build_cell:NN #1 #2
+  {
+    \int_set:Nn \c at colnum {#2}
+    \__tblr_update_colsep_registers:
+    \group_begin:
+    \tl_set:Nx \l__tblr_w_tl
+      { \__tblr_data_item:nen { column } {#2} { @col-width } }
+    \tl_set:Nx \l__tblr_h_tl
+      { \__tblr_data_item:nen { row } {#1} { @row-height } }
+    \tl_set:Nx \l__tblr_x_tl
+      { \__tblr_data_item:nen { column } {#2} { leftsep} }
+    \tl_set:Nx \l__tblr_y_tl
+      { \__tblr_data_item:nen { column } {#2} { rightsep } }
+    \tl_set:Nx \lTblrCellColSpanTl
+      { \__tblr_data_item:neen { cell } {#1} {#2} { colspan } }
+    \int_compare:nNnTF { \lTblrCellColSpanTl } < {2}
+      { \dim_set:Nn \l__tblr_cell_wd_dim { \l__tblr_w_tl } }
+      {
+        \__tblr_get_span_horizontal_sizes:NNNNN #1 #2
+          \l__tblr_o_dim \l__tblr_cell_wd_dim \l__tblr_q_dim
+      }
+    \tl_set:Nx \lTblrCellRowSpanTl
+      { \__tblr_data_item:neen { cell } {#1} {#2} { rowspan } }
+    \int_compare:nNnTF { \lTblrCellRowSpanTl } < {2}
+      { \dim_set:Nn \l__tblr_cell_ht_dim { \l__tblr_h_tl } }
+      {
+        \__tblr_get_span_vertical_sizes:NNNNN #1 #2
+          \l__tblr_r_dim \l__tblr_cell_ht_dim \l__tblr_t_dim
+      }
+    \__tblr_get_cell_alignments:nn {#1} {#2}
+    \__tblr_build_cell_background:NN #1 #2
+    \__tblr_build_cell_content:NN #1 #2
+    \group_end:
+  }
+
+%% These public variables are updated by html library before building a cell
+\tl_new:N \lTblrCellAboveBorderStyleTl
+\tl_new:N \lTblrCellAboveBorderWidthTl
+\tl_new:N \lTblrCellAboveBorderColorTl
+\tl_new:N \lTblrCellBelowBorderStyleTl
+\tl_new:N \lTblrCellBelowBorderWidthTl
+\tl_new:N \lTblrCellBelowBorderColorTl
+\tl_new:N \lTblrCellLeftBorderStyleTl
+\tl_new:N \lTblrCellLeftBorderWidthTl
+\tl_new:N \lTblrCellLeftBorderColorTl
+\tl_new:N \lTblrCellRightBorderStyleTl
+\tl_new:N \lTblrCellRightBorderWidthTl
+\tl_new:N \lTblrCellRihgtBorderColorTl
+
+%% #1: row number in tl; #2: column number in tl
+%% This function is called only when html library is loaded.
+%% The properties can be used by tagpdf, tex4ht and lwarp packages
+\cs_new_protected:Npn \__tblr_expose_cell_properties:NN #1 #2
+  {
+    \__tblr_expose_cell_border:NNnn #1 #2 { hline } { Above }
+    \tl_set:Nx \l_tmpa_tl { \int_eval:n { #1 + \lTblrCellRowSpanTl } }
+    \__tblr_expose_cell_border:NNnn \l_tmpa_tl #2 { hline } { Below }
+    \__tblr_expose_cell_border:NNnn #1 #2 { vline } { Left }
+    \tl_set:Nx \l_tmpb_tl { \int_eval:n { #2 + \lTblrCellColSpanTl } }
+    \__tblr_expose_cell_border:NNnn #1 \l_tmpb_tl { vline } { Right }
+  }
+
+\tl_new:N \l__tblr_dash_value_tl
+\tl_new:N \l__tblr_dash_value_head_tl
+\tl_new:N \l__tblr_dash_value_tail_tl
+\tl_new:N \l__tblr_width_value_tl
+\tl_new:N \l__tblr_color_value_tl
+
+%% #1: row number in tl; #2: column number in tl;
+%% #3: hline or vline; #4: position of border (Above/Below/Left/Right).
+\cs_new_protected:Npn \__tblr_expose_cell_border:NNnn #1 #2 #3 #4
+  {
+    %% get border style
+    \tl_set:Nx \l__tblr_dash_value_tl %% may be empty
+      { \__tblr_spec_item:ne { #3 } { [#1][#2](1) / @dash } }
+    \tl_set:Nx \l__tblr_dash_value_head_tl { \tl_head:N \l__tblr_dash_value_tl }
+    \tl_set:Nx \l__tblr_dash_value_tail_tl { \tl_tail:N \l__tblr_dash_value_tl }
+    \exp_args:NV \tl_if_eq:NNTF \l__tblr_dash_value_head_tl \@tblr at dash
+      {
+        \tl_set_eq:cN { lTblrCell #4 BorderStyleTl } \l__tblr_dash_value_tail_tl
+        %% get border width
+        \tl_set:Nx \l__tblr_width_value_tl
+          { \__tblr_spec_item:ne { #3 } { [#1][#2](1) / wd } }
+        \tl_if_empty:NTF \l__tblr_width_value_tl
+          { \tl_set:cn { lTblrCell #4 BorderWidthTl } { 0.4pt } }
+          { \tl_set_eq:cN { lTblrCell #4 BorderWidthTl } \l__tblr_width_value_tl }
+        %% get border color
+        \tl_set:cx { lTblrCell #4 BorderColorTl }
+          { \__tblr_spec_item:ne { #3 } { [#1][#2](1) / fg } }
+      }
+      {
+        \tl_clear:c { lTblrCell #4 BorderStyleTl }
+        \tl_set:cn { lTblrCell #4 BorderWidthTl } { 0pt }
+        \tl_clear:c { lTblrCell #4 BorderColorTl }
+      }
+  }
+
+\cs_new_protected:Npn \__tblr_build_cell_content:NN #1 #2
+  {
+    \bool_if:NT \l__tblr_html_variables_bool
+      { \__tblr_expose_cell_properties:NN #1 #2 }
+    \__tblr_hook_use:n { tabularray/cell/before }
+    \hbox_set_to_wd:Nnn \l__tblr_a_box { \l__tblr_cell_wd_dim }
+      {
+        \tl_if_eq:NnTF \g__tblr_cell_halign_tl {j}
+          % cell width may be less than column width for j cells
+          { \__tblr_get_cell_text:nn {#1} {#2} \hfil }
+          {
+            \tl_if_eq:NnF \g__tblr_cell_halign_tl {l} { \hfil }
+            \__tblr_get_cell_text:nn {#1} {#2}
+            \tl_if_eq:NnF \g__tblr_cell_halign_tl {r} { \hfil }
+          }
+      }
+    \vbox_set_to_ht:Nnn \l__tblr_b_box { \l__tblr_cell_ht_dim }
+      {
+        \tl_case:Nn \g__tblr_cell_valign_tl
+          {
+            \c__tblr_valign_m_tl
+              {
+                \vfil
+                \int_compare:nNnT { \lTblrCellRowSpanTl } < {2}
+                  {
+                    \box_set_ht:Nn \l__tblr_a_box
+                      { \__tblr_data_item:nen { row } {#1} { @row-upper } }
+                    \box_set_dp:Nn \l__tblr_a_box
+                      { \__tblr_data_item:nen { row } {#1} { @row-lower } }
+                  }
+                \box_use:N \l__tblr_a_box
+                \vfil
+              }
+            \c__tblr_valign_h_tl
+              {
+                \box_set_ht:Nn \l__tblr_a_box
+                  { \__tblr_data_item:nen { row } {#1} { @row-head } }
+                \box_use:N \l__tblr_a_box
+                \vfil
+              }
+            \c__tblr_valign_f_tl
+              {
+                \vfil
+                \int_compare:nNnTF { \lTblrCellRowSpanTl } < {2}
+                  {
+                    \box_set_dp:Nn \l__tblr_a_box
+                      { \__tblr_data_item:nen { row } {#1} { @row-foot } }
+                  }
+                  {
+                    \box_set_dp:Nn \l__tblr_a_box
+                      {
+                        \__tblr_data_item:nen
+                          { row }
+                          { \int_eval:n { #1 + \lTblrCellRowSpanTl - 1 } }
+                          { @row-foot }
+                      }
+                  }
+                \box_use:N \l__tblr_a_box
+              }
+          }
+        \hrule height ~ 0pt %% zero depth
+      }
+    \vbox_set_to_ht:Nnn \l__tblr_c_box
+      { \l__tblr_row_ht_dim - \l__tblr_row_abovesep_dim }
+      {
+        \box_use:N \l__tblr_b_box
+        \vss
+      }
+    \skip_horizontal:n { \l__tblr_x_tl }
+    \box_use:N \l__tblr_c_box
+    \skip_horizontal:n { \l__tblr_y_tl - \l__tblr_cell_wd_dim + \l__tblr_w_tl }
+    \__tblr_hook_use:n { tabularray/cell/after }
+  }
+
+\cs_new_protected:Npn \__tblr_build_cell_background:NN #1 #2
+  {
+    \bool_set:Nn \lTblrCellOmittedBool
+      {
+        \int_compare_p:nNn
+          { \__tblr_data_item:neen { cell } {#1} {#2} { omit } } = {1}
+      }
+    \bool_if:NF \lTblrCellOmittedBool
+      {
+        \tl_set:Nx \lTblrCellBackgroundTl
+          { \__tblr_data_item:neen { cell } {#1} {#2} { background } }
+        \group_begin:
+        \tl_if_empty:NF \lTblrCellBackgroundTl
+          {
+            \__tblr_get_cell_background_width:NNN #1 #2 \l_tmpa_dim
+            \__tblr_get_cell_background_depth:NNN #1 #2 \l_tmpb_dim
+            \__tblr_build_cell_background:nnnn
+              { \dim_use:N \l_tmpa_dim }
+              { \l__tblr_row_ht_dim }
+              { \dim_use:N \l_tmpb_dim }
+              { \lTblrCellBackgroundTl }
+          }
+        \group_end:
+      }
+  }
+
+%% #1: row number; #2: column number; #3 resulting dimension
+\cs_new_protected:Npn \__tblr_get_cell_background_width:NNN #1 #2 #3
+  {
+    \int_compare:nNnTF { \lTblrCellColSpanTl } < {2}
+      { \dim_set:Nn #3 { \l__tblr_x_tl + \l__tblr_w_tl + \l__tblr_y_tl } }
+      {
+        \dim_set:Nn #3 { \l__tblr_o_dim + \l__tblr_cell_wd_dim + \l__tblr_q_dim }
+      }
+  }
+
+%% #1: row number; #2: column number; #3 resulting dimension
+\cs_new_protected:Npn \__tblr_get_cell_background_depth:NNN #1 #2 #3
+  {
+    \int_compare:nNnTF { \lTblrCellRowSpanTl } < {2}
+      { \dim_set_eq:NN #3 \l__tblr_row_dp_dim }
+      {
+        \dim_set:Nn #3
+          {
+            \l__tblr_r_dim + \l__tblr_cell_ht_dim
+                           + \l__tblr_t_dim - \l__tblr_row_ht_dim
+          }
+      }
+  }
+
+%% #1: width, #2: height, #3: depth, #4: color
+\cs_new_protected:Npn \__tblr_build_cell_background:nnnn #1 #2 #3 #4
+  {
+    \hbox_set:Nn \l__tblr_a_box
+      {
+        \color {#4}
+        \vrule width ~ #1 ~ height ~ #2 ~ depth ~ #3
+      }
+    \box_set_dp:Nn \l__tblr_a_box { 0pt }
+    \box_use:N \l__tblr_a_box
+    \skip_horizontal:n { - #1 }
+  }
+
+%% #1: row number; #2: column number; #3: dimen register for rowsep above.
+%% #4: dimen register for total height; #5: dimen register for rowsep below.
+%% We can use \l__tblr_row_item_skip_size_prop which was made before
+%% But when vspan=even, there are no itemskip in the prop list.
+%% Therefore we need to calculate them from the sizes of items and skips
+\cs_new_protected:Npn \__tblr_get_span_vertical_sizes:NNNNN #1 #2 #3 #4 #5
+  {
+    \dim_set:Nn #3
+      { \__tblr_data_item:nen { row } {#1} { abovesep } }
+    \dim_zero:N #4
+    \dim_add:Nn #4
+      { \prop_item:Ne \l__tblr_row_item_skip_size_prop { item[#1] } }
+    \int_step_inline:nnn { #1 + 1 } { #1 + \lTblrCellRowSpanTl - 1 }
+      {
+        \dim_add:Nn #4
+          {
+            \prop_item:Ne \l__tblr_row_item_skip_size_prop { skip[##1] }
+            +
+            \prop_item:Ne \l__tblr_row_item_skip_size_prop { item[##1] }
+          }
+      }
+    \dim_set:Nn #5
+      {
+        \__tblr_data_item:nen { row }
+          { \int_eval:n { #1 + \lTblrCellRowSpanTl - 1 } } { belowsep }
+      }
+    %\tl_log:x { cell[#1][#2] ~:~ \dim_use:N #3, \dim_use:N #4, \dim_use:N #5 }
+  }
+
+%% #1: row number; #2: column number; #3: dimen register for colsep left.
+%% #4: dimen register for total width; #5: dimen register for colsep right.
+%% We can use \l__tblr_col_item_skip_size_prop which was made before
+%% But when hspan=even or hspan=minimal, there are no itemskip in the prop list.
+%% Therefore we need to calculate them from the sizes of items and skips
+\cs_new_protected:Npn \__tblr_get_span_horizontal_sizes:NNNNN #1 #2 #3 #4 #5
+  {
+    \dim_set:Nn #3
+      { \__tblr_data_item:nen { column } {#2} { leftsep } }
+    \dim_zero:N #4
+    \dim_add:Nn #4
+      { \prop_item:Ne \l__tblr_col_item_skip_size_prop { item[#2] } }
+    \int_step_inline:nnn { #2 + 1 } { #2 + \lTblrCellColSpanTl - 1 }
+      {
+        \dim_add:Nn #4
+          {
+            \prop_item:Ne \l__tblr_col_item_skip_size_prop { skip[##1] }
+            +
+            \prop_item:Ne \l__tblr_col_item_skip_size_prop { item[##1] }
+          }
+      }
+    \dim_set:Nn #5
+      {
+        \__tblr_data_item:nen { column }
+          { \int_eval:n {#2 + \lTblrCellColSpanTl - 1} } { rightsep }
+      }
+    %\tl_log:x { cell[#1][#2] ~:~ \dim_use:N #3, \dim_use:N #4, \dim_use:N #5 }
+  }
+
+%%% --------------------------------------------------------
+%%> \section{Tracing Tabularray}
+%%% --------------------------------------------------------
+
+\NewDocumentCommand \SetTblrTracing { m }
+  {
+    \keys_set:nn { tblr-set-tracing } {#1}
+  }
+
+\bool_new:N \g__tblr_tracing_text_bool
+\bool_new:N \g__tblr_tracing_command_bool
+\bool_new:N \g__tblr_tracing_option_bool
+\bool_new:N \g__tblr_tracing_theme_bool
+\bool_new:N \g__tblr_tracing_outer_bool
+\bool_new:N \g__tblr_tracing_inner_bool
+\bool_new:N \g__tblr_tracing_column_bool
+\bool_new:N \g__tblr_tracing_row_bool
+\bool_new:N \g__tblr_tracing_cell_bool
+\bool_new:N \g__tblr_tracing_vline_bool
+\bool_new:N \g__tblr_tracing_hline_bool
+\bool_new:N \g__tblr_tracing_colspec_bool
+\bool_new:N \g__tblr_tracing_rowspec_bool
+\bool_new:N \g__tblr_tracing_target_bool
+\bool_new:N \g__tblr_tracing_cellspan_bool
+\bool_new:N \g__tblr_tracing_intarray_bool
+\bool_new:N \g__tblr_tracing_page_bool
+\bool_new:N \g__tblr_tracing_step_bool
+
+\bool_gset_true:N \g__tblr_tracing_step_bool
+
+\keys_define:nn { tblr-set-tracing }
+  {
+    +text .code:n = \bool_gset_true:N \g__tblr_tracing_text_bool,
+    -text .code:n = \bool_gset_false:N \g__tblr_tracing_text_bool,
+    +command .code:n = \bool_gset_true:N \g__tblr_tracing_command_bool,
+    -command .code:n = \bool_gset_false:N \g__tblr_tracing_command_bool,
+    +option .code:n = \bool_gset_true:N \g__tblr_tracing_option_bool,
+    -option .code:n = \bool_gset_false:N \g__tblr_tracing_option_bool,
+    +theme .code:n = \bool_gset_true:N \g__tblr_tracing_theme_bool,
+    -theme .code:n = \bool_gset_false:N \g__tblr_tracing_theme_bool,
+    +outer .code:n = \bool_gset_true:N \g__tblr_tracing_outer_bool,
+    -outer .code:n = \bool_gset_false:N \g__tblr_tracing_outer_bool,
+    +inner .code:n = \bool_gset_true:N \g__tblr_tracing_inner_bool,
+    -inner .code:n = \bool_gset_false:N \g__tblr_tracing_inner_bool,
+    +column .code:n = \bool_gset_true:N \g__tblr_tracing_column_bool,
+    -column .code:n = \bool_gset_false:N \g__tblr_tracing_column_bool,
+    +row .code:n = \bool_gset_true:N \g__tblr_tracing_row_bool,
+    -row .code:n = \bool_gset_false:N \g__tblr_tracing_row_bool,
+    +cell .code:n = \bool_gset_true:N \g__tblr_tracing_cell_bool,
+    -cell .code:n = \bool_gset_false:N \g__tblr_tracing_cell_bool,
+    +vline .code:n = \bool_gset_true:N \g__tblr_tracing_vline_bool,
+    -vline .code:n = \bool_gset_false:N \g__tblr_tracing_vline_bool,
+    +hline .code:n = \bool_gset_true:N \g__tblr_tracing_hline_bool,
+    -hline .code:n = \bool_gset_false:N \g__tblr_tracing_hline_bool,
+    +colspec .code:n = \bool_gset_true:N \g__tblr_tracing_colspec_bool,
+    -colspec .code:n = \bool_gset_false:N \g__tblr_tracing_colspec_bool,
+    +rowspec .code:n = \bool_gset_true:N \g__tblr_tracing_rowspec_bool,
+    -rowspec .code:n = \bool_gset_false:N \g__tblr_tracing_rowspec_bool,
+    +target .code:n = \bool_gset_true:N \g__tblr_tracing_target_bool,
+    -target .code:n = \bool_gset_false:N \g__tblr_tracing_target_bool,
+    +cellspan .code:n = \bool_gset_true:N \g__tblr_tracing_cellspan_bool,
+    -cellspan .code:n = \bool_gset_false:N \g__tblr_tracing_cellspan_bool,
+    +intarray .code:n = \bool_gset_true:N \g__tblr_tracing_intarray_bool,
+    -intarray .code:n = \bool_gset_false:N \g__tblr_tracing_intarray_bool,
+    +page .code:n = \bool_gset_true:N \g__tblr_tracing_page_bool,
+    -page .code:n = \bool_gset_false:N \g__tblr_tracing_page_bool,
+    +step .code:n = \bool_gset_true:N \g__tblr_tracing_step_bool,
+    -step .code:n = \bool_gset_false:N \g__tblr_tracing_step_bool,
+    all .code:n = \__tblr_enable_all_tracings:,
+    none .code:n = \__tblr_disable_all_tracings:,
+  }
+
+\cs_new_protected_nopar:Npn \__tblr_enable_all_tracings:
+  {
+    \bool_gset_true:N \g__tblr_tracing_text_bool
+    \bool_gset_true:N \g__tblr_tracing_command_bool
+    \bool_gset_true:N \g__tblr_tracing_option_bool
+    \bool_gset_true:N \g__tblr_tracing_theme_bool
+    \bool_gset_true:N \g__tblr_tracing_outer_bool
+    \bool_gset_true:N \g__tblr_tracing_inner_bool
+    \bool_gset_true:N \g__tblr_tracing_column_bool
+    \bool_gset_true:N \g__tblr_tracing_row_bool
+    \bool_gset_true:N \g__tblr_tracing_cell_bool
+    \bool_gset_true:N \g__tblr_tracing_vline_bool
+    \bool_gset_true:N \g__tblr_tracing_hline_bool
+    \bool_gset_true:N \g__tblr_tracing_colspec_bool
+    \bool_gset_true:N \g__tblr_tracing_rowspec_bool
+    \bool_gset_true:N \g__tblr_tracing_target_bool
+    \bool_gset_true:N \g__tblr_tracing_cellspan_bool
+    \bool_gset_true:N \g__tblr_tracing_intarray_bool
+    \bool_gset_true:N \g__tblr_tracing_page_bool
+    \bool_gset_true:N \g__tblr_tracing_step_bool
+  }
+
+\cs_new_protected_nopar:Npn \__tblr_disable_all_tracings:
+  {
+    \bool_gset_false:N \g__tblr_tracing_text_bool
+    \bool_gset_false:N \g__tblr_tracing_command_bool
+    \bool_gset_false:N \g__tblr_tracing_option_bool
+    \bool_gset_false:N \g__tblr_tracing_theme_bool
+    \bool_gset_false:N \g__tblr_tracing_outer_bool
+    \bool_gset_false:N \g__tblr_tracing_inner_bool
+    \bool_gset_false:N \g__tblr_tracing_column_bool
+    \bool_gset_false:N \g__tblr_tracing_row_bool
+    \bool_gset_false:N \g__tblr_tracing_cell_bool
+    \bool_gset_false:N \g__tblr_tracing_vline_bool
+    \bool_gset_false:N \g__tblr_tracing_hline_bool
+    \bool_gset_false:N \g__tblr_tracing_colspec_bool
+    \bool_gset_false:N \g__tblr_tracing_rowspec_bool
+    \bool_gset_false:N \g__tblr_tracing_target_bool
+    \bool_gset_false:N \g__tblr_tracing_cellspan_bool
+    \bool_gset_false:N \g__tblr_tracing_intarray_bool
+    \bool_gset_false:N \g__tblr_tracing_page_bool
+    \bool_gset_false:N \g__tblr_tracing_step_bool
+  }
+
+\NewDocumentCommand \LogTblrTracing { m }
+  {
+    \keys_set:nn { tblr-log-tracing } {#1}
+  }
+
+\keys_define:nn { tblr-log-tracing }
+  {
+    step .code:n = \__tblr_log_tracing_step:n {#1},
+    unknown .code:n = \__tblr_log_tracing:N \l_keys_key_str
+  }
+
+\cs_new_protected:Npn \__tblr_log_tracing:N #1
+  {
+    \bool_if:cT { g__tblr_tracing_ #1 _bool }
+      { \cs:w __tblr_log_tracing _ #1 : \cs_end: }
+  }
+
+\cs_new_protected:Npn \__tblr_log_tracing_text:
+  {
+    \__tblr_spec_log:n { text }
+  }
+
+\cs_new_protected:Npn \__tblr_log_tracing_command:
+  {
+    \__tblr_prop_log:n { command }
+  }
+
+\cs_new_protected:Npn \__tblr_log_tracing_option:
+  {
+    \__tblr_prop_log:n { note }
+    \__tblr_prop_log:n { remark }
+    \__tblr_prop_log:n { more }
+  }
+
+\cs_new_protected:Npn \__tblr_log_tracing_theme:
+  {
+    \__tblr_style_log:
+  }
+
+\cs_new_protected:Npn \__tblr_log_tracing_outer:
+  {
+    \__tblr_spec_log:n { outer }
+  }
+
+\cs_new_protected:Npn \__tblr_log_tracing_inner:
+  {
+    \__tblr_prop_log:n { inner }
+  }
+
+\cs_new_protected:Npn \__tblr_log_tracing_column:
+  {
+    \__tblr_data_log:n { column }
+  }
+
+\cs_new_protected:Npn \__tblr_log_tracing_row:
+  {
+    \__tblr_data_log:n { row }
+  }
+
+\cs_new_protected:Npn \__tblr_log_tracing_cell:
+  {
+    \__tblr_data_log:n { cell }
+  }
+
+\cs_new_protected:Npn \__tblr_log_tracing_vline:
+  {
+    \__tblr_spec_log:n { vline }
+  }
+
+\cs_new_protected:Npn \__tblr_log_tracing_hline:
+  {
+    \__tblr_spec_log:n { hline }
+  }
+
+\cs_new_protected:Npn \__tblr_log_tracing_colspec:
+  {
+    \tl_if_eq:NnT \g__tblr_column_or_row_tl { column }
+      { \tl_log:N \g__tblr_expanded_colrow_spec_tl }
+  }
+
+\cs_new_protected:Npn \__tblr_log_tracing_rowspec:
+  {
+    \tl_if_eq:NnT \g__tblr_column_or_row_tl { row }
+      { \tl_log:N \g__tblr_expanded_colrow_spec_tl }
+  }
+
+\cs_new_protected:Npn \__tblr_log_tracing_target:
+  {
+    \dim_log:N \l__column_target_dim
+    \prop_log:N \l__column_coefficient_prop
+    \prop_log:N \l__column_natural_width_prop
+    \prop_log:N \l__column_computed_width_prop
+  }
+
+\cs_new_protected:Npn \__tblr_log_tracing_cellspan:
+  {
+    \prop_log:N \l__tblr_col_item_skip_size_prop
+    \prop_log:N \l__tblr_col_span_size_prop
+    \prop_log:N \l__tblr_row_item_skip_size_prop
+    \prop_log:N \l__tblr_row_span_size_prop
+    \prop_log:N \l__tblr_row_span_to_row_prop
+  }
+
+\cs_new_protected:Npn \__tblr_log_tracing_page:
+  {
+    \dim_log:N \pagegoal
+    \dim_log:N \pagetotal
+  }
+
+\cs_new_protected:Npn \__tblr_log_tracing_step:n #1
+  {
+    \bool_if:NT \g__tblr_tracing_step_bool { \tl_log:x {Step :~ #1} }
+  }
+
+\cs_new_protected:Npn \__tblr_do_if_tracing:nn #1 #2
+  {
+    \bool_if:cT { g__tblr_tracing_ #1 _bool } {#2}
+  }
+
+%%% --------------------------------------------------------
+%%> \section{Tabularray Libraries}
+%%% --------------------------------------------------------
+
+%% \NewTblrLibrary and \UseTblrLibrary commands
+
+\NewDocumentCommand \NewTblrLibrary { m m }
+  {
+    \cs_new_protected:cpn { __tblr_use_lib_ #1: } {#2}
+  }
+
+\NewDocumentCommand \UseTblrLibrary { m }
+  {
+    \clist_map_inline:nn {#1}
+      {
+        \use:c { __tblr_use_lib_ ##1: }
+        \cs_undefine:c { __tblr_use_lib_ ##1: }
+      }
+  }
+
+%% Library amsmath and environments +array, +matrix, +cases, ...
+
+\NewTblrLibrary { amsmath }
+  {
+    \RequirePackage { amsmath }
+    \NewTblrEnviron { +array }
+    \SetTblrInner[+array]{colsep = 5pt}
+    \NewDocumentEnvironment { +matrix } { O{} +b } {
+      \begin{+array}{
+        column{1} = {leftsep = 0pt}, column{Z} = {rightsep = 0pt},
+        cells = {c}, ##1
+      }
+        ##2
+      \end{+array}
+    } { }
+    \NewDocumentEnvironment { +bmatrix } { O{} +b } {
+      \begin{+array}{
+        column{1} = {leftsep = 0pt}, column{Z} = {rightsep = 0pt},
+        cells = {c}, delimiter = {left = [, right = ]}, ##1
+      }
+        ##2
+      \end{+array}
+    } { }
+    \NewDocumentEnvironment { +Bmatrix } { O{} +b } {
+      \begin{+array} {
+        column{1} = {leftsep = 0pt}, column{Z} = {rightsep = 0pt},
+        cells = {c}, delimiter = {left = \lbrace, right = \rbrace}, ##1
+      }
+        ##2
+      \end{+array}
+    } { }
+    \NewDocumentEnvironment { +pmatrix } { O{} +b } {
+      \begin{+array} {
+        column{1} = {leftsep = 0pt}, column{Z} = {rightsep = 0pt},
+        cells = {c}, delimiter = {left = (, right = )}, ##1
+      }
+        ##2
+      \end{+array}
+    } { }
+    \NewDocumentEnvironment { +vmatrix } { O{} +b } {
+      \begin{+array} {
+        column{1} = {leftsep = 0pt}, column{Z} = {rightsep = 0pt},
+        cells = {c}, delimiter = {left = \lvert, right = \rvert}, ##1
+      }
+        ##2
+      \end{+array}
+    } { }
+    \NewDocumentEnvironment { +Vmatrix } { O{} +b } {
+      \begin{+array} {
+        column{1} = {leftsep = 0pt}, column{Z} = {rightsep = 0pt},
+        cells = {c}, delimiter = {left = \lVert, right = \rVert}, ##1
+      }
+        ##2
+      \end{+array}
+    } { }
+    \NewDocumentEnvironment { +cases } { O{} +b } {
+      \begin{+array} {
+        column{1} = {leftsep = 0pt}, column{Z} = {rightsep = 0pt},
+        colspec = {ll}, stretch = 1.2, delimiter = {left=\lbrace, right=.}, ##1
+      }
+        ##2
+      \end{+array}
+    } { }
+  }
+
+%% Library booktabs and commands \toprule, \midrule, \bottomrule
+
+\NewTblrLibrary { booktabs }
+  {
+    % We only use dimensions \aboverulesep and \belowrulesep in booktabs package
+    \RequirePackage { booktabs }
+    \newcommand \tblr at booktabs@hline [1] [] { \hline [##1] }
+    \newcommand \tblr at booktabs@oldhline [1] [] {
+      \hline [##1]
+      \hborder { abovespace = \aboverulesep, belowspace = \belowrulesep }
+    }
+    \newcommand \tblr at booktabs@cline [2] [] { \cline [##1] {##2} }
+    \newcommand \tblr at booktabs@oldcline [2] [] {
+      \cline [##1] {##2}
+      \hborder { abovespace = \aboverulesep, belowspace = \belowrulesep }
+    }
+    \newcommand \tblr at booktabs@cline at more [2] [] { \SetHline [+] {##2} {##1} }
+    \newcommand \tblr at booktabs@oldcline at more [2] [] {
+      \SetHline [+] {##2} {##1}
+      \hborder { abovespace = \aboverulesep, belowspace = \belowrulesep }
+    }
+    \NewTableCommand \toprule [1] [] {
+      \tblr at booktabs@hline [wd=\heavyrulewidth, ##1]
+    }
+    \NewTableCommand \midrule [1] [] {
+      \tblr at booktabs@hline [wd=\lightrulewidth, ##1]
+    }
+    \NewTableCommand \bottomrule [1] [] {
+      \tblr at booktabs@hline [wd=\heavyrulewidth, ##1]
+    }
+    \NewTableCommand \cmidrule [2] [] {
+      \tblr at booktabs@cline [wd=\cmidrulewidth, endpos, ##1] {##2}
+    }
+    \NewTableCommand \cmidrulemore [2] [] {
+      \tblr at booktabs@cline at more [wd=\cmidrulewidth, endpos, ##1] {##2}
+    }
+    \newcommand \tblr at booktabs@change at more [1] { \cmidrulemore }
+    \NewTableCommand \morecmidrules {
+      \peek_meaning:NTF \cmidrule { \tblr at booktabs@change at more } { \relax }
+    }
+    \NewTblrEnviron { booktabs }
+    \NewTblrEnviron { longtabs }
+    \NewTblrEnviron { talltabs }
+    \SetTblrInner [ booktabs ] { rowsep = 0pt }
+    \SetTblrInner [ longtabs ] { rowsep = 0pt }
+    \SetTblrInner [ talltabs ] { rowsep = 0pt }
+    \SetTblrOuter [ longtabs ] { long }
+    \SetTblrOuter [ talltabs ] { tall }
+    \RequirePackage { etoolbox }
+    \newcommand \tblr at booktabs@begin at hook
+      {
+        \let \tblr at booktabs@hline = \tblr at booktabs@oldhline
+        \let \tblr at booktabs@cline = \tblr at booktabs@oldcline
+        \let \tblr at booktabs@cline at more = \tblr at booktabs@oldcline at more
+      }
+    \AtBeginEnvironment { booktabs } { \tblr at booktabs@begin at hook }
+    \AtBeginEnvironment { longtabs } { \tblr at booktabs@begin at hook }
+    \AtBeginEnvironment { talltabs } { \tblr at booktabs@begin at hook }
+    \NewTableCommand \specialrule [3]
+      { \hline [##1] \hborder { abovespace = ##2, belowspace = ##3 } }
+    \NewTableCommand \addrowspace [1] [\defaultaddspace]
+      { \hborder { abovespace+ = (##1) / 2, belowspace+ = (##1) / 2 } }
+    \NewTableCommand \addlinespace [1] [\defaultaddspace]
+      { \hborder { abovespace+ = (##1) / 2, belowspace+ = (##1) / 2 } }
+  }
+
+%% Library counter for resetting all counters
+
+\tl_new:N \__tblr_saved_trial_counters_tl
+\tl_new:N \__tblr_saved_cell_counters_tl
+
+\cs_new_protected:Npn \__tblr_save_counters:n #1 { }
+\cs_new_protected:Npn \__tblr_restore_counters:n #1 { }
+
+%% We use code from tabularx package for resetting all LaTeX counters,
+%% where internal macro \cl@@ckpt looks like the following:
+%% \@elt{page} \@elt{equation} \@elt{enumi} \@elt{enumii} \@elt{enumiii} ...
+
+\NewTblrLibrary { counter }
+  {
+    \cs_set_protected:Npn \__tblr_save_counters:n ##1
+      {
+        \def \@elt ####1 { \global\value{####1} = \the\value{####1} \relax }
+        \tl_set:cx { __tblr_saved_ ##1 _counters_tl } { \cl@@ckpt }
+        \let \@elt = \relax
+      }
+    \cs_set_protected:Npn \__tblr_restore_counters:n ##1
+      {
+        \tl_use:c { __tblr_saved_ ##1 _counters_tl }
+      }
+  }
+
+%% Library diagbox and command \diagbox
+
+\NewTblrLibrary { diagbox }
+  {
+    \RequirePackage{ diagbox }
+    \cs_set_eq:NN \__tblr_lib_saved_diagbox:w \diagbox
+    \NewContentCommand \diagbox [3] []
+      {
+        \__tblr_lib_diagbox_fix:n
+          {
+            \__tblr_lib_saved_diagbox:w
+              [ leftsep=\leftsep, rightsep=\rightsep, ##1 ]
+              { \__tblr_lib_diagbox_math_or_text:n {##2} }
+              { \__tblr_lib_diagbox_math_or_text:n {##3} }
+          }
+      }
+    \NewContentCommand \diagboxthree [4] []
+      {
+        \__tblr_lib_diagbox_fix:n
+          {
+            \__tblr_lib_saved_diagbox:w
+              [ leftsep=\leftsep, rightsep=\rightsep, ##1 ]
+              { \__tblr_lib_diagbox_math_or_text:n {##2} }
+              { \__tblr_lib_diagbox_math_or_text:n {##3} }
+              { \__tblr_lib_diagbox_math_or_text:n {##4} }
+          }
+      }
+  }
+
+\cs_new_protected:Npn \__tblr_lib_diagbox_math_or_text:n #1
+  {
+    \bool_if:NTF \l__tblr_cell_math_mode_bool {$#1$} {#1}
+  }
+
+\box_new:N \l__tblr_diag_box
+
+\cs_new_protected:Npn \__tblr_lib_diagbox_fix:n #1
+  {
+    \hbox_set:Nn \l__tblr_diag_box {#1}
+    \box_set_ht:Nn \l__tblr_diag_box { \box_ht:N \l__tblr_diag_box - \abovesep }
+    \box_set_dp:Nn \l__tblr_diag_box { \box_dp:N \l__tblr_diag_box - \belowsep }
+    \box_use:N \l__tblr_diag_box
+  }
+
+%% Library functional with evaluate and process options
+
+\cs_set_eq:NN \__tblr_functional_calculation: \prg_do_nothing:
+
+\NewTblrLibrary { functional }
+  {
+    \RequirePackage { functional }
+    %% Add outer specification "evaluate"
+    \keys_define:nn { tblr-outer }
+      { evaluate .code:n = \__tblr_outer_gput_spec:nn { evaluate } {##1} }
+    \tl_new:N \l__tblr_evaluate_tl
+    \cs_set_protected:Npn \__tblr_hook_split_before:
+      {
+        \tl_set:Nx \l__tblr_evaluate_tl
+          { \__tblr_spec_item:nn { outer } { evaluate } }
+        \tl_if_empty:NF \l__tblr_evaluate_tl
+          {
+            \tl_if_eq:NnTF \l__tblr_evaluate_tl { all }
+              {
+                \tlSet \l__tblr_body_tl { \evalWhole {\expValue \l__tblr_body_tl} }
+              }
+              {
+                \exp_last_unbraced:NNV
+                \__tblr_evaluate_table_body:NN \l__tblr_body_tl \l__tblr_evaluate_tl
+              }
+          }
+      }
+    %% Evaluate every occurrence of the specified function
+    %% Note that funtional package runs every return processor inside a group
+    %% #1: tl with table content; #2: function to be evaluated
+    \tl_new:N \g__tblr_functional_result_tl
+    \cs_new_protected:Npn \__tblr_evaluate_table_body:NN ##1 ##2
+      {
+        \tl_gclear:N \g__tblr_functional_result_tl
+        \cs_set_protected:Npn \__tblr_evaluate_table_body_aux:w ####1 ##2
+          {
+            \tl_gput_right:Nn \g__tblr_functional_result_tl {####1}
+            \peek_meaning:NTF \q_stop { \use_none:n } {##2}
+          }
+        \fun_run_return_processor:nn
+          {
+            \exp_last_unbraced:NV \__tblr_evaluate_table_body_aux:w \gResultTl
+          }
+          {
+            \exp_last_unbraced:NV
+              \__tblr_evaluate_table_body_aux:w ##1 ##2 \q_stop
+          }
+        \tl_set_eq:NN ##1 \g__tblr_functional_result_tl
+      }
+    %% Add inner specification "process"
+    \clist_put_right:Nn \g__tblr_table_known_keys_clist { process }
+    \keys_define:nn { tblr }
+      { process .code:n = \__tblr_keys_gput:nn { process } {##1} }
+    \cs_set:Npn \__tblr_functional_calculation:
+      {
+        \LogTblrTracing { step = do ~ functional ~ calculation }
+        \__tblr_prop_item:nn { inner } { process }
+      }
+    \prgNewFunction \cellGetText { m m }
+      {
+        \expWhole { \__tblr_spec_item:nn { text } { [##1][##2] } }
+      }
+    \prgNewFunction \cellSetText { m m m }
+      {
+        \__tblr_spec_gput:nnn { text } { [##1][##2] } {##3}
+      }
+    \prgNewFunction \cellSetStyle { m m m }
+      {
+        \tblr_set_cell:nnnn {##1} {##2} {} {##3}
+      }
+    \prgNewFunction \rowSetStyle { m m }
+      {
+        \tblr_set_row:nnn {##1} {} {##2}
+      }
+    \prgNewFunction \columnSetStyle { m m }
+      {
+        \tblr_set_column:nnn {##1} {} {##2}
+      }
+  }
+
+%% Library hook provides some public hooks
+
+\cs_new_protected:Npn \__tblr_hook_use:n #1 {}
+
+\NewTblrLibrary { hook }
+  {
+    \cs_set_eq:NN \__tblr_hook_use:n \hook_use:n
+    \hook_new_pair:nn { tabularray/trial/before } { tabularray/trial/after }
+    \hook_new_pair:nn { tabularray/table/before } { tabularray/table/after }
+    \hook_new_pair:nn { tabularray/row/before } { tabularray/row/after }
+    \hook_new_pair:nn { tabularray/cell/before } { tabularray/cell/after }
+  }
+
+%% Library html provides more public variables
+%% These variables can be used by tagpdf, tex4ht and lwarp packages
+
+\bool_new:N \l__tblr_html_variables_bool
+
+\NewTblrLibrary { html }
+  {
+    \bool_set_true:N \l__tblr_html_variables_bool
+  }
+
+%% Library nameref and its caption-ref template
+
+\NewTblrLibrary { nameref }
+  {
+    \RequirePackage { nameref }
+    \clist_if_in:NnF \lTblrRefMoreClist { nameref }
+      {
+        \clist_put_right:Nn \lTblrRefMoreClist { nameref }
+        \DefTblrTemplate { caption-ref }{ nameref }
+          {
+            \tl_if_eq:NnTF \lTblrEntryTl { none }
+              { \exp_args:NV \GetTitleString \lTblrCaptionTl }
+              {
+                \tl_if_empty:NTF \lTblrEntryTl
+                  { \exp_args:NV \GetTitleString \lTblrCaptionTl }
+                  { \exp_args:NV \GetTitleString \lTblrEntryTl }
+              }
+            \tl_set_eq:NN \@currentlabelname \GetTitleStringResult
+          }
+      }
+  }
+
+%% Library siunitx and S columns
+
+\NewTblrLibrary { siunitx }
+  {
+    \RequirePackage { siunitx }
+    \NewColumnType { S } [1] [] { Q[si = {##1}, c] }
+    \NewColumnType { s } [1] [] { Q[si = {##1}, c, cmd = \TblrUnit] }
+    \__tblr_data_new_key:nnn { cell } { si } { str }
+    \keys_define:nn { tblr-column }
+      {
+        si .code:n = \__tblr_siunitx_setcolumn:n {##1}
+      }
+    \cs_new_protected:Npn \__tblr_siunitx_setcolumn:n ##1
+      {
+        \__tblr_column_gput_cell:nn { si } {##1}
+        \__tblr_column_gput_cell:nn { cmd } { \TblrNum }
+      }
+    \NewDocumentCommand \TblrNum { m }
+      {
+        \__tblr_siunitx_process:Nn \tablenum {##1}
+      }
+    \NewDocumentCommand \TblrUnit { m }
+      {
+        \__tblr_siunitx_process:Nn \si {##1}
+      }
+    \cs_new_protected:Npn \__tblr_siunitx_process:Nn ##1 ##2
+      {
+        \tl_if_head_is_group:nTF {##2}
+          { ##2 }
+          {
+            \group_begin:
+            \tl_set:Nx \l_tmpa_tl
+              {
+                \__tblr_data_item:neen { cell }
+                  { \int_use:N \c at rownum } { \int_use:N \c at colnum } { si }
+              }
+            \exp_args:NV \sisetup \l_tmpa_tl
+            ##1 {##2}
+            \group_end:
+          }
+      }
+    \keys_define:nn { tblr-cell-spec } { guard .meta:n = { cmd = } }
+    \keys_define:nn { tblr-row }       { guard .meta:n = { cmd = } }
+    \keys_define:nn { tblr-column }    { guard .meta:n = { cmd = } }
+  }
+
+%% Library varwidth and measure option
+
+\NewTblrLibrary { varwidth }
+  {
+    \RequirePackage { varwidth }
+    \clist_gput_left:Nn \g__tblr_table_known_keys_clist { measure }
+    \keys_define:nn { tblr } { measure .tl_set:N = \l__tblr_inner_spec_measure_tl }
+  }
+
+%% Library zref and its caption-ref template
+
+\NewTblrLibrary { zref }
+  {
+    \RequirePackage { zref-user }
+    \clist_if_in:NnF \lTblrRefMoreClist { zref }
+      {
+        \clist_put_right:Nn \lTblrRefMoreClist { zref }
+        \DefTblrTemplate { caption-ref }{ zref }
+          {
+            \exp_args:NV \zlabel \lTblrLabelTl
+          }
+      }
+  }
+


Property changes on: trunk/Master/texmf-dist/tex/latex/tabularray/tabularray-2024.sty
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Modified: trunk/Master/texmf-dist/tex/latex/tabularray/tabularray.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/tabularray/tabularray.sty	2025-03-11 20:05:45 UTC (rev 74568)
+++ trunk/Master/texmf-dist/tex/latex/tabularray/tabularray.sty	2025-03-11 20:06:02 UTC (rev 74569)
@@ -1,13 +1,15 @@
 %%% % -*- coding: utf-8 -*-
 %%% ----------------------------------------------------------------------------
-%%% Tabularray: Typeset tabulars and arrays with LaTeX3
-%%% Copyright : 2021-2023 (c) Jianrui Lyu <tolvjr at 163.com>
-%%% Repository: https://github.com/lvjr/tabularray
-%%% License   : The LaTeX Project Public License 1.3c
+%%% Caption     : Typeset tabulars and arrays with LaTeX3
+%%% Author      : Jianrui Lyu <tolvjr at 163.com>
+%%% Collaborator: Yukai Chou <muzimuzhi at gmail.com>
+%%% CTAN Page   : https://ctan.org/pkg/tabularray
+%%% Repository  : https://github.com/lvjr/tabularray
+%%% License     : The LaTeX Project Public License 1.3c
 %%% ----------------------------------------------------------------------------
 
 %%% --------------------------------------------------------
-%%> \section{Scratch Variables and Function Variants}
+%%> \section{Scratch variables and function variants}
 %%% --------------------------------------------------------
 
 %% \DeclareRelease and \DeclareCurrentRelease are added in LaTeX 2018-04-01
@@ -14,11 +16,11 @@
 \NeedsTeXFormat{LaTeX2e}[2018-04-01]
 \providecommand\DeclareRelease[3]{}
 \providecommand\DeclareCurrentRelease[2]{}
+\DeclareRelease{v2024}{2022-01-01}{tabularray-2024.sty}
 \DeclareRelease{v2021}{2021-01-01}{tabularray-2021.sty}
-\DeclareCurrentRelease{}{2022-01-01}
+\DeclareCurrentRelease{}{2025-01-01}
 
-\RequirePackage{expl3}
-\ProvidesExplPackage{tabularray}{2024-02-16}{2024A}
+\ProvidesExplPackage{tabularray}{2025-03-11}{2025A}
   {Typeset tabulars and arrays with LaTeX3}
 
 %% \IfFormatAtLeastTF, xparse and lthooks are added in LaTeX 2020-10-01
@@ -26,23 +28,18 @@
 \msg_new:nnn { tabularray } { latex-too-old }
   {
     Your ~ LaTeX ~ release ~ is ~ too ~ old. \\
-    Please ~ update ~ it ~ to ~ 2020-10-01 ~ first.
+    Please ~ update ~ it ~ to ~ 2022-11-01 ~ first.
   }
-\@ifl at t@r\fmtversion{2020-10-01}{}{
-  %% Support TeX Live 2020 on Overleaf
-  \msg_warning:nn { tabularray } { latex-too-old }
-  \usepackage{xparse}
+\@ifl at t@r\fmtversion{2022-11-01}{}{
+  \msg_error:nn { tabularray } { latex-too-old }
 }
 
-\AtBeginDocument{
-  \@ifpackageloaded{xcolor}{\RequirePackage{ninecolors}}{}
-  \@ifpackageloaded{hyperref}{
-    \newenvironment{tblrNoHyper}{\NoHyper}{\endNoHyper}
-  }{
-    \newenvironment{tblrNoHyper}{}{}
-  }
-}
+\AddToHook {package/xcolor/after} [tabularray] { \RequirePackage{ninecolors} }
 
+\newenvironment{tblrNoHyper}{}{}
+\AddToHook {package/hyperref/after} [tabularray]
+  { \renewenvironment{tblrNoHyper}{\NoHyper}{\endNoHyper} }
+
 \NewDocumentCommand \TblrParboxRestore { } { \@parboxrestore }
 
 \NewDocumentCommand \TblrAlignBoth { }
@@ -83,20 +80,42 @@
     \fi:
   }
 
-\cs_generate_variant:Nn \msg_error:nnnn { nnVn }
+\cs_generate_variant:Nn \msg_error:nnnnn { nnnVV }
+\cs_generate_variant:Nn \seq_map_indexed_inline:Nn { cn }
+\cs_generate_variant:Nn \seq_set_split:Nnn { NVe }
+\cs_generate_variant:Nn \seq_set_split_keep_spaces:Nnn { Ne }
+\cs_generate_variant:Nn \seq_use:Nn { Ne }
+\cs_generate_variant:Nn \tl_gput_right:Nn { Nf }
+\cs_generate_variant:Nn \tl_rescan:nn { ne }
+\prg_generate_conditional_variant:Nnn \clist_if_in:Nn { Ne } { TF }
+\prg_generate_conditional_variant:Nnn \str_if_eq:nn { en } { TF }
+\prg_generate_conditional_variant:Nnn \str_if_in:Nn { Ne } { TF }
+
+%% Add missing function variants for texlive 2022. They can be removed in 2026.
+\cs_generate_variant:Nn \clist_gput_right:Nn { ce }
+\cs_generate_variant:Nn \clist_put_right:Nn { ce }
+\cs_generate_variant:Nn \clist_set:Nn { Ne, ce }
+\cs_generate_variant:Nn \keyval_parse:NNn { NNV }
+\cs_generate_variant:Nn \msg_error:nnn { nnV }
+\cs_generate_variant:Nn \msg_error:nnnn { nnV }
 \cs_generate_variant:Nn \prop_item:Nn { Ne, NV }
-\cs_generate_variant:Nn \prop_put:Nnn { Nxn, Nxx, NxV }
-\cs_generate_variant:Nn \regex_replace_all:NnN { NVN }
-\cs_generate_variant:Nn \seq_map_indexed_inline:Nn { cn }
+\cs_generate_variant:Nn \prop_put:Nnn { Nne, Nen, Nee, NeV }
+\cs_generate_variant:Nn \seq_put_right:Nn { Ne }
+\cs_generate_variant:Nn \seq_set_split:Nnn { Nne, NVn }
+\cs_generate_variant:Nn \str_gset:Nn { Ne }
 \cs_generate_variant:Nn \tl_const:Nn { ce }
-\cs_generate_variant:Nn \tl_log:n { x }
-\cs_generate_variant:Nn \tl_gput_right:Nn { Nf }
-\cs_generate_variant:Nn \tl_put_left:Nn { Nv }
-\prg_generate_conditional_variant:Nnn \clist_if_in:Nn { Nx } { TF }
+\cs_generate_variant:Nn \tl_gput_right:Nn { Ne }
+\cs_generate_variant:Nn \tl_gset:Nn { Ne, ce }
+\cs_generate_variant:Nn \tl_log:n { e }
+\cs_generate_variant:Nn \tl_put_left:Nn { Ne, Nv }
+\cs_generate_variant:Nn \tl_put_right:Nn { Ne }
+\cs_generate_variant:Nn \tl_set:Nn { Ne, ce }
+\cs_generate_variant:Nn \tl_set_rescan:Nnn { Nne, NnV }
+\cs_generate_variant:Nn \tl_to_str:n { e }
 \prg_generate_conditional_variant:Nnn \prop_if_in:Nn { c } { T }
-\prg_generate_conditional_variant:Nnn \regex_match:Nn { NV } { TF }
-\prg_generate_conditional_variant:Nnn \str_if_eq:nn { xn } { TF }
 \prg_generate_conditional_variant:Nnn \tl_if_eq:nn { en } { T, TF }
+\prg_generate_conditional_variant:Nnn \tl_if_in:nn { nV } { TF }
+\prg_generate_conditional_variant:Nnn \tl_if_in:Nn { Ne } { TF }
 \prg_generate_conditional_variant:Nnn \tl_if_head_eq_catcode:nN { VN } { TF }
 \prg_generate_conditional_variant:Nnn \tl_if_head_eq_meaning:nN { VN } { T, TF }
 
@@ -138,9 +157,6 @@
 \box_new:N \l__tblr_c_box % for cell box
 \box_new:N \l__tblr_d_box
 
-%% Total number of tblr tables
-\int_new:N \g__tblr_table_count_int
-
 %% Some commands for horizontal alignment
 \cs_new_eq:NN \__tblr_halign_command_j: \TblrAlignBoth
 \cs_new_eq:NN \__tblr_halign_command_l: \TblrAlignLeft
@@ -147,6 +163,11 @@
 \cs_new_eq:NN \__tblr_halign_command_c: \TblrAlignCenter
 \cs_new_eq:NN \__tblr_halign_command_r: \TblrAlignRight
 
+%% Total number of tblr tables, used for creating hyperref targets and tikz nodes.
+%% We need to save and restore it before and after measuring stage respectively,
+%% so we must define it with \newcounter command.
+\newcounter { tblrcount }
+
 %% Some counters for row and column numbering.
 %% We may need to restore all LaTeX counters in measuring and building cells,
 %% so we must not define these counters with \newcounter command.
@@ -186,67 +207,312 @@
   }
 
 %%% --------------------------------------------------------
-%%> \section{Data Structures Based on Property Lists}
+%%> \section{Functions for splitting, extracting and matching}
 %%% --------------------------------------------------------
 
-\int_new:N \g_tblr_level_int % store table nesting level
+\str_const:Nn \c__tblr_left_bracket_str { [ }
 
+\cs_new_protected:Npn \__tblr_split_before_brace:NNn #1 #2 #3
+  {
+    \__tblr_split_before:NNVn #1 #2 \c_left_brace_str {#3}
+  }
+
+\cs_new_protected:Npn \__tblr_split_before_bracket:NNn #1 #2 #3
+  {
+    \__tblr_split_before:NNVn #1 #2 \c__tblr_left_bracket_str {#3}
+  }
+\cs_generate_variant:Nn \__tblr_split_before_bracket:NNn { NNV }
+
+%% Split tl #2 as two parts: sub tl before first #3 and sub tl from first #1.
+%% And the results are stored in tl vars #1 and #2, respectively.
+\cs_new_protected:Npn \__tblr_split_before:NNnn #1 #2 #3 #4
+  {
+    \cs_set_protected:Npn \__tblr_split_before_auxa:ww ##1 #3 ##2 \q_stop
+      {
+        \tl_set:Nn #1 { ##1 }
+        \tl_if_empty:nTF { ##2 }
+          { \tl_set:Nn #2 { ##2 } } { \__tblr_split_before_auxb:w ##2 \q_stop }
+      }
+    \cs_set_protected:Npn \__tblr_split_before_auxb:w ##1 #3 \q_stop
+      {
+        \tl_set:Nn #2 { #3 ##1 }
+      }
+    \__tblr_split_before_auxa:ww #4 #3 \q_stop
+  }
+\cs_generate_variant:Nn \__tblr_split_before:NNnn { NNV }
+
+%% The name of a child indexer must start with uppercase letter.
+%% The name of a child selector must start with lowercase letter.
+
+\tl_const:Nn \c__tblr_upper_letter_tl { ABCDEFGHIJKLMNOPQRSTUVWXYZ }
+\tl_const:Nn \c__tblr_lower_letter_tl { abcdefghijklmnopqrstuvwxyz }
+\tl_const:Nn \c__tblr_digit_str { 0123456789 }
+
+\prg_new_protected_conditional:Npnn \__tblr_if_head_upper:n #1 { TF }
+  {
+    \tl_if_in:NeTF \c__tblr_upper_letter_tl { \tl_head:n { #1 } }
+      { \prg_return_true: } { \prg_return_false: }
+  }
+
+\prg_new_protected_conditional:Npnn \__tblr_if_head_lower:n #1 { TF }
+  {
+    \tl_if_in:NeTF \c__tblr_lower_letter_tl { \tl_head:n { #1 } }
+      { \prg_return_true: } { \prg_return_false: }
+  }
+
+\prg_new_protected_conditional:Npnn \__tblr_tl_if_upper:n #1 { TF }
+  {
+    \tl_if_in:NnTF \c__tblr_upper_letter_tl { #1 }
+      { \prg_return_true: } { \prg_return_false: }
+  }
+\prg_generate_conditional_variant:Nnn \__tblr_tl_if_upper:n { V } { TF }
+
+\prg_new_protected_conditional:Npnn \__tblr_tl_if_lower:n #1 { TF }
+  {
+    \tl_if_in:NnTF \c__tblr_lower_letter_tl { #1 }
+      { \prg_return_true: } { \prg_return_false: }
+  }
+\prg_generate_conditional_variant:Nnn \__tblr_tl_if_lower:n { V } { TF }
+
+\prg_new_protected_conditional:Npnn \__tblr_tl_if_digit:n #1 { F, TF }
+  {
+    \tl_if_in:NnTF \c__tblr_digit_tl { #1 }
+      { \prg_return_true: } { \prg_return_false: }
+  }
+\prg_generate_conditional_variant:Nnn \__tblr_tl_if_digit:n { e, V } { F, TF }
+
+%% When the key name is omitted, we need to detect the name from its value.
+
+\str_const:Nn \c__tblr_letter_str
+  { ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz }
+
+%% It is enough to check only the first item
+\prg_new_protected_conditional:Npnn \__tblr_if_color_value:n #1 { T, F, TF }
+  {
+    \str_if_in:NeTF \c__tblr_letter_str { \tl_head:n { #1 } }
+      { \prg_return_true: } { \prg_return_false: }
+  }
+
+\str_const:Nn \c__tblr_number_str { 0123456789+-.~ }
+\bool_new:N \l__tblr_if_number_bool
+
+%% The number value could be '25.6 - 3.14'.
+\prg_new_protected_conditional:Npnn \__tblr_if_number_value:n #1 { T, F, TF }
+  {
+    \bool_set_true:N \l__tblr_if_number_bool
+    \tl_map_inline:nn { #1 }
+      {
+        \str_if_in:NnF \c__tblr_number_str { ##1 }
+          {
+            \bool_set_false:N \l__tblr_if_number_bool
+            \tl_map_break:
+          }
+      }
+    \bool_if:NTF \l__tblr_if_number_bool
+      { \prg_return_true: } { \prg_return_false: }
+  }
+
+\tl_const:Nn \c__tblr_digit_tl { 0123456789 }
+
+%% It is enough to check only the first item
+\prg_new_protected_conditional:Npnn \__tblr_if_positive_value:n #1 { T, F, TF }
+  {
+    \tl_if_in:NeTF \c__tblr_digit_tl { \tl_head:n { #1 } }
+      { \prg_return_true: } { \prg_return_false: }
+  }
+\prg_generate_conditional_variant:Nnn \__tblr_if_positive_value:n { V } { TF }
+
+%% It is enough to check only the first item
+\prg_new_protected_conditional:Npnn \__tblr_if_negative_value:n #1 { T, F, TF }
+  {
+    \tl_if_eq:enTF { \tl_head:n { #1 } } { - }
+      { \prg_return_true: } { \prg_return_false: }
+  }
+\prg_generate_conditional_variant:Nnn \__tblr_if_negative_value:n { V } { TF }
+
+%%% --------------------------------------------------------
+%%> \section{Declare and set tabularray keys}
+%%% --------------------------------------------------------
+
+\cs_new_protected:Npn \__tblr_keys_define:nn #1 #2
+  {
+    \keys_define:nn { tabularray/#1 } {#2}
+  }
+
+\cs_set_eq:NN \DeclareTblrKeys \__tblr_keys_define:nn
+
+\cs_new_protected:Npn \__tblr_keys_set:nn #1 #2
+  {
+    \keys_set:nn { tabularray/#1 } {#2}
+  }
+\cs_generate_variant:Nn \__tblr_keys_set:nn { nV, nv, ne }
+
+\cs_set_eq:NN \SetTblrKeys \__tblr_keys_set:nn
+
+\cs_new_protected:Npn \__tblr_keys_set_groups:nnn #1 #2 #3
+  {
+    \keys_set_groups:nnn { tabularray/#1 } {#2} {#3}
+  }
+
+\prg_set_conditional:Npnn \__tblr_keys_if_exist:nn #1 #2 { p, T, F, TF }
+  {
+    \keys_if_exist:nnTF { tabularray/#1 } { #2 }
+      { \prg_return_true: } { \prg_return_false: }
+  }
+\prg_generate_conditional_variant:Nnn \__tblr_keys_if_exist:nn { nV } { TF }
+
+%% A special key such as cell{i}{j} or note{a} consists of name and args.
+%% These functions extracts two components of a special key as string #1.
+
+\seq_new:N \l__tblr_key_split_seq
+\str_new:N \l__tblr_key_split_name_str
+\tl_new:N \l__tblr_key_split_args_tl
+
+\cs_new_protected:Npn \__tblr_key_split_name:n #1
+  {
+    \seq_set_split_keep_spaces:Nen \l__tblr_key_split_seq \c_left_brace_str {#1}
+    \seq_pop_left:NN \l__tblr_key_split_seq \l__tblr_key_split_name_str
+    %\str_log:N \l__tblr_key_split_name_str
+  }
+\cs_generate_variant:Nn \__tblr_key_split_name:n { e }
+
+\cs_new_protected:Npn \__tblr_key_split_name_args:n #1
+  {
+    \__tblr_key_split_name:n {#1}
+    \seq_if_empty:NTF \l__tblr_key_split_seq
+      { \tl_clear:N \l__tblr_key_split_args_tl }
+      {
+        \tl_set_rescan:Nne \l__tblr_key_split_args_tl {}
+          {
+            \c_left_brace_str
+            \seq_use:Ne \l__tblr_key_split_seq { \c_left_brace_str }
+          }
+      }
+  }
+
+%% To distinguish between modern table specs and traditional column specs,
+%% we need to extract first key name from tl #1 which is an argument of tblr.
+
+\cs_new_protected:Npn \__tblr_keyval_extract_first_name:n #1
+  {
+    \seq_set_split:Nnn \l__tblr_key_split_seq {,} {#1}
+    \seq_set_split:Nne \l__tblr_key_split_seq {=}
+      { \seq_item:Nn \l__tblr_key_split_seq {1} }
+    \__tblr_key_split_name:e
+      { \tl_to_str:e { \seq_item:Nn \l__tblr_key_split_seq {1} } }
+  }
+
+%%% --------------------------------------------------------
+%%> \section{Create and use tabularray hooks}
+%%% --------------------------------------------------------
+
+\cs_new_protected:Npn \__tblr_hook_new:n #1
+  {
+    \hook_new:n { tabularray/#1 }
+  }
+
+\cs_new_protected:Npn \__tblr_hook_new_pair:nn #1 #2
+  {
+    \hook_new_pair:nn { tabularray/#1 } { tabularray/#2 }
+  }
+
+\cs_new_protected:Npn \__tblr_hook_use_false:n #1 {}
+
+\cs_new_protected:Npn \__tblr_hook_use_true:n #1
+  {
+    \hook_use:n { tabularray/#1 }
+  }
+
+\cs_set_eq:NN \__tblr_hook_use:n \__tblr_hook_use_false:n
+
+\NewDocumentCommand \AddToTblrHook { m o +m }
+  {
+    \hook_gput_code:nnn  { tabularray/#1 } { #2 } { #3 }
+  }
+
+\NewDocumentCommand \AddToTblrHookNext { m +m }
+  {
+    \hook_gput_next_code:nn  { tabularray/#1 } { #2 }
+  }
+
+%%% --------------------------------------------------------
+%%> \section{Data structures based on property lists}
+%%% --------------------------------------------------------
+
+\int_new:N \gTblrLevelInt % store table nesting level
+
+%% \g_tblr_level_int is deprecated and will be removed later
+\cs_set_eq:NN \g_tblr_level_int \gTblrLevelInt
+
 \cs_new_protected:Npn \__tblr_clear_prop_lists:
   {
-    \prop_gclear_new:c { g__tblr_text_ \int_use:N \g_tblr_level_int _prop }
-    \prop_gclear_new:c { g__tblr_command_ \int_use:N \g_tblr_level_int _prop }
-    \prop_gclear_new:c { g__tblr_inner_ \int_use:N \g_tblr_level_int _prop }
-    \prop_gclear_new:c { g__tblr_note_ \int_use:N \g_tblr_level_int _prop }
-    \prop_gclear_new:c { g__tblr_remark_ \int_use:N \g_tblr_level_int _prop }
-    \prop_gclear_new:c { g__tblr_more_ \int_use:N \g_tblr_level_int _prop }
-    \prop_gclear_new:c { g__tblr_row_ \int_use:N \g_tblr_level_int _prop }
-    \prop_gclear_new:c { g__tblr_column_ \int_use:N \g_tblr_level_int _prop }
-    \prop_gclear_new:c { g__tblr_cell_ \int_use:N \g_tblr_level_int _prop }
-    \prop_gclear_new:c { g__tblr_hline_ \int_use:N \g_tblr_level_int _prop }
-    \prop_gclear_new:c { g__tblr_vline_ \int_use:N \g_tblr_level_int _prop }
+    \__tblr_prop_gclear_new:c
+      { g__tblr_text_ \int_use:N \gTblrLevelInt _prop }
+    \__tblr_prop_gclear_new:c
+      { g__tblr_command_ \int_use:N \gTblrLevelInt _prop }
+    \__tblr_prop_gclear_new:c
+      { g__tblr_inner_ \int_use:N \gTblrLevelInt _prop }
+    \__tblr_prop_gclear_new:c
+      { g__tblr_outer_ \int_use:N \gTblrLevelInt _prop }
+    \__tblr_prop_gclear_new:c
+      { g__tblr_note_ \int_use:N \gTblrLevelInt _prop }
+    \__tblr_prop_gclear_new:c
+      { g__tblr_remark_ \int_use:N \gTblrLevelInt _prop }
+    \__tblr_prop_gclear_new:c
+      { g__tblr_more_ \int_use:N \gTblrLevelInt _prop }
+    \__tblr_prop_gclear_new:c
+      { g__tblr_row_ \int_use:N \gTblrLevelInt _prop }
+    \__tblr_prop_gclear_new:c
+      { g__tblr_column_ \int_use:N \gTblrLevelInt _prop }
+    \__tblr_prop_gclear_new:c
+      { g__tblr_cell_ \int_use:N \gTblrLevelInt _prop }
+    \__tblr_prop_gclear_new:c
+      { g__tblr_hline_ \int_use:N \gTblrLevelInt _prop }
+    \__tblr_prop_gclear_new:c
+      { g__tblr_vline_ \int_use:N \gTblrLevelInt _prop }
   }
 
 \cs_new_protected:Npn \__tblr_prop_gput:nnn #1 #2 #3
   {
     \prop_gput:cnn
-      { g__tblr_#1_ \int_use:N \g_tblr_level_int _prop } { #2 } { #3 }
+      { g__tblr_#1_ \int_use:N \gTblrLevelInt _prop } { #2 } { #3 }
   }
-\cs_generate_variant:Nn \__tblr_prop_gput:nnn { nnx, nnV, nxn, nxx, nxV }
+\cs_generate_variant:Nn \__tblr_prop_gput:nnn { nne, nnV, nen, nee, neV }
 
 \cs_new:Npn \__tblr_prop_item:nn #1 #2
   {
-    \prop_item:cn { g__tblr_#1_ \int_use:N \g_tblr_level_int _prop } { #2 }
+    \prop_item:cn { g__tblr_#1_ \int_use:N \gTblrLevelInt _prop } { #2 }
   }
 \cs_generate_variant:Nn \__tblr_prop_item:nn { ne }
 
 \cs_new_protected:Npn \__tblr_prop_if_in:nnT #1
   {
-    \prop_if_in:cnT { g__tblr_#1_ \int_use:N \g_tblr_level_int _prop }
+    \prop_if_in:cnT { g__tblr_#1_ \int_use:N \gTblrLevelInt _prop }
   }
 \cs_new_protected:Npn \__tblr_prop_if_in:nnF #1
   {
-    \prop_if_in:cnF { g__tblr_#1_ \int_use:N \g_tblr_level_int _prop }
+    \prop_if_in:cnF { g__tblr_#1_ \int_use:N \gTblrLevelInt _prop }
   }
 \cs_new_protected:Npn \__tblr_prop_if_in:nnTF #1
   {
-    \prop_if_in:cnTF { g__tblr_#1_ \int_use:N \g_tblr_level_int _prop }
+    \prop_if_in:cnTF { g__tblr_#1_ \int_use:N \gTblrLevelInt _prop }
   }
-\prg_generate_conditional_variant:Nnn \__tblr_prop_if_in:nn { nx } { T, F, TF }
+\prg_generate_conditional_variant:Nnn \__tblr_prop_if_in:nn { ne } { T, F, TF }
 
 \cs_new_protected:Npn \__tblr_prop_log:n #1
   {
-    \prop_log:c { g__tblr_#1_ \int_use:N \g_tblr_level_int _prop }
+    \prop_log:c { g__tblr_#1_ \int_use:N \gTblrLevelInt _prop }
   }
 
 \cs_new_protected:Npn \__tblr_prop_map_inline:nn #1 #2
   {
-    \prop_map_inline:cn { g__tblr_#1_ \int_use:N \g_tblr_level_int _prop } {#2}
+    \prop_map_inline:cn { g__tblr_#1_ \int_use:N \gTblrLevelInt _prop } {#2}
   }
 
 \cs_new_protected:Npn \__tblr_prop_gput_if_larger:nnn #1 #2 #3
   {
     \__tblr_gput_if_larger:cnn
-      { g__tblr_#1_ \int_use:N \g_tblr_level_int _prop } { #2 } { #3 }
+      { g__tblr_#1_ \int_use:N \gTblrLevelInt _prop } { #2 } { #3 }
   }
 \cs_generate_variant:Nn \__tblr_prop_gput_if_larger:nnn { nnx, nnV, nxn, nxx, nxV }
 
@@ -253,7 +519,7 @@
 \cs_new_protected:Npn \__tblr_prop_gadd_dimen_value:nnn #1 #2 #3
   {
     \__tblr_gadd_dimen_value:cnn
-      { g__tblr_#1_ \int_use:N \g_tblr_level_int _prop } { #2 } { #3 }
+      { g__tblr_#1_ \int_use:N \gTblrLevelInt _prop } { #2 } { #3 }
   }
 \cs_generate_variant:Nn \__tblr_prop_gadd_dimen_value:nnn { nnx, nnV, nxn, nxx }
 
@@ -263,23 +529,23 @@
 
 \cs_new_protected:Npn \__tblr_put_if_larger:Nnn #1 #2 #3
   {
-    \tl_set:Nx \l__tblr_put_if_larger_tl { \prop_item:Nn #1 { #2 } }
+    \tl_set:Ne \l__tblr_put_if_larger_tl { \prop_item:Nn #1 { #2 } }
     \bool_lazy_or:nnT
       { \tl_if_empty_p:N \l__tblr_put_if_larger_tl }
       { \dim_compare_p:nNn { #3 } > { \l__tblr_put_if_larger_tl } }
       { \prop_put:Nnn #1 { #2 } { #3 }  }
   }
-\cs_generate_variant:Nn \__tblr_put_if_larger:Nnn { Nnx, Nxn, Nxx, NnV }
+\cs_generate_variant:Nn \__tblr_put_if_larger:Nnn { Nee, NnV }
 
 \cs_new_protected:Npn \__tblr_gput_if_larger:Nnn #1 #2 #3
   {
-    \tl_set:Nx \l__tblr_put_if_larger_tl { \prop_item:Nn #1 { #2 } }
+    \tl_set:Ne \l__tblr_put_if_larger_tl { \prop_item:Nn #1 { #2 } }
     \bool_lazy_or:nnT
       { \tl_if_empty_p:N \l__tblr_put_if_larger_tl }
       { \dim_compare_p:nNn { #3 } > { \l__tblr_put_if_larger_tl } }
       { \prop_gput:Nnn #1 { #2 } { #3 }  }
   }
-\cs_generate_variant:Nn \__tblr_gput_if_larger:Nnn { Nnx, Nxn, Nxx, cnn }
+\cs_generate_variant:Nn \__tblr_gput_if_larger:Nnn { cnn }
 
 %% Add the dimension to some key value of the prop list
 %% #1: the prop list, #2: the key, #3: the dimen to add
@@ -286,18 +552,18 @@
 
 \cs_new_protected:Npn \__tblr_add_dimen_value:Nnn #1 #2 #3
   {
-    \prop_put:Nnx #1 { #2 } { \dim_eval:n { \prop_item:Nn #1 { #2 } + #3 } }
+    \prop_put:Nne #1 { #2 } { \dim_eval:n { \prop_item:Nn #1 { #2 } + #3 } }
   }
 \cs_generate_variant:Nn \__tblr_add_dimen_value:Nnn { cnn }
 
 \cs_new_protected:Npn \__tblr_gadd_dimen_value:Nnn #1 #2 #3
   {
-    \prop_gput:Nnx #1 { #2 } { \dim_eval:n { \prop_item:Nn #1 { #2 } + #3 } }
+    \prop_gput:Nne #1 { #2 } { \dim_eval:n { \prop_item:Nn #1 { #2 } + #3 } }
   }
 \cs_generate_variant:Nn \__tblr_gadd_dimen_value:Nnn { cnn }
 
 %%% --------------------------------------------------------
-%%> \section{Data Structures Based on Token Lists}
+%%> \section{Data structures based on token lists}
 %%% --------------------------------------------------------
 
 \cs_new_protected:Npn \__tblr_clear_spec_lists:
@@ -313,30 +579,32 @@
 
 \cs_new_protected:Npn \__tblr_clear_one_spec_lists:n #1
   {
-    \clist_if_exist:cTF { g__tblr_#1_ \int_use:N \g_tblr_level_int _clist }
+    \clist_if_exist:cTF { g__tblr_#1_ \int_use:N \gTblrLevelInt _clist }
       {
-        \clist_map_inline:cn { g__tblr_#1_ \int_use:N \g_tblr_level_int _clist }
+        \clist_map_inline:cn { g__tblr_#1_ \int_use:N \gTblrLevelInt _clist }
           {
-            \tl_gclear:c { g__tblr_spec_ \int_use:N \g_tblr_level_int _#1_##1_tl }
+            \tl_gclear_new:c { g__tblr_spec_ \int_use:N \gTblrLevelInt _#1_##1_tl }
           }
       }
-      { \clist_new:c { g__tblr_#1_ \int_use:N \g_tblr_level_int _clist } }
+      { \clist_new:c { g__tblr_#1_ \int_use:N \gTblrLevelInt _clist } }
   }
 
 \cs_new_protected:Npn \__tblr_spec_gput:nnn #1 #2 #3
   {
+    \tl_if_exist:cF { g__tblr_spec_ \int_use:N \gTblrLevelInt _#1_#2_tl }
+      { \tl_new:c { g__tblr_spec_ \int_use:N \gTblrLevelInt _#1_#2_tl } }
     \tl_gset:cn
-      { g__tblr_spec_ \int_use:N \g_tblr_level_int _#1_#2_tl } {#3}
-    \clist_gput_right:cx { g__tblr_#1_ \int_use:N \g_tblr_level_int _clist } {#2}
+      { g__tblr_spec_ \int_use:N \gTblrLevelInt _#1_#2_tl } {#3}
+    \clist_gput_right:ce { g__tblr_#1_ \int_use:N \gTblrLevelInt _clist } {#2}
   }
 \cs_generate_variant:Nn \__tblr_spec_gput:nnn { nne, nnV, nen, nee, neV }
 
 \cs_new:Npn \__tblr_spec_item:nn #1 #2
   {
-    \tl_if_exist:cT { g__tblr_spec_ \int_use:N \g_tblr_level_int _#1_#2_tl }
+    \tl_if_exist:cT { g__tblr_spec_ \int_use:N \gTblrLevelInt _#1_#2_tl }
       {
         \exp_args:Nv \exp_not:n
-          { g__tblr_spec_ \int_use:N \g_tblr_level_int _#1_#2_tl }
+          { g__tblr_spec_ \int_use:N \gTblrLevelInt _#1_#2_tl }
       }
   }
 \cs_generate_variant:Nn \__tblr_spec_item:nn { ne }
@@ -343,7 +611,7 @@
 
 \cs_new_protected:Npn \__tblr_spec_gput_if_larger:nnn #1 #2 #3
   {
-    \tl_set:Nx \l__tblr_put_if_larger_tl { \__tblr_spec_item:nn {#1} {#2} }
+    \tl_set:Ne \l__tblr_put_if_larger_tl { \__tblr_spec_item:nn {#1} {#2} }
     \bool_lazy_or:nnT
       { \tl_if_empty_p:N \l__tblr_put_if_larger_tl }
       { \dim_compare_p:nNn {#3} > { \l__tblr_put_if_larger_tl } }
@@ -361,15 +629,15 @@
 \cs_new_protected:Npn \__tblr_spec_log:n #1
   {
     \clist_gremove_duplicates:c
-      { g__tblr_#1_ \int_use:N \g_tblr_level_int _clist }
-    \tl_log:x
+      { g__tblr_#1_ \int_use:N \gTblrLevelInt _clist }
+    \tl_log:e
       {
-        The ~ spec ~ list ~ #1 _ \int_use:N \g_tblr_level_int
+        The ~ spec ~ list ~ #1 _ \int_use:N \gTblrLevelInt
               \space contains ~ the ~ pairs:
       }
-    \clist_map_inline:cn { g__tblr_#1_ \int_use:N \g_tblr_level_int _clist }
+    \clist_map_inline:cn { g__tblr_#1_ \int_use:N \gTblrLevelInt _clist }
       {
-        \tl_log:x
+        \tl_log:e
           {
             \space { ##1 } ~\space=>~\space { \__tblr_spec_item:nn {#1} {##1} }
           }
@@ -377,7 +645,7 @@
   }
 
 %%% --------------------------------------------------------
-%%> \section{Data Structures Based on Integer Arrays}
+%%> \section{Data structures based on integer arrays}
 %%% --------------------------------------------------------
 
 \msg_new:nnn { tabularray } { intarray-beyond-bound }
@@ -467,9 +735,9 @@
         \int_use:c { g__tblr_data_#1_key_count_int }
           * \tl_use:c { c__tblr_data_#1_count_tl }
       }
-    \cs_set_eq:cc { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray }
+    \cs_set_eq:cc { g__tblr_#1_ \int_use:N \gTblrLevelInt _intarray }
       { g__tblr_#1_ \int_use:N \g__tblr_array_int _intarray }
-    %\intarray_log:c { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray }
+    %\intarray_log:c { g__tblr_#1_ \int_use:N \gTblrLevelInt _intarray }
   }
 
 %% #1: data name; #2: data index; #3: key name
@@ -509,7 +777,7 @@
       }
     \int_compare:nNnT { \l__tblr_key_remainder_int } = { 0 }
       { \int_set_eq:NN \l__tblr_key_remainder_int \l__tblr_key_count_int }
-    \tl_set:Nx #3 { \int_use:N \l__tblr_key_quotient_int }
+    \tl_set:Ne #3 { \int_use:N \l__tblr_key_quotient_int }
     \tl_set_eq:Nc #4
       { c__tblr_data_#1_key_name_ \int_use:N \l__tblr_key_remainder_int _tl }
   }
@@ -546,8 +814,8 @@
       }
     \int_compare:nNnT { \l__tblr_key_remainder_int } = { 0 }
       { \int_set_eq:NN \l__tblr_key_remainder_int \c at colcount }
-    \tl_set:Nx #4 { \int_use:N \l__tblr_key_remainder_int }
-    \tl_set:Nx #3 { \int_use:N \l__tblr_key_quotient_two_int }
+    \tl_set:Ne #4 { \int_use:N \l__tblr_key_remainder_int }
+    \tl_set:Ne #3 { \int_use:N \l__tblr_key_quotient_two_int }
   }
 
 \tl_new:N \g__tblr_data_int_from_value_tl
@@ -584,7 +852,7 @@
 
 \cs_new_protected:Npn \__tblr_data_int_from_dim:n #1
   {
-    \tl_gset:Nx \g__tblr_data_int_from_value_tl { \dim_to_decimal_in_sp:n {#1} }
+    \tl_gset:Ne \g__tblr_data_int_from_value_tl { \dim_to_decimal_in_sp:n {#1} }
   }
 
 %% Return a dimension in pt so that it's easier to understand in tracing messages
@@ -597,7 +865,7 @@
 
 \cs_new_protected:Npn \__tblr_data_int_from_dec:n #1
   {
-    \tl_gset:Nx \g__tblr_data_int_from_value_tl
+    \tl_gset:Ne \g__tblr_data_int_from_value_tl
       { \dim_to_decimal_in_sp:n {#1 pt} }
   }
 
@@ -618,12 +886,15 @@
       }
       {
         \int_gincr:N \g__tblr_data_str_value_count_int
-        \tl_gset:cx { g__tblr_data_ \tl_to_str:n {#1} _to_int_tl }
+        \tl_new:c { g__tblr_data_ \tl_to_str:n {#1} _to_int_tl }
+        \tl_gset:ce { g__tblr_data_ \tl_to_str:n {#1} _to_int_tl }
           { \int_use:N \g__tblr_data_str_value_count_int }
+        \tl_new:c
+          { g__tblr_data_ \int_use:N \g__tblr_data_str_value_count_int _to_str_tl }
         \tl_gset:cn
           { g__tblr_data_ \int_use:N \g__tblr_data_str_value_count_int _to_str_tl }
           { \exp_not:n {#1} }
-        \tl_gset:Nx \g__tblr_data_int_from_value_tl
+        \tl_gset:Ne \g__tblr_data_int_from_value_tl
           { \int_use:N \g__tblr_data_str_value_count_int }
       }
   }
@@ -638,7 +909,7 @@
   {
     \__tblr_data_int_from_value:nnn {#1} {#3} {#4}
     \__tblr_intarray_gset:cnn
-      { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray }
+      { g__tblr_#1_ \int_use:N \gTblrLevelInt _intarray }
       { \__tblr_data_key_to_int:nnn {#1} {#2} {#3} }
       { \g__tblr_data_int_from_value_tl }
   }
@@ -650,7 +921,7 @@
   {
     \__tblr_data_int_from_value:nnn {#1} {#4} {#5}
     \__tblr_intarray_gset:cnn
-      { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray }
+      { g__tblr_#1_ \int_use:N \gTblrLevelInt _intarray }
       { \__tblr_data_key_to_int:nnnn {#1} {#2} {#3} {#4} }
       { \g__tblr_data_int_from_value_tl }
   }
@@ -662,7 +933,7 @@
   {
     \__tblr_data_int_to_value:nne {#1} {#3}
       {
-        \intarray_item:cn { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray }
+        \intarray_item:cn { g__tblr_#1_ \int_use:N \gTblrLevelInt _intarray }
           { \__tblr_data_key_to_int:nnn {#1} {#2} {#3} }
       }
   }
@@ -673,7 +944,7 @@
   {
     \__tblr_data_int_to_value:nne {#1} {#4}
       {
-        \intarray_item:cn { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray }
+        \intarray_item:cn { g__tblr_#1_ \int_use:N \gTblrLevelInt _intarray }
           { \__tblr_data_key_to_int:nnnn {#1} {#2} {#3} {#4} }
       }
   }
@@ -691,8 +962,8 @@
 
 \cs_new_protected:cpn { __tblr_data_log_1:n } #1
   {
-    %\intarray_log:c { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray }
-    \tl_set:Nx \l_tmpa_tl { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray }
+    %\intarray_log:c { g__tblr_#1_ \int_use:N \gTblrLevelInt _intarray }
+    \tl_set:Ne \l_tmpa_tl { g__tblr_#1_ \int_use:N \gTblrLevelInt _intarray }
     \tl_log:n { ----------~----------~----------~----------~---------- }
     \int_step_inline:nn
       { \intarray_count:c { \l_tmpa_tl } }
@@ -699,7 +970,7 @@
       {
         \__tblr_data_int_to_key:nnNN {#1} {##1}
           \l__tblr_data_index_tl \l__tblr_data_key_tl
-        \tl_log:x
+        \tl_log:e
           {
             \space
             { #1 [\l__tblr_data_index_tl] / \l__tblr_data_key_tl }
@@ -714,8 +985,8 @@
 
 \cs_new_protected:cpn { __tblr_data_log_2:n } #1
   {
-    %\intarray_log:c { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray }
-    \tl_set:Nx \l_tmpa_tl { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray }
+    %\intarray_log:c { g__tblr_#1_ \int_use:N \gTblrLevelInt _intarray }
+    \tl_set:Ne \l_tmpa_tl { g__tblr_#1_ \int_use:N \gTblrLevelInt _intarray }
     \tl_log:n { ----------~----------~----------~----------~---------- }
     \int_step_inline:nn
       { \intarray_count:c { \l_tmpa_tl } }
@@ -722,7 +993,7 @@
       {
         \__tblr_data_int_to_key:nnNNN {#1} {##1}
           \l__tblr_data_index_tl \l__tblr_data_index_two_tl \l__tblr_data_key_tl
-        \tl_log:x
+        \tl_log:e
           {
             \space
             {
@@ -743,7 +1014,7 @@
   {
     \__tblr_data_int_from_value:nnn {#1} {#3} {#4}
     \__tblr_array_gput_if_larger:cnn
-      { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray }
+      { g__tblr_#1_ \int_use:N \gTblrLevelInt _intarray }
       { \__tblr_data_key_to_int:nnn {#1} {#2} {#3} }
       { \g__tblr_data_int_from_value_tl }
   }
@@ -761,7 +1032,7 @@
   {
     \__tblr_data_int_from_value:nnn {#1} {#3} {#4}
     \__tblr_array_gadd_value:cnn
-      { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray }
+      { g__tblr_#1_ \int_use:N \gTblrLevelInt _intarray }
       { \__tblr_data_key_to_int:nnn {#1} {#2} {#3} }
       { \g__tblr_data_int_from_value_tl }
   }
@@ -774,83 +1045,222 @@
   }
 \cs_generate_variant:Nn \__tblr_array_gadd_value:Nnn { cnn }
 
+%%% --------------------------------------------------------
+%%> \section{Switch between different data structures}
+%%% --------------------------------------------------------
+
+\cs_set_eq:NN \__tblr_prop_new:N \prop_new:N
+\cs_set_eq:NN \__tblr_prop_gclear_new:N \prop_gclear_new:N
+\cs_generate_variant:Nn \__tblr_prop_gclear_new:N { c }
+
 \bool_new:N \g__tblr_use_intarray_bool
 \bool_gset_true:N \g__tblr_use_intarray_bool
 
-\AtBeginDocument
+\cs_new_protected:Npn \__tblr_map_data_to_spec:
   {
-    \bool_if:NF \g__tblr_use_intarray_bool
+    \cs_set_protected:Npn \__tblr_data_gput:nnnn ##1 ##2 ##3 ##4
       {
-        \cs_set_protected:Npn \__tblr_data_gput:nnnn #1 #2 #3 #4
+        \__tblr_spec_gput:nnn {##1} { [##2] / ##3 } {##4}
+      }
+    \cs_set_protected:Npn \__tblr_data_gput:nnnnn ##1 ##2 ##3 ##4 ##5
+      {
+        \__tblr_spec_gput:nnn {##1} { [##2][##3] / ##4 } {##5}
+      }
+    \cs_set:Npn \__tblr_data_item:nnn ##1 ##2 ##3
+      {
+        \__tblr_spec_item:nn {##1} { [##2] / ##3 }
+      }
+    \cs_set:Npn \__tblr_data_item:nnnn ##1 ##2 ##3 ##4
+      {
+        \__tblr_spec_item:nn {##1} { [##2][##3] / ##4 }
+      }
+    \cs_set_protected:Npn \__tblr_data_log:n ##1
+      {
+        \__tblr_spec_log:n {##1}
+      }
+    \cs_set_protected:Npn \__tblr_data_gput_if_larger:nnnn ##1 ##2 ##3 ##4
+      {
+        \__tblr_spec_gput_if_larger:nnn {##1} { [##2] / ##3 } {##4}
+      }
+    \cs_set_protected:Npn \__tblr_data_gput_if_larger:nnnnn ##1 ##2 ##3 ##4 ##5
+      {
+        \__tblr_spec_gput_if_larger:nnn {##1} { [##2][##3] / ##4 } {##5}
+      }
+    \cs_set_protected:Npn \__tblr_data_gadd_dimen_value:nnnn ##1 ##2 ##3 ##4
+      {
+        \__tblr_spec_gadd_dimen_value:nnn {##1} { [##2] / ##3 } {##4}
+      }
+    \cs_set_protected:Npn \__tblr_data_gadd_dimen_value:nnnnn ##1 ##2 ##3 ##4 ##5
+      {
+        \__tblr_spec_gadd_dimen_value:nnn {##1} { [##2][##3] / ##4 } {##5}
+      }
+  }
+
+\bool_new:N \g__tblr_use_linked_prop_bool
+
+\cs_new_protected:Npn \__tblr_map_data_to_prop:
+  {
+    \cs_set_protected:Npn \__tblr_data_gput:nnnn ##1 ##2 ##3 ##4
+      {
+        \__tblr_prop_gput:nnn {##1} { [##2] / ##3 } {##4}
+      }
+    \cs_set_protected:Npn \__tblr_data_gput:nnnnn ##1 ##2 ##3 ##4 ##5
+      {
+        \__tblr_prop_gput:nnn {##1} { [##2][##3] / ##4 } {##5}
+      }
+    \cs_set:Npn \__tblr_data_item:nnn ##1 ##2 ##3
+      {
+        % Be careful not to add \tl_log in an expandable function
+        \__tblr_prop_item:nn {##1} { [##2] / ##3 }
+      }
+    \cs_set:Npn \__tblr_data_item:nnnn ##1 ##2 ##3 ##4
+      {
+        % Be careful not to add \tl_log in an expandable function
+        \__tblr_prop_item:nn {##1} { [##2][##3] / ##4 }
+      }
+    \cs_set_protected:Npn \__tblr_data_log:n ##1
+      {
+        \__tblr_prop_log:n {##1}
+      }
+    \cs_set_protected:Npn \__tblr_data_gput_if_larger:nnnn ##1 ##2 ##3 ##4
+      {
+        \__tblr_prop_gput_if_larger:nnn {##1} { [##2] / ##3 } {##4}
+      }
+    \cs_set_protected:Npn \__tblr_data_gput_if_larger:nnnnn ##1 ##2 ##3 ##4 ##5
+      {
+        \__tblr_prop_gput_if_larger:nnn {##1} { [##2][##3] / ##4 } {##5}
+      }
+    \cs_set_protected:Npn \__tblr_data_gadd_dimen_value:nnnn ##1 ##2 ##3 ##4
+      {
+        \__tblr_prop_gadd_dimen_value:nnn {##1} { [##2] / ##3 } {##4}
+      }
+    \cs_set_protected:Npn \__tblr_data_gadd_dimen_value:nnnnn ##1 ##2 ##3 ##4 ##5
+      {
+        \__tblr_prop_gadd_dimen_value:nnn {##1} { [##2][##3] / ##4 } {##5}
+      }
+  }
+
+\cs_new_protected:Npn \__tblr_map_spec_to_prop:
+  {
+    \cs_set_eq:NN \__tblr_spec_gput:nnn \__tblr_prop_gput:nnn
+    \cs_set_eq:NN \__tblr_spec_item:nn \__tblr_prop_item:nn
+    \cs_set_eq:NN \__tblr_spec_log:n \__tblr_prop_log:n
+    \cs_set_eq:NN
+      \__tblr_spec_gput_if_larger:nnn
+      \__tblr_prop_gput_if_larger:nnn
+    \cs_set_eq:NN
+      \__tblr_spec_gadd_dimen_value:nnn
+      \__tblr_prop_gadd_dimen_value:nnn
+  }
+
+%% Backport fix for https://github.com/latex3/latex3/issues/1630
+\cs_new_protected:Npn \__tblr_backport_prop_item_fix:
+  {
+    \cs_set:Npn \prop_item:Nn ##1 ##2
+      {
+        \__prop_if_flat:NTF ##1
           {
-            \__tblr_spec_gput:nnn {#1} { [#2] / #3 } {#4}
+            \exp_args:NNo \prop_map_tokens:Nn ##1
+              {
+                \exp_after:wN \__prop_item:nnn
+                \exp_after:wN { \tl_to_str:n {##2} }
+              }
           }
-        \cs_set_protected:Npn \__tblr_data_gput:nnnnn #1 #2 #3 #4 #5
-          {
-            \__tblr_spec_gput:nnn {#1} { [#2][#3] / #4 } {#5}
-          }
-        \cs_set:Npn \__tblr_data_item:nnn #1 #2 #3
-          {
-            \__tblr_spec_item:nn {#1} { [#2] / #3 }
-          }
-        \cs_set:Npn \__tblr_data_item:nnnn #1 #2 #3 #4
-          {
-            \__tblr_spec_item:nn {#1} { [#2][#3] / #4 }
-          }
-        \cs_set_protected:Npn \__tblr_data_log:n #1
-          {
-            \__tblr_spec_log:n {#1}
-          }
-        \cs_set_protected:Npn \__tblr_data_gput_if_larger:nnnn #1 #2 #3 #4
-          {
-            \__tblr_spec_gput_if_larger:nnn {#1} { [#2] / #3 } {#4}
-          }
-        \cs_set_protected:Npn \__tblr_data_gput_if_larger:nnnnn #1 #2 #3 #4 #5
-          {
-            \__tblr_spec_gput_if_larger:nnn {#1} { [#2][#3] / #4 } {#5}
-          }
-        \cs_set_protected:Npn \__tblr_data_gadd_dimen_value:nnnn #1 #2 #3 #4
-          {
-            \__tblr_spec_gadd_dimen_value:nnn {#1} { [#2] / #3 } {#4}
-          }
-        \cs_set_protected:Npn \__tblr_data_gadd_dimen_value:nnnnn #1 #2 #3 #4 #5
-          {
-            \__tblr_spec_gadd_dimen_value:nnn {#1} { [#2][#3] / #4 } {#5}
-          }
+          { \exp_after:wN \__prop_get_linked:w ##1 {##2} \exp_not:n { } { } }
       }
   }
 
+%% We can't use \IfExplAtLeastTF since it was added in LaTeX release 2023-11-01.
+\@ifl at t@r \ExplLoaderFileDate { 2024-02-18 }
+  { \bool_gset_true:N \g__tblr_use_linked_prop_bool } { }
+
+\AtBeginDocument
+  {
+    \bool_if:NTF \g__tblr_use_linked_prop_bool
+      {
+        \__tblr_map_spec_to_prop:
+        \__tblr_map_data_to_prop:
+        \cs_set_eq:NN \__tblr_prop_new:N \prop_new_linked:N
+        \cs_set_eq:NN \__tblr_prop_gclear_new:N \prop_gclear_new_linked:N
+        \@ifl at t@r \ExplLoaderFileDate { 2024-12-09 }
+          { } { \__tblr_backport_prop_item_fix: }
+      }
+      { \bool_if:NF \g__tblr_use_intarray_bool { \__tblr_map_data_to_spec: } }
+  }
+
 %%% --------------------------------------------------------
-%%> \section{Child Selectors}
+%%> \section{Child indexers and child selectors}
 %%% --------------------------------------------------------
 
-\clist_new:N \g_tblr_used_child_selectors_clist
+\clist_new:N \lTblrUsedChildIndexerClist
+\clist_new:N \lTblrUsedChildSelectorClist
 
-\tl_new:N \l__tblr_childs_arg_spec_tl
+\msg_new:nnn { tabularray } { invalid-child-indexer }
+  { Child ~ indexer ~ name ~ '#1' ~ must ~ start ~ with ~ uppercase ~ letter. }
 
+\msg_new:nnn { tabularray } { invalid-child-selector }
+  { Child ~ selector ~ name ~ '#1' ~ must ~ start ~ with ~ lowercase ~ letter. }
+
+\msg_new:nnn { tabularray } { used-child-indexer }
+  { Child ~ indexer ~ name ~ '#1' ~ has ~ been ~ used. }
+
 \msg_new:nnn { tabularray } { used-child-selector }
-  { Child ~ selector ~ name ~ "#1" ~ has ~ been ~ used! }
+  { Child ~ selector ~ name ~ '#1' ~ has ~ been ~ used. }
 
-\NewDocumentCommand \NewChildSelector { m O{0} o m }
+\tl_new:N \l__tblr_child_arg_spec_tl
+
+\NewDocumentCommand \NewTblrChildIndexer { m O{0} o m }
   {
-    \__tblr_new_child_selector_aux:xnnn { \tl_trim_spaces:n {#1} } {#2} {#3} {#4}
+    \__tblr_new_child_indexer_aux:ennn { \tl_trim_spaces:n {#1} } {#2} {#3} {#4}
   }
 
-\cs_new_protected:Npn \__tblr_new_child_selector_aux:nnnn #1 #2 #3 #4
+\cs_new_protected:Npn \__tblr_new_child_indexer_aux:nnnn #1 #2 #3 #4
   {
-    \clist_if_in:NnTF \g_tblr_used_child_selectors_clist { #1 }
+    \__tblr_if_head_upper:nTF { #1 }
       {
-        \msg_error:nnn { tabularray } { used-child-selector } { #1 }
-        \clist_log:N \g_tblr_used_child_selectors_clist
+        \clist_if_in:NnTF \lTblrUsedChildIndexerClist { #1 }
+          {
+            \msg_error:nnn { tabularray } { used-child-indexer } { #1 }
+            \clist_log:N \lTblrUsedChildIndexerClist
+          }
+          {
+            \__tblr_make_xparse_arg_spec:nnN
+              { #2 } { #3 } \l__tblr_child_arg_spec_tl
+            \exp_args:NcV \NewDocumentCommand
+              { __tblr_child_indexer_ #1 } \l__tblr_child_arg_spec_tl { #4 }
+            \clist_put_right:Nn \lTblrUsedChildIndexerClist { #1 }
+          }
       }
+      { \msg_error:nnn { tabularray } { invalid-child-indexer } { #1 } }
+  }
+\cs_generate_variant:Nn \__tblr_new_child_indexer_aux:nnnn { ennn }
+
+\NewDocumentCommand \NewTblrChildSelector { m O{0} o m }
+  {
+    \__tblr_new_child_selector_aux:ennn { \tl_trim_spaces:n {#1} } {#2} {#3} {#4}
+  }
+\cs_set_eq:NN \NewChildSelector \NewTblrChildSelector
+
+\cs_new_protected:Npn \__tblr_new_child_selector_aux:nnnn #1 #2 #3 #4
+  {
+    \__tblr_if_head_lower:nTF { #1 }
       {
-        \__tblr_make_xparse_arg_spec:nnN { #2 } { #3 } \l__tblr_childs_arg_spec_tl
-        \exp_args:NcV \NewDocumentCommand
-          { __tblr_child_selector_ #1 :w } \l__tblr_childs_arg_spec_tl { #4 }
-        \clist_gput_right:Nn \g_tblr_used_child_selectors_clist { #1 }
+        \clist_if_in:NnTF \lTblrUsedChildSelectorClist { #1 }
+          {
+            \msg_error:nnn { tabularray } { used-child-selector } { #1 }
+            \clist_log:N \lTblrUsedChildSelectorClist
+          }
+          {
+            \__tblr_make_xparse_arg_spec:nnN
+              { #2 } { #3 } \l__tblr_child_arg_spec_tl
+            \exp_args:NcV \NewDocumentCommand
+              { __tblr_child_selector_ #1 } \l__tblr_child_arg_spec_tl { #4 }
+            \clist_put_right:Nn \lTblrUsedChildSelectorClist { #1 }
+          }
       }
+      { \msg_error:nnn { tabularray } { invalid-child-selector } { #1 } }
   }
-\cs_generate_variant:Nn \__tblr_new_child_selector_aux:nnnn { xnnn }
+\cs_generate_variant:Nn \__tblr_new_child_selector_aux:nnnn { ennn }
 
 %% #1: argument number, #2: optional argument default, #3: result tl
 \cs_new_protected:Npn \__tblr_make_xparse_arg_spec:nnN #1 #2 #3
@@ -861,123 +1271,338 @@
         \IfValueTF { #2 }
           { \tl_set:Nn #3 { O{#2} } }
           { \tl_set:Nn #3 { m } }
-        \tl_put_right:Nx #3 { \prg_replicate:nn { #1 - 1 } { m } }
+        \tl_put_right:Ne #3 { \prg_replicate:nn { #1 - 1 } { m } }
       }
   }
 
-\clist_new:N \l_tblr_childs_clist
-\tl_new:N \l_tblr_childs_total_tl
+\int_new:N \lTblrChildTotalInt
+\int_new:N \lTblrChildHtotalInt
+\int_new:N \lTblrChildVtotalInt
+\tl_new:N \lTblrChildIndexTl % may be <i> or {<i>}{<j>}
+\clist_new:N \lTblrChildClist
 
-\NewChildSelector { odd } [1] []
+\NewTblrChildIndexer {U}
   {
-    \tl_if_blank:nTF {#1}
+    \tl_set:Ne \lTblrChildIndexTl
+      { \int_eval:n { \int_max:nn { \lTblrChildTotalInt - 5 } { 1 } } }
+  }
+\NewTblrChildIndexer {V}
+  {
+    \tl_set:Ne \lTblrChildIndexTl
+      { \int_eval:n { \int_max:nn { \lTblrChildTotalInt - 4 } { 1 } } }
+  }
+\NewTblrChildIndexer {W}
+  {
+    \tl_set:Ne \lTblrChildIndexTl
+      { \int_eval:n { \int_max:nn { \lTblrChildTotalInt - 3 } { 1 } } }
+  }
+\NewTblrChildIndexer {X}
+  {
+    \tl_set:Ne \lTblrChildIndexTl
+      { \int_eval:n { \int_max:nn { \lTblrChildTotalInt - 2 } { 1 } } }
+  }
+\NewTblrChildIndexer {Y}
+  {
+    \tl_set:Ne \lTblrChildIndexTl
+      { \int_eval:n { \int_max:nn { \lTblrChildTotalInt - 1 } { 1 } } }
+  }
+\NewTblrChildIndexer {Z} [1] [1]
+  {
+    \tl_set:Ne \lTblrChildIndexTl
+      { \int_eval:n { \int_max:nn { \lTblrChildTotalInt + 1 - #1 } { 1 } } }
+  }
+
+\NewTblrChildSelector { odd } [1] [1]
+  {
+    \int_if_odd:nTF {#1}
       {
-        \int_step_inline:nnnn {1} {2} { \l_tblr_childs_total_tl }
-          { \clist_put_right:Nn \l_tblr_childs_clist {##1} }
+        \clist_set:Ne \lTblrChildClist
+          { {#1} {2} {\int_use:N \lTblrChildTotalInt} }
       }
-      { \__tblr_child_selector_odd_or_even:nn { odd } {#1} }
+      {
+        \clist_set:Ne \lTblrChildClist
+          { {\int_eval:n {#1 + 1}} {2} {\int_use:N \lTblrChildTotalInt} }
+      }
   }
 
-\NewChildSelector { even } [1] []
+\NewTblrChildSelector { even } [1] [2]
   {
-    \tl_if_blank:nTF {#1}
+    \int_if_even:nTF {#1}
       {
-        \int_step_inline:nnnn {2} {2} { \l_tblr_childs_total_tl }
-          { \clist_put_right:Nn \l_tblr_childs_clist {##1} }
+        \clist_set:Ne \lTblrChildClist
+          { {#1} {2} {\int_use:N \lTblrChildTotalInt} }
       }
-      { \__tblr_child_selector_odd_or_even:nn { even } {#1} }
+      {
+        \clist_set:Ne \lTblrChildClist
+          { {\int_eval:n {#1 + 1 }} {2} {\int_use:N \lTblrChildTotalInt} }
+      }
   }
 
+%% #1: step; #2: start index; #3: end index
+\NewTblrChildSelector { every } [3] [1]
+  {
+    \clist_set:Ne \lTblrChildClist
+      {
+        {
+          \int_compare:nNnTF {#2} < {0}
+            { \int_eval:n { \lTblrChildTotalInt + 1 #2 } } {#2}
+        }
+        { #1 }
+        {
+          \int_compare:nNnTF {#3} < {0}
+            { \int_eval:n { \lTblrChildTotalInt + 1 #3 } } {#3}
+        }
+      }
+  }
+
+\clist_new:N \l__tblr_child_whole_clist
+\seq_new:N \l__child_spec_seq
+\tl_new:N \l__tblr_child_item_head_tl
+
+%% #1, str of child specifications; #2, total number of children.
+%% The result will be put into \l__tblr_child_whole_clist
+\cs_new_protected:Npn \__tblr_child_parse:nn #1 #2
+  {
+    \clist_clear:N \l__tblr_child_whole_clist
+    \seq_set_split:Nnn \l__child_spec_seq {,} {#1}
+    \seq_map_inline:Nn \l__child_spec_seq
+      {
+        \clist_clear:N \lTblrChildClist
+        \int_set:Nn \lTblrChildTotalInt {#2}
+        \tl_set:Ne \l__tblr_child_item_head_tl { \tl_head:n {##1} }
+        \tl_if_eq:NnTF \l__tblr_child_item_head_tl {-}
+          { \clist_set:Ne \lTblrChildClist { {1} {1} {#2} } }
+          {
+            \__tblr_tl_if_lower:VTF \l__tblr_child_item_head_tl
+              {
+                \__tblr_child_run_selector:n {##1}
+              }
+              {
+                \__tblr_tl_if_upper:VTF \l__tblr_child_item_head_tl
+                  {
+                    \__tblr_child_split_item:n {##1}
+                    \__tblr_child_run_indexer_from:
+                    \__tblr_child_parse_indexer_to:N \__tblr_child_set_clist:
+                  }
+                  {
+                    \__tblr_tl_if_digit:VTF \l__tblr_child_item_head_tl
+                      {
+                        \__tblr_child_split_item:n {##1}
+                        \__tblr_child_parse_indexer_to:N \__tblr_child_set_clist:
+                      }
+                      {
+                        % error
+                      }
+                  }
+              }
+          }
+        \clist_put_right:NV \l__tblr_child_whole_clist \lTblrChildClist
+      }
+    %\clist_log:N \l__tblr_child_whole_clist
+  }
+\cs_generate_variant:Nn \__tblr_child_parse:nn { ne }
+
+\cs_new_protected:Npn \__tblr_child_run_selector:n #1
+  {
+    \tl_rescan:ne { \cctab_select:N \c_code_cctab }
+      { \c_backslash_str __tblr_child_selector_ #1 }
+    \scan_stop: % for selectors with only optional arguments
+  }
+
+\cs_new_protected:Npn \__tblr_child_run_indexer:n #1
+  {
+    \tl_rescan:ne { \cctab_select:N \c_code_cctab }
+      { \c_backslash_str __tblr_child_indexer_ #1 }
+    \scan_stop: % for indexers with only optional arguments
+  }
+\cs_generate_variant:Nn \__tblr_child_run_indexer:n { V }
+
+\seq_new:N \l__tblr_child_item_seq
 \tl_new:N \l__tblr_child_from_tl
 \tl_new:N \l__tblr_child_to_tl
 
-%% #1: odd or even; #2: selector option
-\cs_new_protected:Npn \__tblr_child_selector_odd_or_even:nn #1 #2
+\cs_new_protected:Npn \__tblr_child_split_item:n #1
   {
-    \seq_set_split:Nnn \l_tmpa_seq {-} { #2 - Z }
-    \tl_set:Nx \l__tblr_child_from_tl { \seq_item:Nn \l_tmpa_seq {1} }
-    \tl_set:Nx \l__tblr_child_to_tl { \seq_item:Nn \l_tmpa_seq {2} }
-    \tl_use:c { int_if_ #1 :nF } { \l__tblr_child_from_tl }
+    \seq_set_split:Nnn  \l__tblr_child_item_seq {-} {#1}
+    \tl_set:Ne \l__tblr_child_from_tl
+      { \seq_item:Nn \l__tblr_child_item_seq {1} }
+    \tl_set:Ne \l__tblr_child_to_tl
+      { \seq_item:Nn \l__tblr_child_item_seq {2} }
+  }
+
+\cs_new_protected:Npn \__tblr_child_parse_indexer_to:N #1
+  {
+    \str_if_empty:NTF \l__tblr_child_to_tl
+      { \clist_set:NV \lTblrChildClist \l__tblr_child_from_tl }
       {
-        \tl_set:Nx \l__tblr_child_from_tl
-          { \int_eval:n { \l__tblr_child_from_tl + 1 } }
+        \__tblr_tl_if_digit:eF { \tl_head:N \l__tblr_child_to_tl }
+          { \__tblr_child_run_indexer_to: }
+        #1
       }
-    \__tblr_child_name_to_index:VN \l__tblr_child_to_tl \l__tblr_child_to_tl
-    \int_step_inline:nnnn { \l__tblr_child_from_tl } {2} { \l__tblr_child_to_tl }
-      { \clist_put_right:Nn \l_tblr_childs_clist {##1} }
   }
 
-\regex_const:Nn \c__tblr_split_selector_name_regex { ^ ( [A-Za-z] {2,} ) ( . * ) }
-\seq_new:N \l__tblr_childs_split_seq
-\seq_new:N \l__tblr_childs_regex_seq
-\tl_new:N \l__tblr_childs_selector_tl
+\cs_new_protected:Npn \__tblr_child_run_indexer_from:
+  {
+    \__tblr_child_run_indexer:V \l__tblr_child_from_tl
+    \tl_set_eq:NN \l__tblr_child_from_tl \lTblrChildIndexTl
+  }
 
-%% #1, child specifications; #2, total number.
-%% The result will be put into \l_tblr_childs_clist
-\cs_new_protected:Npn \__tblr_get_childs:nn #1 #2
+\cs_new_protected:Npn \__tblr_child_run_indexer_to:
   {
-    \clist_clear:N \l_tblr_childs_clist
-    \tl_set:Nx \l_tblr_childs_total_tl {#2}
-    \regex_extract_once:NnNTF \c__tblr_split_selector_name_regex {#1}
-      \l__tblr_childs_regex_seq
+    \__tblr_child_run_indexer:V \l__tblr_child_to_tl
+    \tl_set_eq:NN \l__tblr_child_to_tl \lTblrChildIndexTl
+  }
+
+\cs_new_protected:Npn \__tblr_child_set_clist:
+  {
+    \clist_set:Ne \lTblrChildClist
       {
-        \tl_set:No \l__tblr_childs_selector_tl
+        { \l__tblr_child_from_tl } { 1 } { \l__tblr_child_to_tl }
+      }
+  }
+
+\clist_new:N \l__tblr_child_tuple_whole_clist
+
+%% #1, str of child tuple specifications; #2 and #3, total numbers of children.
+%% The result will be put into \l__tblr_child_tuple_whole_clist
+\cs_new_protected:Npn \__tblr_child_parse_tuple:nnn #1 #2 #3
+  {
+    \clist_clear:N \l__tblr_child_tuple_whole_clist
+    \seq_set_split:Nnn \l__child_spec_seq {,} {#1}
+    \seq_map_inline:Nn \l__child_spec_seq
+      {
+        \clist_clear:N \lTblrChildClist
+        \int_set:Nn \lTblrChildHtotalInt {#2}
+        \int_set:Nn \lTblrChildVtotalInt {#3}
+        \tl_if_head_is_group:nTF {##1}
           {
-            \cs:w
-            __tblr_child_selector_ \seq_item:Nn \l__tblr_childs_regex_seq {2} :w
-            \cs_end:
+            \__tblr_child_split_item:n {##1}
+            \__tblr_child_parse_indexer_to:N \__tblr_child_set_clist_tuple:
           }
-        \exp_last_unbraced:Nx \l__tblr_childs_selector_tl
-          { \seq_item:Nn \l__tblr_childs_regex_seq{3} }
+          {
+            \tl_set:Ne \l__tblr_child_item_head_tl { \tl_head:n {##1} }
+            \__tblr_tl_if_upper:VTF \l__tblr_child_item_head_tl
+              {
+                \__tblr_child_split_item:n {##1}
+                \__tblr_child_run_indexer_from:
+                \__tblr_child_parse_indexer_to:N \__tblr_child_set_clist_tuple:
+              }
+              {
+                \__tblr_tl_if_lower:VTF \l__tblr_child_item_head_tl
+                  { \__tblr_child_run_selector:n {##1} }
+                  {
+                    % error
+                  }
+              }
+          }
+        \clist_put_right:NV \l__tblr_child_tuple_whole_clist \lTblrChildClist
       }
-      {
-        \tl_if_eq:nnTF {#1} {-}
-          { \__tblr_get_childs_normal:nn {1-#2} {#2} }
-          { \__tblr_get_childs_normal:nn {#1} {#2} }
-      }
-    %\clist_log:N \l_tblr_childs_clist
+    %\clist_log:N \l__tblr_child_tuple_whole_clist
   }
-\cs_generate_variant:Nn \__tblr_get_childs:nn { nx }
+\cs_generate_variant:Nn \__tblr_child_parse_tuple:nnn { nee }
 
-\cs_new_protected:Npn \__tblr_get_childs_normal:nn #1 #2
+\int_new:N \l__child_diff_h_int
+\int_new:N \l__child_diff_v_int
+\int_new:N \l__child_sign_h_int
+\int_new:N \l__child_sign_v_int
+\int_new:N \l__child_step_int
+
+\cs_new_protected:Npn \__tblr_child_set_clist_tuple:
   {
-    \seq_set_split:Nnn \l__tblr_childs_split_seq {,} {#1}
-    \seq_map_inline:Nn \l__tblr_childs_split_seq
+    \int_set:Nn \l__child_diff_h_int
       {
-        \tl_if_in:nnTF {##1} {-}
-          { \__tblr_get_childs_normal_aux:w ##1 \scan_stop }
-          { \__tblr_get_childs_normal_aux:w ##1 - ##1 \scan_stop }
+          \exp_after:wN \use_i:nn \l__tblr_child_to_tl
+        - \exp_after:wN \use_i:nn \l__tblr_child_from_tl
       }
+    \int_set:Nn \l__child_diff_v_int
+      {
+          \exp_after:wN \use_ii:nn \l__tblr_child_to_tl
+        - \exp_after:wN \use_ii:nn \l__tblr_child_from_tl
+      }
+    \int_set:Nn \l__child_sign_h_int { \int_sign:n { \l__child_diff_h_int } }
+    \int_set:Nn \l__child_sign_v_int { \int_sign:n { \l__child_diff_v_int } }
+    \int_set:Nn \l__child_step_int
+      {
+        \int_min:nn
+          { \int_abs:n { \l__child_diff_h_int } }
+          { \int_abs:n { \l__child_diff_v_int } }
+      }
+    \int_step_inline:nnn { 0 } { \l__child_step_int }
+      {
+        \clist_put_right:Ne \lTblrChildClist
+          {
+            {
+              \int_eval:n
+                {
+                  \exp_after:wN \use_i:nn \l__tblr_child_from_tl
+                  + ##1 * \l__child_sign_h_int
+                }
+            }
+            {
+              \int_eval:n
+                {
+                  \exp_after:wN \use_ii:nn \l__tblr_child_from_tl
+                  + ##1 * \l__child_sign_v_int
+                }
+            }
+          }
+      }
   }
 
-\cs_new_protected_nopar:Npn \__tblr_get_childs_normal_aux:w #1 - #2 \scan_stop
+%% Map over child index lists. We only support two level nesting.
+
+\cs_new_protected:Npn \__tblr_child_map_inline:n #1
   {
-    \__tblr_child_name_to_index:nN {#1} \l__tblr_child_from_tl
-    \__tblr_child_name_to_index:nN {#2} \l__tblr_child_to_tl
-    \int_step_inline:nnn { \l__tblr_child_from_tl } { \l__tblr_child_to_tl }
-      { \clist_put_right:Nn \l_tblr_childs_clist {##1} }
+    \__tblr_child_map_inline_aux:Nn \__tblr_child_map_aux:n {#1}
   }
 
-\regex_const:Nn \c__tblr_child_name_regex { ^ [U-Z] $ }
+\cs_new_protected:Npn \__tblr_child_submap_inline:n #1
+  {
+    \__tblr_child_map_inline_aux:Nn \__tblr_child_submap_aux:n {#1}
+  }
 
-%% Convert U, V, W, X, Y, Z to the indexes of the last six childs, respectively
-\cs_new_protected_nopar:Npn \__tblr_child_name_to_index:nN #1 #2
+%% #1: function for storing the inline code; #2: the inline code.
+\cs_new_protected:Npn \__tblr_child_map_inline_aux:Nn #1 #2
   {
-    \regex_match:NnTF \c__tblr_child_name_regex {#1}
+    \cs_set_protected:Npn #1 ##1 { #2 }
+    \exp_args:NV \clist_map_inline:nn \l__tblr_child_whole_clist
       {
-        \tl_set:Nx #2
-          { \int_eval:n { \l_tblr_childs_total_tl + \int_from_alph:n {#1} - 26 } }
+        \tl_if_head_is_group:nTF { ##1 }
+          { \int_step_inline:nnnn ##1 { #1 { ####1 } } } { #1 { ##1 } }
       }
-      { \tl_set:Nx #2 { #1 } }
   }
-\cs_generate_variant:Nn \__tblr_child_name_to_index:nN { VN }
 
+%% Get the first or last index of a child index list (used by 'endpos' key).
+
+\cs_new_protected:Npn \__tblr_child_get_first:N #1
+  {
+    \tl_set:Ne #1 { \clist_item:Nn \l__tblr_child_whole_clist { 1 } }
+    \exp_args:NV \tl_if_head_is_group:nT #1
+      { \tl_set:Ne #1 { \exp_after:wN \use_i:nnn #1 } }
+  }
+
+\cs_new_protected:Npn \__tblr_child_get_last:N #1
+  {
+    \tl_set:Ne #1 { \clist_item:Nn \l__tblr_child_whole_clist { -1 } }
+    \exp_args:NV \tl_if_head_is_group:nT #1
+      { \exp_after:wN \__tblr_child_get_step_last:nnnN #1 #1 }
+  }
+
+%% #1: from; #2: step; #3: to; #4: tl var for storing the result
+\cs_new_protected:Npn \__tblr_child_get_step_last:nnnN #1 #2 #3 #4
+  {
+    \tl_set:Ne #4 { \int_div_truncate:nn { #3 - #1 } { #2 } }
+    \int_compare:nNnTF { #4 } > { 0 }
+      { \tl_set:Ne #4 { \int_eval:n { #1 + #4 * #2 } } }
+      { \tl_set:Nn #4 { #1 } }
+  }
+
 %%% --------------------------------------------------------
-%%> \section{New Table Commands}
+%%> \section{New table commands}
 %%% --------------------------------------------------------
 
 %% We need some commands to modify table/row/column/cell specifications.
-%% These commands must be defined with \NewTableCommand command,
+%% These commands must be defined with \NewTblrTableCommand command,
 %% so that we could extract them, execute them once, then disable them.
 
 \clist_new:N \g__tblr_table_commands_clist
@@ -985,7 +1610,7 @@
 \msg_new:nnn { tabularray } { defined-table-command }
   { Table ~ command ~ #1 already ~ defined! }
 
-\NewDocumentCommand \NewTableCommand { m O{0} o m }
+\NewDocumentCommand \NewTblrTableCommand { m O{0} o m }
   {
     \clist_if_in:NnTF \g__tblr_table_commands_clist { #1 }
       {
@@ -1005,6 +1630,7 @@
             \exp_args:NcV \NewDocumentCommand
               { __tblr_table_command_ \cs_to_str:N #1 _saved:w } \l__tblr_a_tl { }
           }
+        \tl_new:c { g__tblr_table_cmd_ \cs_to_str:N #1 _arg_numb_tl }
         \IfValueTF { #3 }
           {
             \tl_gset:cn { g__tblr_table_cmd_ \cs_to_str:N #1 _arg_numb_tl } {-#2}
@@ -1015,6 +1641,7 @@
         \clist_gput_right:Nn \g__tblr_table_commands_clist { #1 }
       }
   }
+\cs_set_eq:NN \NewTableCommand \NewTblrTableCommand
 
 \cs_new_protected:Npn \__tblr_enable_table_commands:
   {
@@ -1045,7 +1672,7 @@
   }
 
 %% Add \empty as a table command so that users can write \\\empty\hline (see #328)
-\NewTableCommand\empty{}
+\NewTblrTableCommand\empty{}
 
 %% Table commands are defined only inside tblr environments,
 %% but some packages such as csvsimple need to use them outside tblr environments,
@@ -1062,11 +1689,155 @@
 \ProvideDocumentCommand \SetColumn  { o m } {}
 
 %%% --------------------------------------------------------
-%%> \section{New Content Commands}
+%%> \section{Child ids and child classes}
 %%% --------------------------------------------------------
 
+\NewTblrTableCommand \SetChild [1]
+  {
+    \__tblr_keys_set:nn { child/index } {#1}
+  }
+
+\__tblr_keys_define:nn { child/index }
+  {
+    id  .code:n = \__tblr_child_add_id:n {#1},
+    idh .code:n = \__tblr_child_add_idh:n {#1},
+    idv .code:n = \__tblr_child_add_idv:n {#1},
+    id* .meta:n = { id = #1, idh = #1, idv = #1 },
+    class  .code:n = \__tblr_child_add_class:n {#1},
+    classh .code:n = \__tblr_child_add_classh:n {#1},
+    classv .code:n = \__tblr_child_add_classv:n {#1},
+    class* .meta:n = { class = #1, classh = #1, classv = #1 }
+  }
+
+\clist_new:N \l__tblr_child_id_clist
+\clist_new:N \l__tblr_child_class_clist
+
+\cs_new_protected:Npn \__tblr_child_add_id:n #1
+  {
+    \clist_if_in:NnF \l__tblr_child_id_clist {#1}
+      {
+        \clist_put_right:Nn \l__tblr_child_id_clist {#1}
+        \tl_clear_new:c { l__tblr_child_id_#1_tl }
+      }
+    \tl_set:ce { l__tblr_child_id_#1_tl }
+      { { \int_use:N \c at rownum } { \int_use:N \c at colnum } }
+  }
+\cs_new_protected:Npn \__tblr_child_add_idh:n #1
+  {
+    \clist_if_in:NnF \l__tblr_child_id_clist {#1h}
+      {
+        \clist_put_right:Nn \l__tblr_child_id_clist {#1h}
+        \tl_clear_new:c { l__tblr_child_id_#1h_tl }
+      }
+    \tl_set:ce { l__tblr_child_id_#1h_tl } { \int_use:N \c at rownum }
+  }
+\cs_new_protected:Npn \__tblr_child_add_idv:n #1
+  {
+    \clist_if_in:NnF \l__tblr_child_id_clist {#1v}
+      {
+        \clist_put_right:Nn \l__tblr_child_id_clist {#1v}
+        \tl_clear_new:c { l__tblr_child_id_#1v_tl }
+      }
+    \tl_set:ce { l__tblr_child_id_#1v_tl } { \int_use:N \c at colnum }
+  }
+
+\cs_new_protected:Npn \__tblr_child_add_class:n #1
+  {
+    \clist_if_in:NnTF \l__tblr_child_class_clist {#1}
+      {
+        \clist_put_right:ce { l__tblr_child_class_#1_clist }
+          { { \int_use:N \c at rownum } { \int_use:N \c at colnum } }
+      }
+      {
+        \clist_put_right:Nn \l__tblr_child_class_clist {#1}
+        \clist_clear_new:c { l__tblr_child_class_#1_clist }
+        \clist_set:ce { l__tblr_child_class_#1_clist }
+          { { \int_use:N \c at rownum } { \int_use:N \c at colnum } }
+      }
+  }
+\cs_new_protected:Npn \__tblr_child_add_classh:n #1
+  {
+    \clist_if_in:NnTF \l__tblr_child_class_clist {#1h}
+      {
+        \clist_put_right:ce { l__tblr_child_class_#1h_clist }
+          { \int_use:N \c at rownum }
+      }
+      {
+        \clist_put_right:Nn \l__tblr_child_class_clist {#1h}
+        \clist_clear_new:c { l__tblr_child_class_#1h_clist }
+        \clist_set:ce { l__tblr_child_class_#1h_clist }
+          { \int_use:N \c at rownum }
+      }
+  }
+\cs_new_protected:Npn \__tblr_child_add_classv:n #1
+  {
+    \clist_if_in:NnTF \l__tblr_child_class_clist {#1v}
+      {
+        \clist_put_right:ce { l__tblr_child_class_#1v_clist }
+          { \int_use:N \c at colnum }
+      }
+      {
+        \clist_put_right:Nn \l__tblr_child_class_clist {#1v}
+        \clist_clear_new:c { l__tblr_child_class_#1v_clist }
+        \clist_set:ce { l__tblr_child_class_#1v_clist }
+          { \int_use:N \c at colnum }
+      }
+  }
+
+\cs_new_protected:Npn \__tblr_child_split_table_before:
+  {
+    \clist_clear:N \l__tblr_child_id_clist
+    \clist_clear:N \l__tblr_child_class_clist
+  }
+
+\cs_new_protected:Npn \__tblr_child_split_table_after:
+  {
+    \clist_map_inline:Nn \l__tblr_child_id_clist
+      {
+        \exp_args:Nne
+        \NewTblrChildIndexer {##1}
+          {
+            \tl_set:Nn \exp_not:N \lTblrChildIndexTl
+              { \use:c { l__tblr_child_id_##1_tl } }
+          }
+      }
+    \clist_map_inline:Nn \l__tblr_child_class_clist
+      {
+        \exp_args:Nne
+        \NewTblrChildSelector {##1}
+          {
+            \clist_set:Nn \exp_not:N \lTblrChildClist
+              { \use:c { l__tblr_child_class_##1_clist } }
+          }
+      }
+  }
+
+\cs_new_protected:Npn \__tblr_child_extract_index_command:N #1
+  {
+    \tl_if_head_eq_meaning:VNT #1 \SetChild
+      {
+        \tl_set:Ne #1 { \tl_tail:N #1 }
+        \__tblr_keys_set:ne { child/index } { \tl_head:N #1 }
+        \tl_set:Ne #1 { \tl_tail:N #1 }
+      }
+  }
+
+\NewExpandableDocumentCommand \ExpTblrChildId {m}
+  {
+    \use:c { l__tblr_child_id_#1_tl }
+  }
+
+\NewExpandableDocumentCommand \ExpTblrChildClass {m}
+  {
+    \use:c { l__tblr_child_class_#1_clist }
+  }
+
+%%% --------------------------------------------------------
+%%> \section{New content commands}
+%%% --------------------------------------------------------
+
 %% We need to emulate or fix some commands such as \diagbox in other packages
-%% These commands must be defined with \NewContentCommand command
+%% These commands must be defined with \NewTblrContentCommand command
 %% We only enable them inside tblr environment to avoid potential conflict
 
 \clist_new:N \g__tblr_content_commands_clist
@@ -1074,7 +1845,7 @@
 \msg_new:nnn { tabularray } { defined-content-command }
   { Content ~ command ~ #1 already ~ defined! }
 
-\NewDocumentCommand \NewContentCommand { m O{0} o m }
+\NewDocumentCommand \NewTblrContentCommand { m O{0} o m }
   {
     \clist_if_in:NnTF \g__tblr_content_commands_clist { #1 }
       {
@@ -1088,6 +1859,7 @@
         \clist_gput_right:Nn \g__tblr_content_commands_clist { #1 }
       }
   }
+\cs_set_eq:NN \NewContentCommand \NewTblrContentCommand
 
 \cs_new_protected:Npn \__tblr_enable_content_commands:
   {
@@ -1096,64 +1868,77 @@
   }
 
 %%% --------------------------------------------------------
-%%> \section{New Dash Styles}
+%%> \section{New dash styles}
 %%% --------------------------------------------------------
 
-%% \NewDashStyle commands
+%% \NewTblrDashStyle commands
 
-\dim_zero_new:N \rulewidth
-\dim_set:Nn \rulewidth {0.4pt}
+\dim_new:N \lTblrDefaultHruleWidthDim
+\dim_new:N \lTblrDefaultVruleWidthDim
+\dim_set:Nn \lTblrDefaultHruleWidthDim {0.4pt}
+\dim_set:Nn \lTblrDefaultVruleWidthDim {0.4pt}
 
 \prop_new:N \g__tblr_defined_hdash_styles_prop
 \prop_new:N \g__tblr_defined_vdash_styles_prop
 
 \prop_gset_from_keyval:Nn \g__tblr_defined_hdash_styles_prop
-  { solid = \hrule height \rulewidth }
+  { solid = \hrule height \lTblrDefaultHruleWidthDim }
 \prop_gset_from_keyval:Nn \g__tblr_defined_vdash_styles_prop
-  { solid = \vrule width \rulewidth }
+  { solid = \vrule width \lTblrDefaultVruleWidthDim }
 
-\NewDocumentCommand \NewDashStyle { m m }
+\NewDocumentCommand \NewTblrDashStyle { m m }
   {
     \seq_set_split:Nnn \l_tmpa_seq { ~ } {#2}
-    \tl_set:Nx \l__tblr_a_tl { \seq_item:Nn \l_tmpa_seq {1} }
-    \tl_set:Nx \l__tblr_b_tl { \seq_item:Nn \l_tmpa_seq {2} }
-    \tl_set:Nx \l__tblr_c_tl { \seq_item:Nn \l_tmpa_seq {3} }
-    \tl_set:Nx \l__tblr_d_tl { \seq_item:Nn \l_tmpa_seq {4} }
+    \tl_set:Ne \l__tblr_a_tl { \seq_item:Nn \l_tmpa_seq {1} }
+    \tl_set:Ne \l__tblr_b_tl { \seq_item:Nn \l_tmpa_seq {2} }
+    \tl_set:Ne \l__tblr_c_tl { \seq_item:Nn \l_tmpa_seq {3} }
+    \tl_set:Ne \l__tblr_d_tl { \seq_item:Nn \l_tmpa_seq {4} }
     \tl_if_eq:NnT \l__tblr_a_tl { on }
       {
         \tl_if_eq:NnT \l__tblr_c_tl { off }
           {
-            \__tblr_dash_style_make_boxes:nxx {#1}
+            \__tblr_dash_style_make_boxes:nee {#1}
               { \dim_eval:n {\l__tblr_b_tl} } { \dim_eval:n {\l__tblr_d_tl} }
           }
       }
   }
+\cs_set_eq:NN \NewDashStyle \NewTblrDashStyle
 
 \cs_new_protected:Npn \__tblr_dash_style_make_boxes:nnn #1 #2 #3
   {
     \dim_set:Nn \l_tmpa_dim { #2 + #3 }
     \tl_set:Nn \l__tblr_h_tl { \hbox_to_wd:nn }
-    \tl_put_right:Nx \l__tblr_h_tl { { \dim_use:N \l_tmpa_dim } }
+    \tl_put_right:Ne \l__tblr_h_tl { { \dim_use:N \l_tmpa_dim } }
     \tl_put_right:Nn \l__tblr_h_tl
       {
-        { \hss \vbox:n { \hbox_to_wd:nn {#2} {} \hrule height \rulewidth } \hss }
+        {
+          \hss
+          \vbox:n
+            { \hbox_to_wd:nn {#2} {} \hrule height \lTblrDefaultHruleWidthDim }
+          \hss
+        }
       }
     \prop_gput:NnV \g__tblr_defined_hdash_styles_prop {#1} \l__tblr_h_tl
     %\prop_log:N \g__tblr_defined_hdash_styles_prop
     \tl_set:Nn \l__tblr_v_tl { \vbox_to_ht:nn }
-    \tl_put_right:Nx \l__tblr_v_tl { { \dim_use:N \l_tmpa_dim } }
+    \tl_put_right:Ne \l__tblr_v_tl { { \dim_use:N \l_tmpa_dim } }
     \tl_put_right:Nn \l__tblr_v_tl
       {
-        { \vss \hbox:n { \vbox_to_ht:nn {#2} {} \vrule width \rulewidth } \vss }
+        {
+          \vss
+          \hbox:n
+            { \vbox_to_ht:nn {#2} {} \vrule width \lTblrDefaultVruleWidthDim }
+          \vss
+        }
       }
     \prop_gput:NnV \g__tblr_defined_vdash_styles_prop {#1} \l__tblr_v_tl
     %\prop_log:N \g__tblr_defined_vdash_styles_prop
   }
-\cs_generate_variant:Nn \__tblr_dash_style_make_boxes:nnn { nxx }
+\cs_generate_variant:Nn \__tblr_dash_style_make_boxes:nnn { nee }
 
 \cs_new_protected:Npn \__tblr_get_hline_dash_style:N #1
   {
-    \tl_set:Nx \l_tmpa_tl
+    \tl_set:Ne \l_tmpa_tl
       { \prop_item:NV \g__tblr_defined_hdash_styles_prop #1 }
     \tl_if_empty:NF \l_tmpa_tl { \tl_set_eq:NN #1 \l_tmpa_tl }
   }
@@ -1160,25 +1945,23 @@
 
 \cs_new_protected:Npn \__tblr_get_vline_dash_style:N #1
   {
-    \tl_set:Nx \l_tmpa_tl
+    \tl_set:Ne \l_tmpa_tl
       { \prop_item:NV \g__tblr_defined_vdash_styles_prop #1 }
     \tl_if_empty:NF \l_tmpa_tl { \tl_set_eq:NN #1 \l_tmpa_tl }
   }
 
-\NewDashStyle {dashed} {on ~ 2pt ~ off ~ 2pt}
-\NewDashStyle {dotted} {on ~ 0.4pt ~ off ~ 1pt}
+\NewTblrDashStyle {dashed} {on ~ 2pt ~ off ~ 2pt}
+\NewTblrDashStyle {dotted} {on ~ 0.4pt ~ off ~ 1pt}
 
 %%% --------------------------------------------------------
-%%> \section{Set Hlines and Vlines}
+%%> \section{Set hlines and vlines}
 %%% --------------------------------------------------------
 
-\tl_const:Nn \@tblr at dash { dash }
-\tl_const:Nn \@tblr at text { text }
+\quark_new:N \q__tblr_dash
+\quark_new:N \q__tblr_text
 
-\regex_const:Nn \c__tblr_is_color_key_regex { ^[A-Za-z] }
-
 %% \SetHlines command for setting every hline in the table
-\NewTableCommand \SetHlines [3] [+]
+\NewTblrTableCommand \SetHlines [3] [+]
   {
     \tblr_set_every_hline:nnn {#1} {#2} {#3}
   }
@@ -1220,14 +2003,14 @@
 \tl_new:N \l__tblr_hline_rightpos_tl % right position
 \bool_new:N \l__tblr_hline_endpos_bool % whether set positions only for both ends
 
-\NewTableCommand \cline [2] [] { \SetHline [=] {#2} {#1} }
+\NewTblrTableCommand \cline [2] [] { \SetHline [=] {#2} {#1} }
 
-\NewTableCommand \hline [1] [] { \SetHline [+] {-} {#1} }
+\NewTblrTableCommand \hline [1] [] { \SetHline [+] {-} {#1} }
 
 %% #1: the index of the hline (may be + or =)
 %% #2: which columns of the hline, separate by commas
 %% #3: key=value pairs
-\NewTableCommand \SetHline [3] [+]
+\NewTblrTableCommand \SetHline [3] [+]
   {
     \tblr_set_hline:nnn {#1} {#2} {#3}
   }
@@ -1237,12 +2020,13 @@
 \cs_new_protected:Npn \tblr_set_hline:nnn #1 #2 #3
   {
     \group_begin:
-    \keys_set_groups:nnn { tblr-hline } { text } {#3}
-    \tl_if_eq:NnF \l__tblr_hline_dash_tl { \exp_not:N \@tblr at text }
+    \__tblr_keys_set_groups:nnn { hline/inner } { text } {#3}
+    % true if "text=" is set
+    \tl_if_eq:NNF \l__tblr_hline_dash_tl \q__tblr_text
       {
         \__tblr_set_hline_num:n {#1}
         \tl_clear:N \l__tblr_hline_dash_tl
-        \keys_set:nn { tblr-hline } { dash = solid, #3 }
+        \__tblr_keys_set:nn { hline/inner } { dash = solid, #3 }
         \__tblr_set_hline_cmd:n {#2}
       }
     \group_end:
@@ -1251,8 +2035,8 @@
 \cs_new_protected:Npn \tblr_set_hline:nnnn #1 #2 #3 #4
   {
     \group_begin:
-    \__tblr_get_childs:nx {#1} { \int_eval:n { \c at rowcount + 1 } }
-    \clist_map_inline:Nn \l_tblr_childs_clist
+    \__tblr_child_parse:ne {#1} { \int_eval:n { \c at rowcount + 1 } }
+    \__tblr_child_map_inline:n
       {
         \int_set:Nn \c at rownum {##1}
         \tblr_set_hline:nnn {#2} {#3} {#4}
@@ -1279,12 +2063,12 @@
 \cs_new_protected:Npn \__tblr_set_hline_num:n #1
   {
     \tl_clear:N \l__tblr_hline_num_tl
-    \tl_set:Nx \l__tblr_hline_count_tl
+    \tl_set:Ne \l__tblr_hline_count_tl
       { \__tblr_spec_item:ne { hline } { [\int_use:N \c at rownum] / @hline-count } }
     %% \l__tblr_hline_count_tl may be empty when rowspec has extra |'s
     \int_compare:nNnTF { \l__tblr_hline_count_tl + 0 } = {0}
       {
-        \tl_set:Nx \l__tblr_hline_num_tl { 1 }
+        \tl_set:Ne \l__tblr_hline_num_tl { 1 }
         \__tblr_spec_gput:nen { hline }
           { [\int_use:N \c at rownum] / @hline-count } { 1 }
       }
@@ -1305,7 +2089,7 @@
 
 \cs_new_protected:Npn \__tblr_set_hline_num_incr:
   {
-    \tl_set:Nx \l__tblr_hline_count_tl
+    \tl_set:Ne \l__tblr_hline_count_tl
       { \int_eval:n { \l__tblr_hline_count_tl + 1 } }
     \__tblr_spec_gput:nee { hline }
       { [\int_use:N \c at rownum] / @hline-count } { \l__tblr_hline_count_tl }
@@ -1312,15 +2096,15 @@
     \tl_set_eq:NN \l__tblr_hline_num_tl \l__tblr_hline_count_tl
   }
 
-\keys_define:nn { tblr-hline }
+\__tblr_keys_define:nn { hline/inner }
   {
-    dash .code:n = \tl_set:Nn \l__tblr_hline_dash_tl { \exp_not:N \@tblr at dash #1 },
-    text .code:n = \tl_set:Nn \l__tblr_hline_dash_tl { \exp_not:N \@tblr at text #1 },
+    dash .code:n = \tl_set:Nn \l__tblr_hline_dash_tl { \q__tblr_dash #1 },
+    text .code:n = \tl_set:Nn \l__tblr_hline_dash_tl { \q__tblr_text #1 },
     text .groups:n = { text },
     wd .code:n = \tl_set:Nn \l__tblr_hline_wd_tl { \dim_eval:n {#1} },
     fg .code:n = \tl_set:Nn \l__tblr_hline_fg_tl {#1},
-    leftpos  .code:n = \tl_set:Nx \l__tblr_hline_leftpos_tl {#1},
-    rightpos .code:n = \tl_set:Nx \l__tblr_hline_rightpos_tl {#1},
+    leftpos  .code:n = \tl_set:Ne \l__tblr_hline_leftpos_tl {#1},
+    rightpos .code:n = \tl_set:Ne \l__tblr_hline_rightpos_tl {#1},
     l        .meta:n = { leftpos = #1 },
     l     .default:n = { -0.8 },
     r        .meta:n = { rightpos = #1 },
@@ -1334,9 +2118,9 @@
 \cs_new_protected:Npn \__tblr_hline_unknown_key:n #1
   {
     \prop_if_in:NnTF \g__tblr_defined_hdash_styles_prop {#1}
-      { \tl_set:Nn \l__tblr_hline_dash_tl { \exp_not:N \@tblr at dash #1 } }
+      { \tl_set:Nn \l__tblr_hline_dash_tl { \q__tblr_dash #1 } }
       {
-        \regex_match:NnTF \c__tblr_is_color_key_regex {#1}
+        \__tblr_if_color_value:nTF {#1}
           { \tl_set:Nn \l__tblr_hline_fg_tl {#1} }
           {
             \tl_set_rescan:Nnn \l__tblr_v_tl {} {#1}
@@ -1348,10 +2132,12 @@
 
 \cs_new_protected_nopar:Npn \__tblr_set_hline_cmd:n #1
   {
-    \__tblr_get_childs:nx {#1} { \int_use:N \c at colcount }
-    \clist_map_inline:Nn \l_tblr_childs_clist
+    \__tblr_child_parse:ne {#1} { \int_use:N \c at colcount }
+    \__tblr_child_map_inline:n
       {
-        \__tblr_set_hline_option:nnn { ##1 } { @dash } { \l__tblr_hline_dash_tl }
+        % prevent expansion of vline text (see issue #303)
+        \__tblr_set_hline_option:nnn { ##1 } { @dash } 
+          { \exp_not:V \l__tblr_hline_dash_tl }
         \tl_if_empty:NF \l__tblr_hline_wd_tl
           {
             \__tblr_set_hline_option:nnn { ##1 } { wd } { \l__tblr_hline_wd_tl }
@@ -1365,13 +2151,12 @@
       {
         \bool_if:NTF \l__tblr_hline_endpos_bool
           {
+            \__tblr_child_get_first:N \l_tmpa_tl
             \__tblr_set_hline_option:nnn
-              { \clist_item:Nn \l_tblr_childs_clist {1} }
-              { leftpos }
-              { \l__tblr_hline_leftpos_tl }
+              { \l_tmpa_tl } { leftpos } { \l__tblr_hline_leftpos_tl }
           }
           {
-            \clist_map_inline:Nn \l_tblr_childs_clist
+            \__tblr_child_map_inline:n
               {
                 \__tblr_set_hline_option:nnn
                   { ##1 } { leftpos } { \l__tblr_hline_leftpos_tl }
@@ -1382,13 +2167,12 @@
       {
         \bool_if:NTF \l__tblr_hline_endpos_bool
           {
+            \__tblr_child_get_last:N \l_tmpb_tl
             \__tblr_set_hline_option:nnn
-              { \clist_item:Nn \l_tblr_childs_clist {-1} }
-              { rightpos }
-              { \l__tblr_hline_rightpos_tl }
+              { \l_tmpb_tl } { rightpos } { \l__tblr_hline_rightpos_tl }
           }
           {
-            \clist_map_inline:Nn \l_tblr_childs_clist
+            \__tblr_child_map_inline:n
               {
                 \__tblr_set_hline_option:nnn
                   { ##1 } { rightpos } { \l__tblr_hline_rightpos_tl }
@@ -1410,18 +2194,18 @@
 \msg_new:nnn { tabularray } { obsolete-lasthline }
   { \lasthline ~ is ~ obsolete; ~ use ~ 'baseline=B' ~ instead. }
 
-\NewTableCommand \firsthline [1] []
+\NewTblrTableCommand \firsthline [1] []
   {
     \msg_error:nn { tabularray } { obsolete-firsthline }
   }
 
-\NewTableCommand \lasthline [1] []
+\NewTblrTableCommand \lasthline [1] []
   {
     \msg_error:nn { tabularray } { obsolete-lasthline }
   }
 
 %% \SetVlines command for setting every vline in the table
-\NewTableCommand \SetVlines [3] [+]
+\NewTblrTableCommand \SetVlines [3] [+]
   {
     \tblr_set_every_vline:nnn {#1} {#2} {#3}
   }
@@ -1430,7 +2214,7 @@
 \cs_new_protected:Npn \tblr_set_every_vline:nnn #1 #2 #3
   {
     \group_begin:
-    \int_step_inline:nn { \int_eval:n { \c at colcount + 1 } }
+    \int_step_inline:nn { \c at colcount + 1 }
       {
         \int_set:Nn \c at colnum {##1}
         \tblr_set_vline:nnn {#1} {#2} {#3}
@@ -1462,14 +2246,14 @@
 \tl_new:N \l__tblr_vline_abovepos_tl % above position
 \tl_new:N \l__tblr_vline_belowpos_tl % below position
 
-\NewTableCommand \rline [2] [] { \SetVline [=] {#2} {#1} }
+\NewTblrTableCommand \rline [2] [] { \SetVline [=] {#2} {#1} }
 
-\NewTableCommand \vline [1] [] { \SetVline [+] {-} {#1} }
+\NewTblrTableCommand \vline [1] [] { \SetVline [+] {-} {#1} }
 
 %% #1: the index of the vline (may be + or =)
 %% #2: which rows of the vline, separate by commas
 %% #3: key=value pairs
-\NewTableCommand \SetVline [3] [+]
+\NewTblrTableCommand \SetVline [3] [+]
   {
     \tblr_set_vline:nnn {#1} {#2} {#3}
   }
@@ -1479,12 +2263,13 @@
 \cs_new_protected:Npn \tblr_set_vline:nnn #1 #2 #3
   {
     \group_begin:
-    \keys_set_groups:nnn { tblr-vline } { text } {#3}
-    \tl_if_eq:NnF \l__tblr_vline_dash_tl { \exp_not:N \@tblr at text }
+    \__tblr_keys_set_groups:nnn { vline/inner } { text } {#3}
+    % true if "text=" is set
+    \tl_if_eq:NNF \l__tblr_vline_dash_tl \q__tblr_text
       {
         \__tblr_set_vline_num:n {#1}
         \tl_clear:N \l__tblr_vline_dash_tl
-        \keys_set:nn { tblr-vline } { dash = solid, #3 }
+        \__tblr_keys_set:nn { vline/inner } { dash = solid, #3 }
         \__tblr_set_vline_cmd:n {#2}
       }
     \group_end:
@@ -1493,8 +2278,8 @@
 \cs_new_protected:Npn \tblr_set_vline:nnnn #1 #2 #3 #4
   {
     \group_begin:
-    \__tblr_get_childs:nx {#1} { \int_eval:n { \c at colcount + 1} }
-    \clist_map_inline:Nn \l_tblr_childs_clist
+    \__tblr_child_parse:ne {#1} { \int_eval:n { \c at colcount + 1} }
+    \__tblr_child_map_inline:n
       {
         \int_set:Nn \c at colnum {##1}
         \tblr_set_vline:nnn {#2} {#3} {#4}
@@ -1521,12 +2306,12 @@
 \cs_new_protected:Npn \__tblr_set_vline_num:n #1
   {
     \tl_clear:N \l__tblr_vline_num_tl
-    \tl_set:Nx \l__tblr_vline_count_tl
+    \tl_set:Ne \l__tblr_vline_count_tl
       { \__tblr_spec_item:ne { vline } { [\int_use:N \c at colnum] / @vline-count } }
     %% \l__tblr_vline_count_tl may be empty when colspec has extra |'s
     \int_compare:nNnTF { \l__tblr_vline_count_tl + 0 } = {0}
       {
-        \tl_set:Nx \l__tblr_vline_num_tl { 1 }
+        \tl_set:Ne \l__tblr_vline_num_tl { 1 }
         \__tblr_spec_gput:nen { vline }
           { [\int_use:N \c at colnum] / @vline-count } { 1 }
       }
@@ -1547,7 +2332,7 @@
 
 \cs_new_protected:Npn \__tblr_set_vline_num_incr:
   {
-    \tl_set:Nx \l__tblr_vline_count_tl
+    \tl_set:Ne \l__tblr_vline_count_tl
       { \int_eval:n { \l__tblr_vline_count_tl + 1 } }
     \__tblr_spec_gput:nee { vline }
       { [\int_use:N \c at colnum] / @vline-count } { \l__tblr_vline_count_tl }
@@ -1554,15 +2339,15 @@
     \tl_set_eq:NN \l__tblr_vline_num_tl \l__tblr_vline_count_tl
   }
 
-\keys_define:nn { tblr-vline }
+\__tblr_keys_define:nn { vline/inner }
   {
-    dash .code:n = \tl_set:Nn \l__tblr_vline_dash_tl { \exp_not:N \@tblr at dash #1 },
-    text .code:n = \tl_set:Nn \l__tblr_vline_dash_tl { \exp_not:N \@tblr at text #1 },
+    dash .code:n = \tl_set:Nn \l__tblr_vline_dash_tl { \q__tblr_dash #1 },
+    text .code:n = \tl_set:Nn \l__tblr_vline_dash_tl { \q__tblr_text #1 },
     text .groups:n = { text },
     wd .code:n = \tl_set:Nn \l__tblr_vline_wd_tl { \dim_eval:n {#1} },
     fg .code:n = \tl_set:Nn \l__tblr_vline_fg_tl {#1},
-    abovepos .code:n = \tl_set:Nx \l__tblr_vline_abovepos_tl {#1},
-    belowpos .code:n = \tl_set:Nx \l__tblr_vline_belowpos_tl {#1},
+    abovepos .code:n = \tl_set:Ne \l__tblr_vline_abovepos_tl {#1},
+    belowpos .code:n = \tl_set:Ne \l__tblr_vline_belowpos_tl {#1},
     unknown .code:n = \__tblr_vline_unknown_key:V \l_keys_key_str,
   }
 
@@ -1569,9 +2354,9 @@
 \cs_new_protected:Npn \__tblr_vline_unknown_key:n #1
   {
     \prop_if_in:NnTF \g__tblr_defined_vdash_styles_prop {#1}
-      { \tl_set:Nn \l__tblr_vline_dash_tl { \exp_not:N \@tblr at dash #1 } }
+      { \tl_set:Nn \l__tblr_vline_dash_tl { \q__tblr_dash #1 } }
       {
-        \regex_match:NnTF \c__tblr_is_color_key_regex {#1}
+        \__tblr_if_color_value:nTF {#1}
           { \tl_set:Nn \l__tblr_vline_fg_tl {#1} }
           {
             \tl_set_rescan:Nnn \l__tblr_v_tl {} {#1}
@@ -1583,12 +2368,13 @@
 
 \cs_new_protected_nopar:Npn \__tblr_set_vline_cmd:n #1
   {
-    \__tblr_get_childs:nx {#1} { \int_use:N \c at rowcount }
-    \clist_map_inline:Nn \l_tblr_childs_clist
+    \__tblr_child_parse:ne {#1} { \int_use:N \c at rowcount }
+    \__tblr_child_map_inline:n
       {
         \__tblr_spec_gput:nee { vline }
           { [##1][\int_use:N \c at colnum](\l__tblr_vline_num_tl) / @dash }
-          { \l__tblr_vline_dash_tl }
+          % prevent expansion of vline text (see issue #303)
+          { \exp_not:V \l__tblr_vline_dash_tl }
         \tl_if_empty:NF \l__tblr_vline_wd_tl
           {
             \__tblr_spec_gput:nee { vline }
@@ -1617,22 +2403,22 @@
   }
 
 %%% --------------------------------------------------------
-%%>  \section{Set Hborders and Vborders}
+%%>  \section{Set hborders and vborders}
 %%% --------------------------------------------------------
 
 %% Hborder holds keys not related to a specified hline
-\NewTableCommand \hborder [1] { \tblr_set_hborder:n {#1} }
+\NewTblrTableCommand \hborder [1] { \tblr_set_hborder:n {#1} }
 
 \cs_new_protected:Npn \tblr_set_hborder:n #1
   {
-    \keys_set:nn { tblr-hborder } {#1}
+    \__tblr_keys_set:nn { hborder/inner } {#1}
   }
 
 \cs_new_protected:Npn \tblr_set_hborder:nn #1 #2
   {
     \group_begin:
-    \__tblr_get_childs:nx {#1} { \int_eval:n { \c at rowcount + 1 } }
-    \clist_map_inline:Nn \l_tblr_childs_clist
+    \__tblr_child_parse:ne {#1} { \int_eval:n { \c at rowcount + 1 } }
+    \__tblr_child_map_inline:n
       {
         \int_set:Nn \c at rownum {##1}
         \tblr_set_hborder:n {#2}
@@ -1648,7 +2434,7 @@
   }
 \cs_generate_variant:Nn \__tblr_set_hborder_aux:nn { Vn }
 
-\keys_define:nn { tblr-hborder }
+\__tblr_keys_define:nn { hborder/inner }
   {
     abovespace .code:n = \__tblr_row_gput_above:ne
                           { belowsep } { \dim_eval:n {#1} },
@@ -1678,18 +2464,18 @@
   }
 
 %% Vborder holds keys not related to a specified vline
-\NewTableCommand \vborder [1] { \tblr_set_vborder:n {#1} }
+\NewTblrTableCommand \vborder [1] { \tblr_set_vborder:n {#1} }
 
 \cs_new_protected:Npn \tblr_set_vborder:n #1
   {
-    \keys_set:nn { tblr-vborder } {#1}
+    \__tblr_keys_set:nn { vborder/inner } {#1}
   }
 
 \cs_new_protected:Npn \tblr_set_vborder:nn #1 #2
   {
     \group_begin:
-    \__tblr_get_childs:nx {#1} { \int_eval:n { \c at colcount + 1 } }
-    \clist_map_inline:Nn \l_tblr_childs_clist
+    \__tblr_child_parse:ne {#1} { \int_eval:n { \c at colcount + 1 } }
+    \__tblr_child_map_inline:n
       {
         \int_set:Nn \c at colnum {##1}
         \tblr_set_vborder:n {#2}
@@ -1705,7 +2491,7 @@
   }
 \cs_generate_variant:Nn \__tblr_set_vborder_aux:nn { Vn }
 
-\keys_define:nn { tblr-vborder }
+\__tblr_keys_define:nn { vborder/inner }
   {
     leftspace .code:n = \__tblr_column_gput_left:ne
                           { rightsep } { \dim_eval:n {#1} },
@@ -1717,11 +2503,11 @@
   }
 
 %%% --------------------------------------------------------
-%%> \section{Set Cells}
+%%> \section{Set cells}
 %%% --------------------------------------------------------
 
 %% \SetCells command for setting every cell in the table
-\NewTableCommand \SetCells [2] []
+\NewTblrTableCommand \SetCells [2] []
   {
     \tblr_set_every_cell:nn {#1} {#2}
   }
@@ -1753,7 +2539,7 @@
 
 %% \SetCell command for multirow and/or multicolumn cells
 
-\NewTableCommand \SetCell [2] []
+\NewTblrTableCommand \SetCell [2] []
   {
     \tblr_set_cell:nn { #1 } { #2 }
   }
@@ -1765,8 +2551,8 @@
   {
     \tl_set:Nn \l__tblr_row_span_num_tl { 1 }
     \tl_set:Nn \l__tblr_col_span_num_tl { 1 }
-    \keys_set:nn { tblr-cell-span } { #1 }
-    \keys_set:nn { tblr-cell-spec } { #2 }
+    \__tblr_keys_set:nn { cell/outer } { #1 }
+    \__tblr_keys_set:nn { cell/inner } { #2 }
     \__tblr_set_span_spec:VV \l__tblr_row_span_num_tl \l__tblr_col_span_num_tl
   }
 \cs_generate_variant:Nn \tblr_set_cell:nn { nV }
@@ -1774,14 +2560,12 @@
 \cs_new_protected:Npn \tblr_set_cell:nnnn #1 #2 #3 #4
   {
     \group_begin:
-    \__tblr_get_childs:nx {#1} { \int_use:N \c at rowcount }
-    \clist_set_eq:NN \l_tmpa_clist \l_tblr_childs_clist
-    \__tblr_get_childs:nx {#2} { \int_use:N \c at colcount }
-    \clist_set_eq:NN \l_tmpb_clist \l_tblr_childs_clist
-    \clist_map_inline:Nn \l_tmpa_clist
+    \__tblr_child_parse:ne {#1} { \int_use:N \c at rowcount }
+    \__tblr_child_map_inline:n
       {
         \int_set:Nn \c at rownum {##1}
-        \clist_map_inline:Nn \l_tmpb_clist
+        \__tblr_child_parse:ne {#2} { \int_use:N \c at colcount }
+        \__tblr_child_submap_inline:n
           {
             \int_set:Nn \c at colnum {####1}
             \tblr_set_cell:nn {#3} {#4}
@@ -1790,24 +2574,47 @@
     \group_end:
   }
 
-%% Check the number of arguments and call \tblr_set_cell in different ways
-%% Note that #1 is always of the type {<i>}{<j>}
-%% This function is called when parsing table specifications
+\cs_new_protected:Npn \__tblr_set_cell_tuple:nnn #1 #2 #3
+  {
+    \group_begin:
+    \__tblr_child_parse_tuple:nee {#1}
+      { \int_use:N \c at rowcount } { \int_use:N \c at colcount }
+    \clist_map_inline:Nn \l__tblr_child_tuple_whole_clist
+      {
+        \int_set:Nn \c at rownum { \use_i:nn ##1 }
+        \int_set:Nn \c at colnum { \use_ii:nn ##1 }
+        \tblr_set_cell:nn {#2} {#3}
+      }
+    \group_end:
+  }
+
+%% Check the number of arguments and call \tblr_set_cell in different ways.
+%% #1 consists of either two tl items {h_1,h_2,...,h_m}{v_1,v_2,...,v_n},
+%% or one tl item {{h_1}{v_1},{h_2}{v_2},...,{h_k}{v_k}}.
+%% This function is called when parsing table specifications.
 \cs_new_protected:Npn \__tblr_set_cell_aux:nn #1 #2
   {
-    \tl_if_head_is_group:nTF {#2}
-      { \tblr_set_cell:nnnn #1 #2 }
-      { \tblr_set_cell:nnnn #1 {} {#2} }
+    \tl_if_single:nTF {#1}
+      {
+        \tl_if_head_is_group:nTF {#2}
+          { \__tblr_set_cell_tuple:nnn #1 #2 }
+          { \__tblr_set_cell_tuple:nnn #1 {} {#2} }
+      }
+      {
+        \tl_if_head_is_group:nTF {#2}
+          { \tblr_set_cell:nnnn #1 #2 }
+          { \tblr_set_cell:nnnn #1 {} {#2} }
+      }
   }
 \cs_generate_variant:Nn \__tblr_set_cell_aux:nn { Vn }
 
-\keys_define:nn { tblr-cell-span }
+\__tblr_keys_define:nn { cell/outer }
   {
     r .tl_set:N = \l__tblr_row_span_num_tl,
     c .tl_set:N = \l__tblr_col_span_num_tl,
   }
 
-\keys_define:nn { tblr-cell-spec }
+\__tblr_keys_define:nn { cell/inner }
   {
     halign  .code:n = \__tblr_cell_gput:nn { halign } {#1},
     valign  .code:n = \__tblr_cell_gput:nn { valign } {#1},
@@ -1858,7 +2665,7 @@
 
 \cs_new_protected:Npn \__tblr_cell_preto_text:nnn #1 #2 #3
   {
-    \tl_set:Nx \l__tblr_cell_text_tl { \__tblr_spec_item:nn { text } { [#1][#2] } }
+    \tl_set:Ne \l__tblr_cell_text_tl { \__tblr_spec_item:nn { text } { [#1][#2] } }
     \tl_put_left:Nn \l__tblr_cell_text_tl {#3}
     \__tblr_spec_gput:nnV { text } { [#1][#2] } \l__tblr_cell_text_tl
   }
@@ -1872,7 +2679,7 @@
 
 \cs_new_protected:Npn \__tblr_cell_appto_text:nnn #1 #2 #3
   {
-    \tl_set:Nx \l__tblr_cell_text_tl { \__tblr_spec_item:ne { text } { [#1][#2] } }
+    \tl_set:Ne \l__tblr_cell_text_tl { \__tblr_spec_item:ne { text } { [#1][#2] } }
     \tl_put_right:Nn \l__tblr_cell_text_tl {#3}
     \__tblr_spec_gput:neV { text } { [#1][#2] } \l__tblr_cell_text_tl
   }
@@ -1880,7 +2687,7 @@
 
 \cs_new_protected:Npn \__tblr_cell_unknown_key:n #1
   {
-    \regex_match:NnTF \c__tblr_is_color_key_regex {#1}
+    \__tblr_if_color_value:nTF {#1}
       {
         \__tblr_data_gput:neene { cell }
           { \int_use:N \c at rownum } { \int_use:N \c at colnum } { background } {#1}
@@ -1894,6 +2701,9 @@
   }
 \cs_generate_variant:Nn \__tblr_cell_unknown_key:n { V }
 
+%% Whether to allow page breaks in the middle of multirow cells
+\bool_new:N \lTblrCellBreakBool
+
 \cs_new_protected:Npn \__tblr_set_span_spec:nn #1 #2
   {
     \int_compare:nNnT { #1 } > { 1 }
@@ -1911,6 +2721,12 @@
     \int_step_variable:nnNn
       { \int_use:N \c at rownum } { \int_eval:n { \c at rownum + #1 - 1 } } \l__tblr_i_tl
       {
+        \bool_lazy_and:nnT
+          { ! \lTblrCellBreakBool }
+          { \int_compare_p:nNn {\l__tblr_i_tl} > {\c at rownum} }
+          {
+            \__tblr_spec_gput:nen {hline} { [\l__tblr_i_tl] / @pagebreak } {-1}
+          }
         \int_step_variable:nnNn
           { \int_use:N \c at colnum } { \int_eval:n { \c at colnum + #2 - 1 } }
           \l__tblr_j_tl
@@ -1935,7 +2751,7 @@
           }
       }
     %% Make continuous borders for multirow cells
-    \tl_set:Nx \l__tblr_n_tl
+    \tl_set:Ne \l__tblr_n_tl
       {
         \int_max:nn
           {
@@ -1962,22 +2778,22 @@
 \msg_new:nnn { tabularray } { obsolete-multirow }
   { \multirow ~ is ~ obsolete; ~ use ~ \SetCell ~ instead. }
 
-\NewTableCommand \multicolumn [2]
+\NewTblrTableCommand \multicolumn [2]
   {
     \msg_error:nn { tabularray } { obsolete-multicolumn }
   }
 
-\NewTableCommand \multirow [3] [m]
+\NewTblrTableCommand \multirow [3] [m]
   {
     \msg_error:nn { tabularray } { obsolete-multirow }
   }
 
 %%% --------------------------------------------------------
-%%> \section{Set Columns and Rows}
+%%> \section{Set columns and rows}
 %%% --------------------------------------------------------
 
 %% \SetColumns command for setting every column in the table
-\NewTableCommand \SetColumns [2] []
+\NewTblrTableCommand \SetColumns [2] []
   {
     \tblr_set_every_column:nn {#1} {#2}
   }
@@ -2005,7 +2821,7 @@
 
 %% \SetColumn command for current column or each cells in the column
 
-\NewTableCommand \SetColumn [2] []
+\NewTblrTableCommand \SetColumn [2] []
   {
     \tblr_set_column:nn {#1} {#2}
   }
@@ -2012,14 +2828,14 @@
 
 \cs_new_protected:Npn \tblr_set_column:nn #1 #2
   {
-    \keys_set:nn { tblr-column } {#2}
+    \__tblr_keys_set:nn { column/inner } {#2}
   }
 
 \cs_new_protected:Npn \tblr_set_column:nnn #1 #2 #3
   {
     \group_begin:
-    \__tblr_get_childs:nx {#1} { \int_use:N \c at colcount }
-    \clist_map_inline:Nn \l_tblr_childs_clist
+    \__tblr_child_parse:ne {#1} { \int_use:N \c at colcount }
+    \__tblr_child_map_inline:n
       {
         \int_set:Nn \c at colnum {##1}
         \tblr_set_column:nn {#2} {#3}
@@ -2038,7 +2854,7 @@
   }
 \cs_generate_variant:Nn \__tblr_set_column_aux:nn { Vn }
 
-\keys_define:nn { tblr-column }
+\__tblr_keys_define:nn { column/inner }
   {
     halign    .code:n = \__tblr_column_gput_cell:nn { halign } {#1},
     valign    .code:n = \__tblr_column_gput_cell:nn { valign } {#1},
@@ -2127,14 +2943,12 @@
       }
   }
 
-\regex_const:Nn \c__tblr_is_number_key_regex { ^[\+\-]? (\d+|\d*\.\d+)$ }
-
 \cs_new_protected:Npn \__tblr_column_unknown_key:n #1
   {
-    \regex_match:NnTF \c__tblr_is_number_key_regex {#1}
+    \__tblr_if_number_value:nTF {#1}
       { \__tblr_column_gput:ne { coefficient } {#1} }
       {
-        \regex_match:NnTF \c__tblr_is_color_key_regex {#1}
+        \__tblr_if_color_value:nTF {#1}
           { \__tblr_column_gput_cell:nn { background } {#1} }
           {
             \tl_set_rescan:Nnn \l__tblr_v_tl {} {#1}
@@ -2145,7 +2959,7 @@
 \cs_generate_variant:Nn \__tblr_column_unknown_key:n { V }
 
 %% \SetRows command for setting every row in the table
-\NewTableCommand \SetRows [2] []
+\NewTblrTableCommand \SetRows [2] []
   {
     \tblr_set_every_row:nn {#1} {#2}
   }
@@ -2173,7 +2987,7 @@
 
 %% \SetRow command for current row or each cells in the row
 
-\NewTableCommand \SetRow [2] []
+\NewTblrTableCommand \SetRow [2] []
   {
     \tblr_set_row:nn {#1} {#2}
   }
@@ -2180,14 +2994,14 @@
 
 \cs_new_protected:Npn \tblr_set_row:nn #1 #2
   {
-    \keys_set:nn { tblr-row } {#2}
+    \__tblr_keys_set:nn { row/inner } {#2}
   }
 
 \cs_new_protected:Npn \tblr_set_row:nnn #1 #2 #3
   {
     \group_begin:
-    \__tblr_get_childs:nx {#1} { \int_use:N \c at rowcount }
-    \clist_map_inline:Nn \l_tblr_childs_clist
+    \__tblr_child_parse:ne {#1} { \int_use:N \c at rowcount }
+    \__tblr_child_map_inline:n
       {
         \int_set:Nn \c at rownum {##1}
         \tblr_set_row:nn {#2} {#3}
@@ -2206,7 +3020,7 @@
   }
 \cs_generate_variant:Nn \__tblr_set_row_aux:nn { Vn }
 
-\keys_define:nn { tblr-row }
+\__tblr_keys_define:nn { row/inner }
   {
     halign    .code:n = \__tblr_row_gput_cell:nn { halign } {#1},
     valign    .code:n = \__tblr_row_gput_cell:nn { valign } {#1},
@@ -2296,13 +3110,13 @@
 
 \cs_new_protected:Npn \__tblr_row_unknown_key:n #1
   {
-    \regex_match:NnTF \c__tblr_is_number_key_regex {#1}
+    \__tblr_if_number_value:nTF {#1}
       {
         \__tblr_data_gput:nene { row } { \int_use:N \c at rownum }
           { coefficient } {#1}
       }
       {
-        \regex_match:NnTF \c__tblr_is_color_key_regex {#1}
+        \__tblr_if_color_value:nTF {#1}
           { \__tblr_row_gput_cell:nn { background } {#1} }
           {
             \tl_set_rescan:Nnn \l__tblr_v_tl {} {#1}
@@ -2312,72 +3126,72 @@
   }
 \cs_generate_variant:Nn \__tblr_row_unknown_key:n { V }
 
-\NewTableCommand \pagebreak [1] [4]
+\NewTblrTableCommand \pagebreak [1] [4]
   {
     \hborder { pagebreak = yes }
   }
 
-\NewTableCommand \nopagebreak [1] [4]
+\NewTblrTableCommand \nopagebreak [1] [4]
   {
     \hborder { pagebreak = no }
   }
 
 %%% --------------------------------------------------------
-%%> \section{Column Types and Row Types}
+%%> \section{Column types and row types}
 %%% --------------------------------------------------------
 
 %% Some primitive column/row types
 
-\str_const:Nn \c_tblr_primitive_colrow_types_str { Q | < > }
+\str_const:Nn \cTblrPrimitiveColrowTypeStr { Q | < > }
 \tl_new:N \g__tblr_expanded_colrow_spec_tl
 
-\exp_args:Nc \NewDocumentCommand { tblr_primitive_column_type_ Q } { O{} }
+\exp_args:Nc \NewDocumentCommand { tblr_primitive_Column_type_ Q } { O{} }
   {
-    \keys_set:nn { tblr-column } { #1 }
+    \__tblr_keys_set:nn { column/inner } { #1 }
     \int_incr:N \c at colnum
     \__tblr_execute_colrow_spec_next:N
   }
-\exp_args:Nc \NewDocumentCommand { tblr_column_type_ Q } { O{} }
+\exp_args:Nc \NewDocumentCommand { tblr_Column_type_ Q } { O{} }
   {
     \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { Q[#1] }
     \__tblr_expand_colrow_spec_next:N
   }
 
-\exp_args:Nc \NewDocumentCommand { tblr_primitive_row_type_ Q } { O{} }
+\exp_args:Nc \NewDocumentCommand { tblr_primitive_Row_type_ Q } { O{} }
   {
-    \keys_set:nn { tblr-row } { #1 }
+    \__tblr_keys_set:nn { row/inner } { #1 }
     \int_incr:N \c at rownum
     \__tblr_execute_colrow_spec_next:N
   }
-\exp_args:Nc \NewDocumentCommand { tblr_row_type_ Q } { O{} }
+\exp_args:Nc \NewDocumentCommand { tblr_Row_type_ Q } { O{} }
   {
     \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { Q[#1] }
     \__tblr_expand_colrow_spec_next:N
   }
 
-\exp_args:Nc \NewDocumentCommand { tblr_primitive_column_type_ | } { O{} }
+\exp_args:Nc \NewDocumentCommand { tblr_primitive_Column_type_ | } { O{} }
   {
     \vline [#1]
     \__tblr_execute_colrow_spec_next:N
   }
-\exp_args:Nc \NewDocumentCommand { tblr_column_type_ | } { O{} }
+\exp_args:Nc \NewDocumentCommand { tblr_Column_type_ | } { O{} }
   {
     \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { |[#1] }
     \__tblr_expand_colrow_spec_next:N
   }
 
-\exp_args:Nc \NewDocumentCommand { tblr_primitive_row_type_ | } { O{} }
+\exp_args:Nc \NewDocumentCommand { tblr_primitive_Row_type_ | } { O{} }
   {
     \hline [#1]
     \__tblr_execute_colrow_spec_next:N
   }
-\exp_args:Nc \NewDocumentCommand { tblr_row_type_ | } { O{} }
+\exp_args:Nc \NewDocumentCommand { tblr_Row_type_ | } { O{} }
   {
     \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { |[#1] }
     \__tblr_expand_colrow_spec_next:N
   }
 
-\exp_args:Nc \NewDocumentCommand { tblr_primitive_column_type_ > } { O{} m }
+\exp_args:Nc \NewDocumentCommand { tblr_primitive_Column_type_ > } { O{} m }
   {
     \tl_if_blank:nF {#1}
       {
@@ -2392,13 +3206,13 @@
       }
     \__tblr_execute_colrow_spec_next:N
   }
-\exp_args:Nc \NewDocumentCommand { tblr_column_type_ > } { O{} m }
+\exp_args:Nc \NewDocumentCommand { tblr_Column_type_ > } { O{} m }
   {
     \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { >[#1]{#2} }
     \__tblr_expand_colrow_spec_next:N
   }
 
-\exp_args:Nc \NewDocumentCommand { tblr_primitive_row_type_ > } { O{} m }
+\exp_args:Nc \NewDocumentCommand { tblr_primitive_Row_type_ > } { O{} m }
   {
     \tl_if_blank:nF {#1}
       {
@@ -2411,13 +3225,13 @@
       }
     \__tblr_execute_colrow_spec_next:N
   }
-\exp_args:Nc \NewDocumentCommand { tblr_row_type_ > } { O{} m }
+\exp_args:Nc \NewDocumentCommand { tblr_Row_type_ > } { O{} m }
   {
     \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { >[#1]{#2} }
     \__tblr_expand_colrow_spec_next:N
   }
 
-\exp_args:Nc \NewDocumentCommand { tblr_primitive_column_type_ < } { O{} m }
+\exp_args:Nc \NewDocumentCommand { tblr_primitive_Column_type_ < } { O{} m }
   {
     \tl_if_blank:nF {#1}
       {
@@ -2433,13 +3247,13 @@
       }
     \__tblr_execute_colrow_spec_next:N
   }
-\exp_args:Nc \NewDocumentCommand { tblr_column_type_ < } { O{} m }
+\exp_args:Nc \NewDocumentCommand { tblr_Column_type_ < } { O{} m }
   {
     \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { <[#1]{#2} }
     \__tblr_expand_colrow_spec_next:N
   }
 
-\exp_args:Nc \NewDocumentCommand { tblr_primitive_row_type_ < } { O{} m }
+\exp_args:Nc \NewDocumentCommand { tblr_primitive_Row_type_ < } { O{} m }
   {
     \tl_if_blank:nF {#1}
       {
@@ -2455,7 +3269,7 @@
       }
     \__tblr_execute_colrow_spec_next:N
   }
-\exp_args:Nc \NewDocumentCommand { tblr_row_type_ < } { O{} m }
+\exp_args:Nc \NewDocumentCommand { tblr_Row_type_ < } { O{} m }
   {
     \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { <[#1]{#2} }
     \__tblr_expand_colrow_spec_next:N
@@ -2463,11 +3277,11 @@
 
 %% \NewColumnType/\NewRowType command and predefined column/row types
 
-\str_new:N \g_tblr_used_column_types_str
-\str_gset_eq:NN \g_tblr_used_column_types_str \c_tblr_primitive_colrow_types_str
+\str_new:N \gTblrUsedColumnTypeStr
+\str_gset_eq:NN \gTblrUsedColumnTypeStr \cTblrPrimitiveColrowTypeStr
 
-\str_new:N \g_tblr_used_row_types_str
-\str_gset_eq:NN \g_tblr_used_row_types_str \c_tblr_primitive_colrow_types_str
+\str_new:N \gTblrUsedRowTypeStr
+\str_gset_eq:NN \gTblrUsedRowTypeStr \cTblrPrimitiveColrowTypeStr
 
 \bool_new:N \g__tblr_colrow_spec_expand_stop_bool
 \tl_new:N \g__tblr_column_or_row_tl
@@ -2475,34 +3289,37 @@
 \msg_new:nnn { tabularray } { used-colrow-type }
   { #1 ~ type ~ name ~ #2 ~ has ~ been ~ used! }
 
-\NewDocumentCommand \NewColumnType { m O{0} o m }
+\NewDocumentCommand \NewTblrColumnType { m O{0} o m }
   {
-    \tl_set:Nn \g__tblr_column_or_row_tl { column }
+    \tl_gset:Nn \g__tblr_column_or_row_tl { Column }
     \__tblr_new_column_or_row_type:nnnn {#1} {#2} {#3} {#4}
   }
+\cs_set_eq:NN \NewColumnType \NewTblrColumnType
 
-\NewDocumentCommand \NewRowType { m O{0} o m }
+\NewDocumentCommand \NewTblrRowType { m O{0} o m }
   {
-    \tl_set:Nn \g__tblr_column_or_row_tl { row }
+    \tl_gset:Nn \g__tblr_column_or_row_tl { Row }
     \__tblr_new_column_or_row_type:nnnn {#1} {#2} {#3} {#4}
   }
+\cs_set_eq:NN \NewRowType \NewTblrRowType
 
-\NewDocumentCommand \NewColumnRowType { m O{0} o m }
+\NewDocumentCommand \NewTblrColumnRowType { m O{0} o m }
   {
-    \tl_set:Nn \g__tblr_column_or_row_tl { column }
+    \tl_gset:Nn \g__tblr_column_or_row_tl { Column }
     \__tblr_new_column_or_row_type:nnnn {#1} {#2} {#3} {#4}
-    \tl_set:Nn \g__tblr_column_or_row_tl { row }
+    \tl_gset:Nn \g__tblr_column_or_row_tl { Row }
     \__tblr_new_column_or_row_type:nnnn {#1} {#2} {#3} {#4}
   }
+\cs_set_eq:NN \NewColumnRowType \NewTblrColumnRowType
 
 \cs_new_protected:Npn \__tblr_new_column_or_row_type:nnnn #1 #2 #3 #4
   {
-    \str_if_in:cnTF { g_tblr_used_ \g__tblr_column_or_row_tl _types_str } {#1}
+    \str_if_in:cnTF { gTblrUsed \g__tblr_column_or_row_tl TypeStr } {#1}
       {
-        \tl_if_eq:NnTF \g__tblr_column_or_row_tl { row }
+        \tl_if_eq:NnTF \g__tblr_column_or_row_tl { Row }
           { \msg_error:nnnn { tabularray } { used-colrow-type } { Row } {#1} }
           { \msg_error:nnnn { tabularray } { used-colrow-type } { Column } {#1} }
-        \str_log:c { g_tblr_used_ \g__tblr_column_or_row_tl _types_str }
+        \str_log:c { gTblrUsed \g__tblr_column_or_row_tl TypeStr }
       }
       {
         \__tblr_make_xparse_arg_spec:nnN {#2} {#3} \l__tblr_a_tl
@@ -2514,34 +3331,34 @@
             \__tblr_expand_colrow_spec_next:N
           }
         \str_gput_right:cn
-          { g_tblr_used_ \g__tblr_column_or_row_tl _types_str } {#1}
+          { gTblrUsed \g__tblr_column_or_row_tl TypeStr } {#1}
       }
   }
 
-\NewColumnRowType { l } { Q[l] }
-\NewColumnRowType { c } { Q[c] }
-\NewColumnRowType { r } { Q[r] }
-\NewColumnRowType { j } { Q[j] }
+\NewTblrColumnRowType { l } { Q[l] }
+\NewTblrColumnRowType { c } { Q[c] }
+\NewTblrColumnRowType { r } { Q[r] }
+\NewTblrColumnRowType { j } { Q[j] }
 
-\NewColumnType { t } [1] { Q[t,wd=#1] }
-\NewColumnType { p } [1] { Q[p,wd=#1] }
-\NewColumnType { m } [1] { Q[m,wd=#1] }
-\NewColumnType { b } [1] { Q[b,wd=#1] }
-\NewColumnType { h } [1] { Q[h,wd=#1] }
-\NewColumnType { f } [1] { Q[f,wd=#1] }
+\NewTblrColumnType { t } [1] { Q[t,wd=#1] }
+\NewTblrColumnType { p } [1] { Q[p,wd=#1] }
+\NewTblrColumnType { m } [1] { Q[m,wd=#1] }
+\NewTblrColumnType { b } [1] { Q[b,wd=#1] }
+\NewTblrColumnType { h } [1] { Q[h,wd=#1] }
+\NewTblrColumnType { f } [1] { Q[f,wd=#1] }
 
-\NewRowType { t } [1] { Q[t,ht=#1] }
-\NewRowType { p } [1] { Q[p,ht=#1] }
-\NewRowType { m } [1] { Q[m,ht=#1] }
-\NewRowType { b } [1] { Q[b,ht=#1] }
-\NewRowType { h } [1] { Q[h,ht=#1] }
-\NewRowType { f } [1] { Q[f,ht=#1] }
+\NewTblrRowType { t } [1] { Q[t,ht=#1] }
+\NewTblrRowType { p } [1] { Q[p,ht=#1] }
+\NewTblrRowType { m } [1] { Q[m,ht=#1] }
+\NewTblrRowType { b } [1] { Q[b,ht=#1] }
+\NewTblrRowType { h } [1] { Q[h,ht=#1] }
+\NewTblrRowType { f } [1] { Q[f,ht=#1] }
 
-\NewColumnRowType { X } [1][] { Q[co=1,#1] }
+\NewTblrColumnRowType { X } [1][] { Q[co=1,#1] }
 
-\NewColumnRowType { ! } [1] { |[text={#1}] }
-\NewColumnRowType { @ } [1] { <[0pt]{} |[text={#1}] >[0pt]{} }
-\NewColumnRowType { * } [2] { \prg_replicate:nn {#1} {#2} }
+\NewTblrColumnRowType { ! } [1] { |[text={#1}] }
+\NewTblrColumnRowType { @ } [1] { <[0pt]{} |[text={#1}] >[0pt]{} }
+\NewTblrColumnRowType { * } [2] { \prg_replicate:nn {#1} {#2} }
 
 \cs_new_protected:Npn \__tblr_parse_colrow_spec:nn #1 #2
   {
@@ -2583,7 +3400,7 @@
           }
       }
       {
-        \str_if_in:cnTF { g_tblr_used_ \g__tblr_column_or_row_tl _types_str } {#1}
+        \str_if_in:cnTF { gTblrUsed \g__tblr_column_or_row_tl TypeStr } {#1}
           {
             %% Note that #1 may be an active character (see issue #58)
             \cs:w tblr_ \g__tblr_column_or_row_tl _type_ \token_to_str:N #1 \cs_end:
@@ -2591,7 +3408,7 @@
           {
             \msg_error:nnVn { tabularray } { unknown-colrow-type }
               \g__tblr_column_or_row_tl {#1}
-            \str_log:c { g_tblr_used_ \g__tblr_column_or_row_tl _types_str }
+            \str_log:c { gTblrUsed \g__tblr_column_or_row_tl TypeStr }
           }
       }
   }
@@ -2600,7 +3417,7 @@
 
 \cs_new_protected:Npn \__tblr_execute_colrow_spec:N #1
   {
-    \tl_if_eq:NnTF \g__tblr_column_or_row_tl { row }
+    \tl_if_eq:NnTF \g__tblr_column_or_row_tl { Row }
       { \int_set:Nn \c at rownum {1} }
       { \int_set:Nn \c at colnum {1} }
     \exp_last_unbraced:NV \__tblr_execute_colrow_spec_next:N #1 \scan_stop:
@@ -2613,7 +3430,7 @@
   }
 
 %%% --------------------------------------------------------
-%%> \section{Set Environments and New Environments}
+%%> \section{Set environments and new environments}
 %%% --------------------------------------------------------
 
 \tl_new:N \l__tblr_initial_tblr_outer_tl
@@ -2668,7 +3485,7 @@
 \cs_new_protected:Npn \__tblr_environ_code:nnnn #1 #2 #3 #4
   {
     \group_align_safe_begin:
-    \int_gincr:N \g__tblr_table_count_int
+    \int_gincr:N \c at tblrcount
     \tl_set:Nn \l__tblr_env_name_tl {#1}
     \mode_if_math:TF
       { \bool_set_true:N \l__tblr_math_mode_bool }
@@ -2679,11 +3496,14 @@
 
 \bool_new:N \lTblrMeasuringBool
 
+\tl_new:N \l__tblr_inner_spec_tl
+\cs_set_eq:NN \__tblr_hook_parse_inner_spec_before: \prg_do_nothing:
+
 %% Read, split and build the table
 \cs_new_protected:Npn \__tblr_builder:nnn #1 #2 #3
   {
-    \int_gincr:N \g_tblr_level_int
-    \__tblr_hook_use:n { tabularray/trial/before }
+    \int_gincr:N \gTblrLevelInt
+    \__tblr_hook_use:n { trial/before }
     \bool_set_true:N \lTblrMeasuringBool
     \__tblr_clear_prop_lists:
     \__tblr_clear_spec_lists:
@@ -2702,7 +3522,9 @@
     \__tblr_init_table_inner_spec:
     \LogTblrTracing { inner }
     \LogTblrTracing { step = parse ~ table ~ inner ~ spec}
-    \__tblr_parse_table_spec:n {#2}
+    \tl_set:Nn \l__tblr_inner_spec_tl {#2}
+    \__tblr_hook_parse_inner_spec_before:
+    \exp_args:NV \__tblr_parse_table_spec:n \l__tblr_inner_spec_tl
     \LogTblrTracing { step = execute ~ table ~ commands}
     \__tblr_execute_table_commands:
     \__tblr_disable_table_commands:
@@ -2711,46 +3533,16 @@
     \__tblr_enable_content_commands:
     \__tblr_calc_cell_and_line_sizes:
     \bool_set_false:N \lTblrMeasuringBool
-    \__tblr_hook_use:n { tabularray/trial/after }
+    \__tblr_hook_use:n { trial/after }
     \LogTblrTracing { step = build ~ the ~ whole ~ table}
     \__tblr_build_whole:
-    \int_gdecr:N \g_tblr_level_int
+    \int_gdecr:N \gTblrLevelInt
   }
 
 %%% --------------------------------------------------------
-%%> \section{Split Table Contents}
+%%> \section{Split table contents}
 %%% --------------------------------------------------------
 
-%% Insert and remove braces for nesting environments inside cells
-%% These make line split and cell split workable
-%% We need to replace N times for N level nestings
-\regex_const:Nn \c__tblr_insert_braces_regex
-  {
-    \c{begin} \cB\{ (\c[^BE].*) \cE\} (.*?) \c{end} \cB\{ (\c[^BE].*) \cE\}
-  }
-\tl_const:Nn \c__tblr_insert_braces_tl
-  {
-    \c{begin} \cB\{ \cB\{ \1 \cE\} \2 \c{end} \cE\} \cB\{ \3 \cE\}
-  }
-\regex_const:Nn \c__tblr_remove_braces_regex
-  {
-    \c{begin} \cB\{ \cB\{ (.*?) \c{end} \cE\}
-  }
-\tl_const:Nn \c__tblr_remove_braces_tl
-  {
-    \c{begin} \cB\{ \1 \c{end}
-  }
-\cs_new_protected:Npn \__tblr_insert_braces:N #1
-  {
-    \regex_replace_all:NVN \c__tblr_insert_braces_regex \c__tblr_insert_braces_tl #1
-    \regex_replace_all:NVN \c__tblr_insert_braces_regex \c__tblr_insert_braces_tl #1
-  }
-\cs_new_protected:Npn \__tblr_remove_braces:N #1
-  {
-    \regex_replace_all:NVN \c__tblr_remove_braces_regex \c__tblr_remove_braces_tl #1
-    \regex_replace_all:NVN \c__tblr_remove_braces_regex \c__tblr_remove_braces_tl #1
-  }
-
 \tl_new:N \l__tblr_body_tl
 \seq_new:N \l__tblr_lines_seq
 
@@ -2758,12 +3550,14 @@
 %% #1: table content
 \cs_new_protected:Npn \__tblr_split_table:n #1
   {
+    \__tblr_child_split_table_before:
     \tl_set:Nn \l__tblr_body_tl {#1}
-    \tblr_modify_table_body:
+    \__tblr_modify_table_body:
     \int_zero:N \c at rowcount
     \int_zero:N \c at colcount
     \__tblr_split_table_to_lines:NN \l__tblr_body_tl \l__tblr_lines_seq
     \__tblr_split_lines_to_cells:N \l__tblr_lines_seq
+    \__tblr_child_split_table_after:
   }
 
 \tl_new:N \l__tblr_expand_tl
@@ -2770,10 +3564,10 @@
 
 \cs_set_eq:NN \__tblr_hook_split_before: \prg_do_nothing:
 
-\cs_new_protected:Npn \tblr_modify_table_body:
+\cs_new_protected:Npn \__tblr_modify_table_body:
   {
     \__tblr_hook_split_before:
-    \tl_set:Nx \l__tblr_expand_tl { \__tblr_spec_item:nn { outer } { expand } }
+    \tl_set:Ne \l__tblr_expand_tl { \__tblr_spec_item:nn { outer } { expand } }
     \tl_map_inline:Nn \l__tblr_expand_tl
       {
         \__tblr_expand_table_body:NN \l__tblr_body_tl ##1
@@ -2796,12 +3590,76 @@
     \exp_last_unbraced:NV \__tblr_expand_table_body_aux:w \l_tmpa_tl #2 \q_stop
   }
 
+%% Split tl #3 into items separated by tl #2, and store the result in seq #1.
+%% Spaces on both side of each item and outer braces around each item are kept.
+%% We insert \prg_do_nothing: before each item to avoid losing outermost braces.
+\cs_new_protected:Npn \__tblr_seq_set_split:Nnn #1 #2 #3
+  {
+    \seq_clear:N #1
+    \cs_set_protected:Npn \__tblr_seq_set_split_aux:Nw ##1 ##2 #2
+      {
+        \tl_if_eq:nnF { \prg_do_nothing: \c_novalue_tl } { ##2 }
+          {
+            \seq_put_right:No ##1 { ##2 }
+            \__tblr_seq_set_split_aux:Nw ##1 \prg_do_nothing:
+          }
+      }
+    \__tblr_seq_set_split_aux:Nw #1 \prg_do_nothing: #3 #2 \c_novalue_tl #2
+  }
+\cs_generate_variant:Nn \__tblr_seq_set_split:Nnn { NnV }
+
+%% Split tl #3 into items separated by tl #2, and store the result in seq #1.
+%% Spaces on both side of items and outer braces around items are kept.
+%% And we prevent splitting inside the body of an environment.
+\cs_new_protected:Npn \__tblr_seq_set_split_keep_braces_envs:Nnn #1 #2 #3
+  {
+    \__tblr_seq_set_split_keep_envs_aux:NnnN
+      #1 { #2 }  { #3 } \__tblr_seq_set_split:Nnn
+  }
+\cs_generate_variant:Nn \__tblr_seq_set_split_keep_braces_envs:Nnn { NnV }
+
+%% Split tl #3 into items separated by tl #2, and store the result in seq #1.
+%% Spaces on both side of items and outer braces around items are removed.
+%% And we prevent splitting inside the body of an environment.
+\cs_new_protected:Npn \__tblr_seq_set_split_keep_envs:Nnn #1 #2 #3
+  {
+    \__tblr_seq_set_split_keep_envs_aux:NnnN
+      #1 { #2 }  { #3 } \seq_set_split:Nnn
+  }
+\cs_generate_variant:Nn \__tblr_seq_set_split_keep_envs:Nnn { NnV }
+
+\seq_new:N \l__tblr_split_raw_seq
+\seq_new:N \l__tblr_split_temp_seq
+\seq_new:N \l__tblr_split_item_seq
+\int_new:N \l__tblr_split_balance_int
+
+\cs_new_protected:Npn \__tblr_seq_set_split_keep_envs_aux:NnnN #1 #2 #3 #4
+  {
+    #4 \l__tblr_split_raw_seq { #2 }  { #3 }
+    \seq_clear:N #1
+    \seq_clear:N \l__tblr_split_item_seq
+    \seq_map_inline:Nn \l__tblr_split_raw_seq
+      {
+        \seq_put_right:Nn \l__tblr_split_item_seq { ##1 }
+        \seq_set_split:Nnn \l__tblr_split_temp_seq { \begin } { ##1 }
+        \int_add:Nn \l__tblr_split_balance_int
+          { \seq_count:N \l__tblr_split_temp_seq }
+        \seq_set_split:Nnn \l__tblr_split_temp_seq { \end } { ##1 }
+        \int_sub:Nn \l__tblr_split_balance_int
+          { \seq_count:N \l__tblr_split_temp_seq }
+        \int_compare:nNnT { \l__tblr_split_balance_int } = { 0 }
+          {
+            \seq_put_right:Ne #1 { \seq_use:Nn \l__tblr_split_item_seq { #2 } }
+            \seq_clear:N \l__tblr_split_item_seq
+          }
+      }
+  }
+
 %% Split table content to a sequence of lines
 %% #1: tl with table contents, #2: resulting sequence of lines
 \cs_new_protected:Npn \__tblr_split_table_to_lines:NN #1 #2
   {
-    \__tblr_insert_braces:N #1
-    \seq_set_split:NnV \l_tmpa_seq { \\ } #1
+    \__tblr_seq_set_split_keep_braces_envs:NnV \l_tmpa_seq { \\ } #1
     \seq_clear:N #2
     \seq_map_inline:Nn \l_tmpa_seq
       {
@@ -2808,7 +3666,7 @@
         \tl_if_head_eq_meaning:nNTF {##1} *
           {
             \tl_set:Nn \l__tblr_b_tl { \hborder { pagebreak = no } }
-            \tl_set:Nx \l__tblr_c_tl { \tl_tail:n {##1} }
+            \tl_set:Ne \l__tblr_c_tl { \tl_tail:n {##1} }
             \tl_trim_spaces:N \l__tblr_c_tl %% Ignore spaces between * and [dimen]
             \tl_if_head_eq_meaning:VNT \l__tblr_c_tl [
               {
@@ -2827,7 +3685,7 @@
   }
 
 %% Treat \\[dimen] command
-\NewTableCommand \RowBefore at AddBelowSep [1] []
+\NewTblrTableCommand \RowBefore at AddBelowSep [1] []
   {
     \IfValueT { #1 }
       {
@@ -2848,14 +3706,13 @@
 %% #1: row number, #2 the line text
 \cs_new_protected:Npn \__tblr_split_one_line:nn #1 #2
   {
-    \seq_set_split:Nnn \l_tmpa_seq { & } { #2 }
+    \__tblr_seq_set_split_keep_braces_envs:Nnn \l_tmpa_seq { & } { #2 }
     \int_set:Nn \c at rownum {#1}
     \int_zero:N \c at colnum
     \seq_map_inline:Nn \l_tmpa_seq
       {
         \tl_set:Nn \l_tmpa_tl { ##1 }
-        \__tblr_remove_braces:N \l_tmpa_tl
-        \__tblr_trim_par_space_tokens:N \l_tmpa_tl
+        \__tblr_trim_par_space_tokens_left:N \l_tmpa_tl
         \int_incr:N \c at colnum
         \__tblr_extract_table_commands:N \l_tmpa_tl
         \__tblr_trim_par_space_tokens:N \l_tmpa_tl
@@ -2871,7 +3728,7 @@
       }
       { \int_decr:N \c at rowcount }
       {
-        \__tblr_prop_gput:nnx
+        \__tblr_prop_gput:nne
           {row} { [#1] / cell-number } { \int_use:N \c at colnum }
         \int_compare:nT { \c at colnum > \c at colcount }
           {
@@ -2880,20 +3737,37 @@
       }
   }
 
-\regex_const:Nn \c__tblr_trim_left_par_space_regex { ^ \c{par} ? \s * }
-\regex_const:Nn \c__tblr_trim_right_space_par_regex { \s * \c{par} ? $ }
+\cs_new_protected:Npn \__tblr_trim_par_space_tokens_left:N #1
+  {
+    \tl_trim_spaces:N #1
+    \__tblr_remove_head_par:N #1
+    \tl_trim_spaces:N #1
+  }
 
+\tl_new:N \l__tblr_trim_temp_tl
+
 \cs_new_protected:Npn \__tblr_trim_par_space_tokens:N #1
   {
-    \regex_replace_once:NnN \c__tblr_trim_left_par_space_regex {} #1
-    \regex_replace_once:NnN \c__tblr_trim_right_space_par_regex {} #1
+    \tl_trim_spaces:N #1
+    \__tblr_remove_head_par:N #1
+    \tl_set_eq:NN \l__tblr_trim_temp_tl #1
+    \tl_reverse:N \l__tblr_trim_temp_tl
+    \__tblr_remove_head_par:N \l__tblr_trim_temp_tl
+    \tl_reverse:N \l__tblr_trim_temp_tl
+    \tl_set_eq:NN #1 \l__tblr_trim_temp_tl
+    \tl_trim_spaces:N #1
   }
 
+\cs_new_protected:Npn \__tblr_remove_head_par:N #1
+  {
+    \tl_if_head_eq_meaning:VNT #1 \par { \tl_set:Ne #1 { \tl_tail:N #1 } }
+  }
+
 %%% --------------------------------------------------------
-%%> \section{Extract Table Commands from Cell Text}
+%%> \section{Extract table commands from cell text}
 %%% --------------------------------------------------------
 
-%% Extract table commands defined with \NewTableCommand from cell text
+%% Extract table commands defined with \NewTblrTableCommand from cell text
 
 \tl_new:N \l__tblr_saved_table_commands_before_cell_text_tl
 \tl_new:N \l__tblr_saved_cell_text_after_table_commands_tl
@@ -2900,12 +3774,15 @@
 
 \cs_new_protected:Npn \__tblr_extract_table_commands:N #1
   {
+    % We need to execute \SetChild commands before parsing inner specs,
+    % but execute other table commands after parsing inner specs.
+    \__tblr_child_extract_index_command:N #1
     \tl_clear:N \l__tblr_saved_table_commands_before_cell_text_tl
     \tl_clear:N \l__tblr_saved_cell_text_after_table_commands_tl
-    \exp_last_unbraced:NV \__tblr_extract_table_commands_next:n #1 \q_stop
+    \exp_last_unbraced:NV \__tblr_extract_table_commands_next: #1 \q_stop
     \tl_if_empty:NF \l__tblr_saved_table_commands_before_cell_text_tl
       {
-        \__tblr_prop_gput:nxV { command }
+        \__tblr_prop_gput:neV { command }
           {[\int_use:N \c at rownum][\int_use:N \c at colnum]}
           \l__tblr_saved_table_commands_before_cell_text_tl
       }
@@ -2912,8 +3789,24 @@
     \tl_set_eq:NN #1 \l__tblr_saved_cell_text_after_table_commands_tl
   }
 
+\cs_new_protected:Npn \__tblr_extract_table_commands_next:
+  {
+    \peek_after:Nw \__tblr_extract_table_commands_next_peek:
+  }
+
+\cs_new_protected:Npn \__tblr_extract_table_commands_next_peek:
+  {
+    \token_if_group_begin:NTF \l_peek_token
+      {
+        \__tblr_save_real_cell_text:
+      }
+      {
+        \__tblr_extract_table_commands_next_real:n
+      }
+  }
+
 %% #1 maybe a single token or multiple tokens from a pair of braces
-\cs_new_protected:Npn \__tblr_extract_table_commands_next:n #1
+\cs_new_protected:Npn \__tblr_extract_table_commands_next_real:n #1
   {
     \tl_if_single_token:nTF {#1}
       {
@@ -2921,16 +3814,16 @@
           { \__tblr_extract_one_table_command:N #1 }
           {
             \token_if_eq_meaning:NNF #1 \q_stop
-              { \__tblr_save_real_cell_text:w #1 }
+              { \__tblr_save_real_cell_text: #1 }
           }
       }
-      { \__tblr_save_real_cell_text:w {#1} }
+      { \__tblr_save_real_cell_text: {#1} }
   }
 
 \cs_new_protected:Npn \__tblr_extract_one_table_command:N #1
   {
     \int_set:Nn \l__tblr_a_int
-      { \cs:w g__tblr_table_cmd_ \cs_to_str:N #1 _arg_numb_tl \cs_end: }
+      { \tl_use:c { g__tblr_table_cmd_ \cs_to_str:N #1 _arg_numb_tl } }
     \tl_put_right:Nn \l__tblr_saved_table_commands_before_cell_text_tl {#1}
     \int_compare:nNnTF {\l__tblr_a_int} < {0}
       {
@@ -2961,17 +3854,22 @@
         \int_decr:N \l__tblr_a_int
         \__tblr_extract_table_command_arg_m:n
       }
-      { \__tblr_extract_table_commands_next:n }
+      { \__tblr_extract_table_commands_next: }
   }
 
-%% The outermost set of braces of cell text #1 will be removed
-\cs_new_protected:Npn \__tblr_save_real_cell_text:w #1 \q_stop
+%% We prepend \prg_do_nothing: to avoid losing outermost braces.
+\cs_new_protected:Npn \__tblr_save_real_cell_text:
   {
-    \tl_set:Nn \l__tblr_saved_cell_text_after_table_commands_tl {#1}
+    \__tblr_save_real_cell_text_aux:w \prg_do_nothing:
   }
 
+\cs_new_protected:Npn \__tblr_save_real_cell_text_aux:w #1 \q_stop
+  {
+    \tl_set:No \l__tblr_saved_cell_text_after_table_commands_tl {#1}
+  }
+
 %%% --------------------------------------------------------
-%%> \section{Initialize Table Inner Specifications}
+%%> \section{Initialize table inner specifications}
 %%% --------------------------------------------------------
 
 \prop_new:N \g__tblr_initial_table_prop
@@ -3028,13 +3926,13 @@
   }
 
 \tl_new:N \l__tblr_inner_spec_measure_tl
-\tl_new:N \l__tblr_inner_spec_verb_tl
+\tl_set:Nn \l__tblr_inner_spec_measure_tl { hbox }
 
 \cs_new_protected:Npn \__tblr_init_table_inner_spec:
   {
     \prop_map_inline:Nn \g__tblr_initial_table_prop
       {
-        \__tblr_prop_gput:nxn { inner } { ##1 } {##2}
+        \__tblr_prop_gput:nen { inner } { ##1 } {##2}
       }
     \int_step_variable:nNn { \c at rowcount } \l__tblr_i_tl
       {
@@ -3076,34 +3974,37 @@
         \__tblr_spec_gput:nen { vline }
           { [\int_eval:n { \c at colcount + 1}] / ##1 } {##2}
       }
-    \tl_clear:N \l__tblr_inner_spec_measure_tl
-    \tl_clear:N \l__tblr_inner_spec_verb_tl
-    \keys_set:nv { tblr } { l__tblr_default_ \l__tblr_env_name_tl _inner_tl }
+    \__tblr_keys_set:nv { table/inner }
+      { l__tblr_default_ \l__tblr_env_name_tl _inner_tl }
   }
 
 %%% --------------------------------------------------------
-%%> \section{Parse Table Inner Specifications}
+%%> \section{Parse table inner specifications}
 %%% --------------------------------------------------------
 
-\clist_new:N \g__tblr_table_known_keys_clist
-\clist_gset:Nn \g__tblr_table_known_keys_clist
+%% We must define these keys to make \__tblr_keys_if_exist work
+\__tblr_keys_define:nn { table/inner }
   {
-    colspec, rowspec, column, row, cell, hline, vline, hborder, vborder, width,
-    rowhead, rowfoot, columns, rows, cells, hlines, vlines, % hborders, vborders,
-    leftsep, rightsep, colsep, abovesep, belowsep, rowsep, rulesep,
-    baseline, hspan, vspan, stretch, verb, delimiter
+    column  .code:n = ,
+    row     .code:n = ,
+    cell    .code:n = ,
+    hline   .code:n = ,
+    vline   .code:n = ,
+    hborder .code:n = ,
+    vborder .code:n =
   }
 
-\keys_define:nn { tblr }
+\str_new:N \g__tblr_name_str
+
+\__tblr_keys_define:nn { table/inner }
   {
-    colspec .code:n = \__tblr_parse_colrow_spec:nn { column } {#1},
-    rowspec .code:n = \__tblr_parse_colrow_spec:nn { row } {#1},
-    width .code:n = \__tblr_keys_gput:nx { width } { \dim_eval:n {#1} },
+    name .code:n = \__tblr_keys_gput:nn { name } {#1},
+    colspec .code:n = \__tblr_parse_colrow_spec:nn { Column } {#1},
+    rowspec .code:n = \__tblr_parse_colrow_spec:nn { Row } {#1},
+    width .code:n = \__tblr_keys_gput:ne { width } { \dim_eval:n {#1} },
     hspan .code:n = \__tblr_keys_gput:nn { hspan } {#1},
     vspan .code:n = \__tblr_keys_gput:nn { vspan } {#1},
     stretch .code:n = \__tblr_keys_gput:nn { stretch } {#1},
-    verb   .tl_set:N = \l__tblr_inner_spec_verb_tl,
-    verb  .default:n = lite,
     columns .code:n = \__tblr_set_every_column_aux:n {#1},
     rows    .code:n = \__tblr_set_every_row_aux:n {#1},
     cells   .code:n = \__tblr_set_every_cell_aux:n {#1},
@@ -3123,34 +4024,36 @@
     unknown .code:n = \__tblr_table_special_key:Vn \l_keys_key_str {#1},
   }
 
-\regex_const:Nn \c__tblr_split_key_name_regex { ^ ( [a-z] + ) ( . * ) }
+\msg_new:nnn { tabularray } { unknown-inner-key }
+  { Unknown ~ inner ~ key ~ name ~ '#1'. }
 
 \cs_new_protected:Npn \__tblr_table_special_key:nn #1 #2
   {
-    \regex_extract_once:NnNT \c__tblr_split_key_name_regex {#1} \l_tmpa_seq
+    \__tblr_key_split_name_args:n {#1}
+    \__tblr_keys_if_exist:nVTF { table/inner } \l__tblr_key_split_name_str
       {
-        \tl_set:Nx \l__tblr_a_tl { \seq_item:Nn \l_tmpa_seq {2} }
-        \tl_set_rescan:Nnx \l__tblr_b_tl {} { \seq_item:Nn \l_tmpa_seq {3} }
-        \cs:w __tblr_set_ \l__tblr_a_tl _aux:Vn \cs_end: \l__tblr_b_tl {#2}
+        \use:c { __tblr_set_ \l__tblr_key_split_name_str _aux:Vn }
+          \l__tblr_key_split_args_tl {#2}
       }
+      {
+        \msg_error:nnV { tabularray } { unknown-inner-key }
+          \l__tblr_key_split_name_str
+      }
   }
 \cs_generate_variant:Nn \__tblr_table_special_key:nn { Vn }
 
-%% If the first key name is known, treat #1 is the table spec;
+%% If the first key name is known, treat #1 as table spec;
 %% otherwise, treat #1 as colspec.
 
-\regex_const:Nn \c__tblr_first_key_name_regex { ^ \s * ( [A-Za-z\-] + ) }
-
 \cs_new_protected:Npn \__tblr_parse_table_spec:n #1
   {
-    \regex_extract_once:NnNTF \c__tblr_first_key_name_regex {#1} \l_tmpa_seq
-      {
-        \clist_if_in:NxTF \g__tblr_table_known_keys_clist
-          { \seq_item:Nn \l_tmpa_seq {2} }
-          { \keys_set:nn { tblr } {#1} }
-          { \__tblr_parse_colrow_spec:nn { column } {#1} }
-      }
-      { \__tblr_parse_colrow_spec:nn { column } {#1} }
+    \__tblr_keyval_extract_first_name:n {#1}
+    \__tblr_keys_if_exist:nVTF { table/inner } \l__tblr_key_split_name_str
+      { \__tblr_keys_set:nn { table/inner } {#1} }
+      { \__tblr_parse_colrow_spec:nn { Column } {#1} }
+    \str_gset:Ne \g__tblr_name_str { \__tblr_prop_item:ne { inner } { name } }
+    \str_if_empty:NT \g__tblr_name_str
+      { \str_gset:Ne \g__tblr_name_str { @ \int_use:N \c at tblrcount } }
   }
 
 \cs_new_protected:Npn \__tblr_keys_gput:nn #1 #2
@@ -3157,9 +4060,9 @@
   {
     \__tblr_prop_gput:nnn { inner } {#1} {#2}
   }
-\cs_generate_variant:Nn \__tblr_keys_gput:nn { nx }
+\cs_generate_variant:Nn \__tblr_keys_gput:nn { ne }
 
-\keys_define:nn { tblr-delimiter }
+\__tblr_keys_define:nn { table/delimiter }
   {
     left .code:n  = \__tblr_keys_gput:nn { delim-left } { \left #1 },
     right .code:n = \__tblr_keys_gput:nn { delim-right } { \right #1 }
@@ -3167,11 +4070,11 @@
 
 \cs_new_protected:Npn \__tblr_set_delimiter:n #1
   {
-    \keys_set:nn { tblr-delimiter } {#1}
+    \__tblr_keys_set:nn { table/delimiter } {#1}
   }
 
 %%% --------------------------------------------------------
-%%> \section{Initialize and Parse Table Outer Specifications}
+%%> \section{Initialize and parse table outer specifications}
 %%% --------------------------------------------------------
 
 \msg_new:nnn { tabularray } { used-theme-name }
@@ -3196,18 +4099,27 @@
 
 \cs_new_protected:Npn \__tblr_init_table_outer_spec:
   {
-    \keys_set:nv { tblr-outer } { l__tblr_default_ \l__tblr_env_name_tl _outer_tl }
+    \__tblr_keys_set:nv { table/outer }
+      { l__tblr_default_ \l__tblr_env_name_tl _outer_tl }
   }
 
 \cs_new_protected:Npn \__tblr_parse_table_option:n #1
   {
-    \keys_set:nn { tblr-outer } {#1}
+    \__tblr_keys_set:nn { table/outer } {#1}
   }
 
-\keys_define:nn { tblr-outer }
+%% We must define these keys to make \__tblr_keys_if_exist work
+\__tblr_keys_define:nn { table/outer }
   {
-    long    .code:n = \__tblr_outer_gput_spec:nn { long } { true },
-    tall    .code:n = \__tblr_outer_gput_spec:nn { tall } { true },
+    note   .code:n = ,
+    remark .code:n = ,
+    more   .code:n =
+  }
+
+\__tblr_keys_define:nn { table/outer }
+  {
+    long    .code:n = \__tblr_outer_gput_spec:nn { portrait } { long },
+    tall    .code:n = \__tblr_outer_gput_spec:nn { portrait } { tall },
     halign  .code:n = \__tblr_outer_gput_spec:nn { halign } {#1},
     baseline .code:n = \__tblr_outer_gput_spec:nn { baseline } {#1},
     l       .meta:n = { halign = l },
@@ -3241,27 +4153,28 @@
 
 \cs_new_protected:Npn \__tblr_outer_gconcat_spec:nn #1 #2
   {
-    \__tblr_outer_gput_spec:ne {#1} { \__tblr_spec_item:nn { outer } { #1 } \exp_not:n { #2 } }
+    \__tblr_outer_gput_spec:ne
+      {#1} { \__tblr_spec_item:nn { outer } { #1 } \exp_not:n { #2 } }
   }
 
-\regex_const:Nn \c__tblr_option_key_name_regex { ^ [A-Za-z\-] + $ }
-
 \msg_new:nnn { tabularray } { unknown-outer-key }
-  { Unknown ~ outer ~ key ~ name ~ #1! }
+  { Unknown ~ outer ~ key ~ name ~ '#1'. }
 
 \cs_new_protected:Npn \__tblr_table_option_key:nn #1 #2
   {
-    \regex_match:NnTF \c__tblr_option_key_name_regex {#1}
-      { \msg_error:nnn { tabularray } { unknown-outer-key } {#1} }
+    \__tblr_key_split_name_args:n {#1}
+    \__tblr_keys_if_exist:nVTF { table/outer } \l__tblr_key_split_name_str
       {
-        \regex_extract_once:NnNT \c__tblr_split_key_name_regex {#1} \l_tmpa_seq
-          {
-            \tl_set:Nx \l__tblr_a_tl { \seq_item:Nn \l_tmpa_seq {2} }
-            \tl_set_rescan:Nnx \l__tblr_b_tl {} { \seq_item:Nn \l_tmpa_seq {3} }
-            \tl_set:Nx \l__tblr_c_tl { \tl_head:N \l__tblr_b_tl }
-            \use:c { __tblr_outer_gput_ \l__tblr_a_tl :Vn } \l__tblr_c_tl {#2}
-          }
+        % remove a pair of outer braces
+        \tl_set:Ne \l__tblr_key_split_args_tl
+          { \tl_head:N \l__tblr_key_split_args_tl }
+        \use:c { __tblr_outer_gput_ \l__tblr_key_split_name_str :Vn }
+          \l__tblr_key_split_args_tl {#2}
       }
+      {
+        \msg_error:nnV { tabularray } { unknown-outer-key }
+          \l__tblr_key_split_name_str
+      }
   }
 \cs_generate_variant:Nn \__tblr_table_option_key:nn { Vn }
 
@@ -3284,7 +4197,7 @@
 \cs_generate_variant:Nn \__tblr_outer_gput_more:nn { Vn }
 
 %%% --------------------------------------------------------
-%%> \section{Typeset and Calculate Sizes}
+%%> \section{Typeset and calculate sizes}
 %%% --------------------------------------------------------
 
 %% Calculate the width and height for every cell and border
@@ -3319,7 +4232,10 @@
       {
         \cs_set_eq:NN \__tblr_process_stretch: \prg_do_nothing:
         \fp_compare:nNnTF \l__tblr_stretch_fp < \c_zero_fp
-          { \cs_set_eq:NN \__tblr_leave_vmode: \@setminipage } % for lists (see issue #99)
+          {
+            % for lists (see issue #99)
+            \cs_set_eq:NN \__tblr_leave_vmode: \@setminipage
+          }
           { \cs_set_eq:NN \__tblr_leave_vmode: \mode_leave_vertical: }
       }
   }
@@ -3384,11 +4300,11 @@
 \cs_new_protected:Npn \__tblr_measure_and_update_vline_size:nn #1 #2
   {
     \dim_zero:N \l__tblr_w_dim
-    \tl_set:Nx \l__tblr_n_tl
+    \tl_set:Ne \l__tblr_n_tl
       { \__tblr_spec_item:ne { vline } { [#2] / @vline-count } }
     \int_compare:nNnT { \l__tblr_n_tl } > {0}
       {
-        \tl_set:Nx \l__tblr_s_tl
+        \tl_set:Ne \l__tblr_s_tl
           { \__tblr_prop_item:ne { inner } { rulesep } }
         \int_step_inline:nn { \l__tblr_n_tl }
           {
@@ -3397,7 +4313,7 @@
                 \__tblr_get_vline_segment_child:nnnnn
                   {#1} {#2} {##1} {1pt} {1pt}
               }
-            \tl_set:Nx \l__tblr_w_tl { \dim_eval:n { \box_wd:N \l__tblr_b_box } }
+            \tl_set:Ne \l__tblr_w_tl { \dim_eval:n { \box_wd:N \l__tblr_b_box } }
             \__tblr_spec_gput_if_larger:nee { vline }
               { [#2](##1) / @vline-width } { \l__tblr_w_tl }
             \dim_add:Nn \l__tblr_w_dim
@@ -3418,14 +4334,14 @@
 \cs_new_protected:Npn \__tblr_get_vline_segment_child:nnnnn #1 #2 #3 #4 #5
   {
     \group_begin:
-    \tl_set:Nx \l__tblr_w_tl
+    \tl_set:Ne \l__tblr_w_tl
       { \__tblr_spec_item:ne { vline } { [#1][#2](#3) / wd } }
-    \tl_if_empty:NF \l__tblr_w_tl { \dim_set:Nn \rulewidth { \l__tblr_w_tl } }
-    \tl_set:Nx \l__tblr_d_tl
+    \tl_if_empty:NF \l__tblr_w_tl
+      { \dim_set:Nn \lTblrDefaultVruleWidthDim { \l__tblr_w_tl } }
+    \tl_set:Ne \l__tblr_d_tl
       { \__tblr_spec_item:ne { vline } { [#1][#2](#3) / @dash } }
-    \tl_set:Nx \l__tblr_a_tl { \tl_head:N \l__tblr_d_tl }
-    \tl_set:Nx \l__tblr_b_tl { \tl_tail:N \l__tblr_d_tl }
-    \exp_args:NV \tl_if_eq:NNTF \l__tblr_a_tl \@tblr at dash
+    \tl_set:Ne \l__tblr_b_tl { \tl_tail:N \l__tblr_d_tl }
+    \tl_if_head_eq_meaning:VNTF \l__tblr_d_tl \q__tblr_dash
       {
         \__tblr_get_vline_dash_style:N \l__tblr_b_tl
         \xleaders \l__tblr_b_tl \vfil
@@ -3445,7 +4361,7 @@
       }
     \group_end:
   }
-\cs_generate_variant:Nn \__tblr_get_vline_segment_child:nnnnn { nnnxx }
+\cs_generate_variant:Nn \__tblr_get_vline_segment_child:nnnnn { nnnee }
 
 %% Measure and update thickness of the hline
 %% #1: row number, #2 column number
@@ -3452,17 +4368,17 @@
 \cs_new_protected:Npn \__tblr_measure_and_update_hline_size:nn #1 #2
   {
     \dim_zero:N \l__tblr_h_dim
-    \tl_set:Nx \l__tblr_n_tl
+    \tl_set:Ne \l__tblr_n_tl
       { \__tblr_spec_item:ne { hline } { [#1] / @hline-count } }
     \int_compare:nNnT { \l__tblr_n_tl } > {0}
       {
-        \tl_set:Nx \l__tblr_s_tl
+        \tl_set:Ne \l__tblr_s_tl
           { \__tblr_prop_item:ne { inner } { rulesep } }
         \int_step_inline:nn { \l__tblr_n_tl }
           {
             \hbox_set_to_wd:Nnn \l__tblr_b_box {1pt}
               { \__tblr_get_hline_segment_child:nnn {#1} {#2} {##1} }
-            \tl_set:Nx \l__tblr_h_tl
+            \tl_set:Ne \l__tblr_h_tl
               {
                 \dim_eval:n
                   { \box_ht:N \l__tblr_b_box + \box_dp:N \l__tblr_b_box }
@@ -3486,14 +4402,15 @@
 \cs_new_protected:Npn \__tblr_get_hline_segment_child:nnn #1 #2 #3
   {
     \group_begin:
-    \tl_set:Nx \l__tblr_w_tl
+    \tl_set:Ne \l__tblr_w_tl
       { \__tblr_spec_item:ne { hline } { [#1][#2](#3) / wd } }
-    \tl_if_empty:NF \l__tblr_w_tl { \dim_set:Nn \rulewidth { \l__tblr_w_tl } }
-    \tl_set:Nx \l__tblr_d_tl
+    \tl_if_empty:NF \l__tblr_w_tl
+      { \dim_set:Nn \lTblrDefaultHruleWidthDim { \l__tblr_w_tl } }
+    \tl_set:Ne \l__tblr_d_tl
       { \__tblr_spec_item:ne { hline } { [#1][#2](#3) / @dash } }
-    \tl_set:Nx \l__tblr_a_tl { \tl_head:N \l__tblr_d_tl }
-    \tl_set:Nx \l__tblr_b_tl { \tl_tail:N \l__tblr_d_tl }
-    \exp_args:NV \tl_if_eq:NNTF \l__tblr_a_tl \@tblr at dash
+    \tl_set:Ne \l__tblr_a_tl { \tl_head:N \l__tblr_d_tl }
+    \tl_set:Ne \l__tblr_b_tl { \tl_tail:N \l__tblr_d_tl }
+    \tl_if_head_eq_meaning:VNTF \l__tblr_d_tl \q__tblr_dash
       {
         \__tblr_get_hline_dash_style:N \l__tblr_b_tl
         \xleaders \l__tblr_b_tl \hfil
@@ -3525,9 +4442,9 @@
 \cs_new_protected:Npn \__tblr_get_cell_alignments:nn #1 #2
   {
     \group_begin:
-    \tl_gset:Nx \g__tblr_cell_halign_tl
+    \tl_gset:Ne \g__tblr_cell_halign_tl
       { \__tblr_data_item:neen { cell } {#1} {#2} { halign } }
-    \tl_set:Nx \l__tblr_v_tl
+    \tl_set:Ne \l__tblr_v_tl
       { \__tblr_data_item:neen { cell } {#1} {#2} { valign } }
     \tl_case:NnF \l__tblr_v_tl
       {
@@ -3572,8 +4489,14 @@
         \int_incr:N \c at rownum
         \int_zero:N \c at colnum
         \__tblr_update_rowsep_registers:
-        \tl_set:Nx \l__tblr_h_tl
-          { \__tblr_data_item:nen { row } { \int_use:N \c at rownum } { height } }
+        %% Note that \__tblr_data_item always returns nonempty value,
+        %% but \__tblr_prop_item may return empty value.
+        %% To make \__tblr_map_data_to_prop: work, we need to add + 0pt.
+        \tl_set:Ne \l__tblr_h_tl
+          {
+            \__tblr_data_item:nen { row } { \int_use:N \c at rownum } { height }
+            + 0pt
+          }
         %% We didn't initialize row heights with -1pt
         \dim_compare:nNnF { \l__tblr_h_tl } = { 0pt }
           {
@@ -3596,7 +4519,7 @@
     \__tblr_restore_counters:n { table }
     \int_step_inline:nn { \c at colcount }
       {
-        \tl_set:Nx \l__tblr_w_tl
+        \tl_set:Ne \l__tblr_w_tl
           { \__tblr_data_item:nen { column } {##1} { width } }
         \dim_compare:nNnF { \l__tblr_w_tl } < { 0pt }
           {
@@ -3650,9 +4573,10 @@
 \tl_new:N \l__tblr_cell_cmd_tl
 \tl_new:N \l__tblr_cell_mode_tl
 \bool_new:N \l__tblr_cell_math_mode_bool
-\tl_const:Nn \l__tblr_cell_math_style_tl  { \relax }
-\tl_const:Nn \l__tblr_cell_imath_style_tl { \textstyle }
-\tl_const:Nn \l__tblr_cell_dmath_style_tl { \displaystyle }
+\tl_const:Nn \c__tblr_cell_math_style_tl { \relax }
+\tl_const:Nn \c__tblr_cell_imath_style_tl { \textstyle }
+\tl_const:Nn \c__tblr_cell_dmath_style_tl { \displaystyle }
+\box_new:N \l__tblr_measured_cell_box
 
 %% Get cell text, #1: row number, #2: column number
 %% If the width of the cell is not set, split it with \\ and compute the width
@@ -3660,7 +4584,7 @@
 \cs_new_protected:Npn \__tblr_get_cell_text_real:nn #1 #2
   {
     \group_begin:
-    \tl_set:Nx \l__tblr_c_tl { \__tblr_spec_item:ne { text } {[#1][#2]} }
+    \tl_set:Ne \l__tblr_c_tl { \__tblr_spec_item:ne { text } {[#1][#2]} }
     %% when the cell text is guarded by a pair of curly braces,
     %% we unbrace it and ignore cmd option of the cell, see issue #90.
     \bool_lazy_and:nnTF
@@ -3668,15 +4592,15 @@
       { \exp_args:NV \tl_if_head_is_group_p:n \l__tblr_c_tl }
       { \exp_last_unbraced:NNV \tl_set:Nn \l__tblr_c_tl \l__tblr_c_tl }
       {
-        \tl_set:Nx \l__tblr_cell_cmd_tl
+        \tl_set:Ne \l__tblr_cell_cmd_tl
           { \__tblr_data_item:neen { cell } {#1} {#2} { cmd } }
         \tl_if_empty:NF \l__tblr_cell_cmd_tl
           {
-            \tl_set:Nx \l__tblr_c_tl
+            \tl_set:Ne \l__tblr_c_tl
               { \exp_not:V \l__tblr_cell_cmd_tl { \exp_not:V \l__tblr_c_tl } }
           }
       }
-    \tl_set:Nx \l__tblr_cell_mode_tl
+    \tl_set:Ne \l__tblr_cell_mode_tl
       { \__tblr_data_item:neen { cell } {#1} {#2} { mode } }
     \tl_if_empty:NT \l__tblr_cell_mode_tl
       {
@@ -3689,12 +4613,12 @@
       {
         \bool_set_true:N \l__tblr_cell_math_mode_bool
         \tl_put_left:Nv \l__tblr_c_tl
-          { l__tblr_cell_ \l__tblr_cell_mode_tl _style_tl }
+          { c__tblr_cell_ \l__tblr_cell_mode_tl _style_tl }
         \tl_put_left:Nn \l__tblr_c_tl { $ }
         \tl_put_right:Nn \l__tblr_c_tl { $ }
       }
-    \tl_set:Nx \l__tblr_f_tl { \__tblr_data_item:neen { cell } {#1} {#2} { font } }
-    \tl_set:Nx \l__tblr_w_tl
+    \tl_set:Ne \l__tblr_f_tl { \__tblr_data_item:neen { cell } {#1} {#2} { font } }
+    \tl_set:Ne \l__tblr_w_tl
       { \__tblr_data_item:neen { cell } {#1} {#2} { width } }
     \dim_compare:nNnT { \l__tblr_w_tl } < { 0pt } % cell width unset
       {
@@ -3701,26 +4625,36 @@
         \int_compare:nNnT
           { \__tblr_data_item:neen { cell } {#1} {#2} { colspan } } < {2}
           {
-            \tl_set:Nx \l__tblr_w_tl
+            \tl_set:Ne \l__tblr_w_tl
               { \__tblr_data_item:nen { column } {#2} { width } }
           }
       }
+    \box_clear:N \l__tblr_measured_cell_box
     \dim_compare:nNnT { \l__tblr_w_tl } < { 0pt } % column width unset
       {
-        \__tblr_save_counters:n { cell }
-        \bool_if:NTF \l__tblr_cell_math_mode_bool
+        %% To keep \lTblrMeasuringBool correct we need measure=vstore from varwidth.
+        %% A temporary private hook for testfiles/supporthook/regression-test.cfg.
+        \use:c { __tblr_temp_hook_for_measure_vstore_testing: }
+        \tl_if_eq:NnTF \l__tblr_inner_spec_measure_tl { vstore }
           {
-            %% Note that font = \boldmath will increase cell width (issue #137)
-            \hbox_set:Nn \l_tmpa_box { \l__tblr_f_tl \l__tblr_c_tl }
-            \tl_set:Nx \l__tblr_w_tl { \box_wd:N \l_tmpa_box }
+            \__tblr_build_vcell_with_vstore:
           }
           {
-            \__tblr_get_cell_size_with_box:
+            \__tblr_save_counters:n { cell }
+            \bool_if:NTF \l__tblr_cell_math_mode_bool
+              {
+                %% Note that font = \boldmath will increase cell width (issue #137)
+                \hbox_set:Nn \l_tmpa_box { \l__tblr_f_tl \l__tblr_c_tl }
+                \tl_set:Ne \l__tblr_w_tl { \dim_eval:n { \box_wd:N \l_tmpa_box } }
+              }
+              {
+                \__tblr_get_cell_size_with_box:
+              }
+            \__tblr_restore_counters:n { cell }
           }
-        \__tblr_restore_counters:n { cell }
       }
     \tl_put_left:NV \l__tblr_c_tl \l__tblr_f_tl
-    \tl_set:Nx \l__tblr_cell_fg_tl
+    \tl_set:Ne \l__tblr_cell_fg_tl
       { \__tblr_data_item:neen { cell } {#1} {#2} { foreground } }
     \tl_if_empty:NF \l__tblr_cell_fg_tl
       { \exp_args:NV \color \l__tblr_cell_fg_tl }
@@ -3728,6 +4662,30 @@
     \group_end:
   }
 
+\box_new:N \g__tblr_last_box
+\box_new:N \l__tblr_temp_box
+
+%% Build cell vbox with varwidth and use it later when hook library is loaded
+%% We apply \lastbox trick to get the cell vbox with correct width
+\cs_new_protected:Npn  \__tblr_build_vcell_with_vstore:
+  {
+    \vbox_set:Nn \l__tblr_temp_box % we don't use it since its width is too large
+      {
+        \begin{varwidth}[t]{\paperwidth}
+          \TblrParboxRestore
+          \cs:w __tblr_halign_command_ \g__tblr_cell_halign_tl : \cs_end:
+          \__tblr_leave_vmode:
+          \l__tblr_f_tl
+          \l__tblr_c_tl
+        \end{varwidth}
+        \box_gset_to_last:N \g__tblr_last_box
+      }
+    \box_set_eq:NN \l__tblr_measured_cell_box \g__tblr_last_box
+    \tl_set:Ne \l__tblr_w_tl
+      { \dim_eval:n { \box_wd:N \l__tblr_measured_cell_box } }
+  }
+
+%% Measure cell width only and build it later when hook library is not loaded
 \cs_new_protected:Npn \__tblr_get_cell_size_with_box:
   {
     \tl_if_eq:NnTF \l__tblr_inner_spec_measure_tl { vbox }
@@ -3755,28 +4713,26 @@
         \begin{tblrNoHyper}
         \begin{varwidth}{\paperwidth}
            \l__tblr_f_tl
-           \__tblr_rescan_cell_tokens:N \l__tblr_c_tl
+           \l__tblr_c_tl
         \end{varwidth}
         \end{tblrNoHyper}
       }
-    \tl_set:Nx \l__tblr_w_tl { \box_wd:N \l_tmpa_box }
+    \tl_set:Ne \l__tblr_w_tl { \dim_eval:n { \box_wd:N \l_tmpa_box } }
   }
 
 \cs_new_protected:Npn \__tblr_get_cell_size_with_hbox:
   {
     \tl_set_eq:NN \l_tmpb_tl \l__tblr_c_tl
-    \__tblr_insert_braces:N \l_tmpb_tl
-    \seq_set_split:NnV \l_tmpa_seq { \\ } \l_tmpb_tl
+    \__tblr_seq_set_split_keep_envs:NnV \l_tmpa_seq { \\ } \l_tmpb_tl
     \tl_set:Nn \l__tblr_w_tl { 0pt }
     \seq_map_variable:NNn \l_tmpa_seq \l_tmpa_tl
       {
-        \__tblr_remove_braces:N \l_tmpa_tl
         \hbox_set:Nn \l_tmpa_box
           {
             \l__tblr_f_tl
-            \__tblr_rescan_cell_tokens:N \l_tmpa_tl
+            \l_tmpa_tl
           }
-        \tl_set:Nx \l__tblr_w_tl
+        \tl_set:Ne \l__tblr_w_tl
           { \dim_max:nn { \l__tblr_w_tl } { \box_wd:N \l_tmpa_box } }
       }
   }
@@ -3785,8 +4741,10 @@
 \cs_new_protected:Npn \__tblr_get_vcell_and_sizes:NN #1 #2
   {
     \group_begin:
-    \vbox_set:Nn \l_tmpb_box { \__tblr_make_vcell_text:NN #1 #2 }
-    \vbox_set_top:Nn \l_tmpa_box { \vbox_unpack:N \l_tmpb_box }
+    \box_if_empty:NTF \l__tblr_measured_cell_box
+      { \vbox_set_top:Nn \l_tmpa_box { \__tblr_make_vcell_text:NN #1 #2 } }
+      { \box_set_eq:NN \l_tmpa_box \l__tblr_measured_cell_box }
+    \vbox_set:Nn \l_tmpb_box { \vbox_unpack:N \l_tmpa_box }
     \__tblr_process_stretch:
     \dim_gset:Nn \g__tblr_cell_wd_dim { \box_wd:N \l_tmpb_box }
     \dim_gset:Nn \g__tblr_cell_ht_dim
@@ -3805,7 +4763,7 @@
                   { \box_use:N \l_tmpa_box }
                 \c__tblr_middle_m_tl
                   {
-                    \tl_set:Nx \l__tblr_b_tl
+                    \tl_set:Ne \l__tblr_b_tl
                       {
                         \dim_eval:n
                           {
@@ -3837,29 +4795,9 @@
     \TblrParboxRestore
     \cs:w __tblr_halign_command_ \g__tblr_cell_halign_tl : \cs_end:
     \__tblr_leave_vmode:
-    \bool_if:NTF \l__tblr_cell_math_mode_bool
-      { #1 }
-      { \__tblr_rescan_cell_tokens:N #1 }
+    #1
   }
 
-%% When using verb option, there is an end-of-line character at the end.
-%% This character causes extra horizontal space at the end when "measure=hbox",
-%% or causes extra vertical space at the end with "measure=vbox".
-%% Therefore we have to use an \empty to remove it.
-%% See https://tex.stackexchange.com/q/213659
-\cs_new_protected:Npn \__tblr_rescan_cell_tokens:N #1
-  {
-    \tl_if_empty:NTF \l__tblr_inner_spec_verb_tl
-      { #1 }
-      {
-        %% insert space characters after some control sequences first (issue #112)
-        \regex_replace_all:nnN { (\c{[A-Za-z]*}) ([A-Za-z]) } { \1 \  \2 } #1
-        \regex_replace_all:nnN { . } { \c{string} \0 } #1
-        \tl_set:Nx #1 { #1 \noexpand \empty }
-        \exp_args:NV \tex_scantokens:D #1
-      }
-  }
-
 %% #1: total height dimension; #2: head dimension; #3: foot dimension;
 %% #4: tl for resulting upper size; #5: tl for resulting lower size
 
@@ -3871,19 +4809,19 @@
       {
         \c__tblr_middle_t_tl
           {
-            \tl_set:Nx #4 { \dim_use:N #2 }
-            \tl_set:Nx #5 { \dim_eval:n { #1 - #2 } }
+            \tl_set:Ne #4 { \dim_use:N #2 }
+            \tl_set:Ne #5 { \dim_eval:n { #1 - #2 } }
           }
         \c__tblr_middle_m_tl
           {
-            \tl_set:Nx \l__tblr_middle_body_tl { \dim_eval:n { #1 - #2 - #3 } }
-            \tl_set:Nx #4 { \dim_eval:n { #2 + \l__tblr_middle_body_tl / 2 } }
-            \tl_set:Nx #5 { \dim_eval:n { #3 + \l__tblr_middle_body_tl / 2 } }
+            \tl_set:Ne \l__tblr_middle_body_tl { \dim_eval:n { #1 - #2 - #3 } }
+            \tl_set:Ne #4 { \dim_eval:n { #2 + \l__tblr_middle_body_tl / 2 } }
+            \tl_set:Ne #5 { \dim_eval:n { #3 + \l__tblr_middle_body_tl / 2 } }
           }
         \c__tblr_middle_b_tl
           {
-            \tl_set:Nx #4 { \dim_eval:n { #1 - #3 } }
-            \tl_set:Nx #5 { \dim_use:N #3 }
+            \tl_set:Ne #4 { \dim_eval:n { #1 - #3 } }
+            \tl_set:Ne #5 { \dim_use:N #3 }
           }
       }
   }
@@ -3894,7 +4832,7 @@
 \cs_new_protected:Npn \__tblr_update_cell_size:nnNNNN #1 #2 #3 #4 #5 #6
   {
     \group_begin:
-    \tl_set:Nx \l__tblr_c_tl
+    \tl_set:Ne \l__tblr_c_tl
       { \__tblr_data_item:neen { cell } {#1} {#2} { colspan } }
     \int_compare:nNnT { \l__tblr_c_tl } > {1}
       {
@@ -3901,7 +4839,7 @@
         \__tblr_data_gput:neene { cell } {#1} {#2} { @cell-width } {\dim_use:N #3}
         \dim_gzero:N #3 % don't affect column width
       }
-    \tl_set:Nx \l__tblr_r_tl
+    \tl_set:Ne \l__tblr_r_tl
       { \__tblr_data_item:neen { cell } {#1} {#2} { rowspan } }
     \int_compare:nNnT { \l__tblr_r_tl } > {1}
       {
@@ -3909,8 +4847,8 @@
           {
             \c__tblr_valign_h_tl
               {
-                \tl_set:Nx \l__tblr_u_tl { \dim_use:N #5 }
-                \tl_set:Nx \l__tblr_v_tl { \dim_eval:n { #4 - #5 } }
+                \tl_set:Ne \l__tblr_u_tl { \dim_use:N #5 }
+                \tl_set:Ne \l__tblr_v_tl { \dim_eval:n { #4 - #5 } }
                 %% Update the head size of the first span row here
                 \__tblr_data_gput_if_larger:nene
                   { row } {#1} { @row-head } { \dim_use:N #5 }
@@ -3917,8 +4855,8 @@
               }
             \c__tblr_valign_f_tl
               {
-                \tl_set:Nx \l__tblr_u_tl { \dim_eval:n { #4 - #6 } }
-                \tl_set:Nx \l__tblr_v_tl { \dim_use:N #6 }
+                \tl_set:Ne \l__tblr_u_tl { \dim_eval:n { #4 - #6 } }
+                \tl_set:Ne \l__tblr_v_tl { \dim_use:N #6 }
                 %% Update the foot size of the last span row here
                 \__tblr_data_gput_if_larger:nene
                   { row }
@@ -3949,13 +4887,13 @@
   {
     \group_begin:
     %% Note that \l__tblr_h_tl may be empty
-    \tl_set:Nx \l__tblr_h_tl
+    \tl_set:Ne \l__tblr_h_tl
       { \__tblr_data_item:nen { row } {#1} { @row-height } }
     \tl_if_eq:NNTF \g__tblr_cell_valign_tl \c__tblr_valign_m_tl
       {
-        \tl_set:Nx \l__tblr_a_tl
+        \tl_set:Ne \l__tblr_a_tl
           { \__tblr_data_item:nen { row } {#1} { @row-upper } }
-        \tl_set:Nx \l__tblr_b_tl
+        \tl_set:Ne \l__tblr_b_tl
           { \__tblr_data_item:nen { row } {#1} { @row-lower } }
         \__tblr_get_middle_cell_upper_lower:NNNNN
           #3 #4 #5 \l__tblr_u_tl \l__tblr_v_tl
@@ -3977,9 +4915,9 @@
           }
       }
       {
-        \tl_set:Nx \l__tblr_e_tl
+        \tl_set:Ne \l__tblr_e_tl
           { \__tblr_data_item:nen { row } {#1} { @row-head } }
-        \tl_set:Nx \l__tblr_f_tl
+        \tl_set:Ne \l__tblr_f_tl
           { \__tblr_data_item:nen { row } {#1} { @row-foot } }
         \dim_compare:nNnT {#4} > {\l__tblr_e_tl}
           {
@@ -3989,8 +4927,8 @@
           {
             \__tblr_data_gput:nene { row } {#1} { @row-foot } { \dim_use:N #5 }
           }
-        \tl_set:Nx \l__tblr_x_tl { \dim_max:nn {#4} { \l__tblr_e_tl } }
-        \tl_set:Nx \l__tblr_y_tl { \dim_max:nn {#5} { \l__tblr_f_tl } }
+        \tl_set:Ne \l__tblr_x_tl { \dim_max:nn {#4} { \l__tblr_e_tl } }
+        \tl_set:Ne \l__tblr_y_tl { \dim_max:nn {#5} { \l__tblr_f_tl } }
         \dim_compare:nNnT
           { #3 - #4 - #5 } > { \l__tblr_h_tl - \l__tblr_x_tl - \l__tblr_y_tl }
           {
@@ -4013,7 +4951,7 @@
 
 \cs_new_protected:Npn \__tblr_update_col_size:nN #1 #2
   {
-    \tl_set:Nx \l_tmpb_tl
+    \tl_set:Ne \l_tmpb_tl
       { \__tblr_data_item:nen { column } {#1} { @col-width } }
     \bool_lazy_or:nnT
       { \tl_if_empty_p:N \l_tmpb_tl }
@@ -4024,15 +4962,15 @@
   }
 
 %%% --------------------------------------------------------
-%%> \section{Calculate and Adjust Extendable Columns}
+%%> \section{Calculate and adjust extendable columns}
 %%% --------------------------------------------------------
 
 %% Compute column widths when there are some extendable columns
 
-\dim_new:N \l__column_target_dim
-\prop_new:N \l__column_coefficient_prop
-\prop_new:N \l__column_natural_width_prop
-\prop_new:N \l__column_computed_width_prop
+\dim_new:N \l__tblr_column_target_dim
+\prop_new:N \l__tblr_column_coefficient_prop
+\prop_new:N \l__tblr_column_natural_width_prop
+\prop_new:N \l__tblr_column_computed_width_prop
 
 \msg_new:nnn { tabularray } { table-width-too-small }
   { Table ~ width ~ is ~ too ~ small, ~ need ~ #1 ~ more! }
@@ -4040,13 +4978,16 @@
 \cs_new_protected:Npn \__tblr_compute_extendable_column_width:
   {
     \__tblr_collect_extendable_column_width:
-    \dim_compare:nNnTF { \l__column_target_dim } < { 0pt }
+    \dim_compare:nNnTF { \l__tblr_column_target_dim } < { 0pt }
       {
-        \msg_warning:nnx { tabularray } { table-width-too-small }
-          { \dim_abs:n { \l__column_target_dim } }
+        \tl_if_empty:eF { \__tblr_prop_item:nn { inner } { width } }
+          {
+            \msg_warning:nne { tabularray } { table-width-too-small }
+              { \dim_abs:n { \l__tblr_column_target_dim } }
+          }
       }
       {
-        \prop_if_empty:NF \l__column_coefficient_prop
+        \prop_if_empty:NF \l__tblr_column_coefficient_prop
           { \__tblr_adjust_extendable_column_width: }
       }
   }
@@ -4053,70 +4994,77 @@
 
 \cs_new_protected:Npn \__tblr_collect_extendable_column_width:
   {
-    \tl_set:Nx \l_tmpa_tl { \__tblr_prop_item:nn { inner } { width } }
+    \tl_set:Ne \l_tmpa_tl { \__tblr_prop_item:nn { inner } { width } }
     \tl_if_empty:NTF \l_tmpa_tl
-      { \dim_set_eq:NN \l__column_target_dim \linewidth }
-      { \dim_set:Nn \l__column_target_dim { \l_tmpa_tl } }
-    \prop_clear:N \l__column_coefficient_prop
-    \prop_clear:N \l__column_natural_width_prop
-    \prop_clear:N \l__column_computed_width_prop
+      { \dim_set_eq:NN \l__tblr_column_target_dim \linewidth }
+      { \dim_set:Nn \l__tblr_column_target_dim { \l_tmpa_tl } }
+    \prop_clear:N \l__tblr_column_coefficient_prop
+    \prop_clear:N \l__tblr_column_natural_width_prop
+    \prop_clear:N \l__tblr_column_computed_width_prop
     \int_step_variable:nNn { \c at colcount } \l__tblr_j_tl
       {
-        \tl_set:Nx \l__tblr_a_tl
+        \tl_set:Ne \l__tblr_a_tl
           { \__tblr_data_item:nen { column } { \l__tblr_j_tl } { width } }
-        \tl_set:Nx \l__tblr_b_tl
+        \tl_set:Ne \l__tblr_b_tl
           { \__tblr_data_item:nen { column } { \l__tblr_j_tl } { coefficient } }
-        \tl_set:Nx \l__tblr_c_tl
+        \tl_set:Ne \l__tblr_c_tl
           { \__tblr_data_item:nen  { column } { \l__tblr_j_tl } { @col-width } }
         \dim_compare:nNnTF { \l__tblr_a_tl } < { 0pt } % column width unset
           {
             \dim_compare:nNnTF { \l__tblr_b_tl pt } = { 0pt }
-              { \dim_sub:Nn \l__column_target_dim { \l__tblr_c_tl } }
+              { \dim_sub:Nn \l__tblr_column_target_dim { \l__tblr_c_tl } }
               {
-                \prop_put:Nxx \l__column_coefficient_prop
+                \prop_put:Nee \l__tblr_column_coefficient_prop
                   { \l__tblr_j_tl } { \l__tblr_b_tl }
-                \prop_put:Nxn \l__column_computed_width_prop
+                \prop_put:Nen \l__tblr_column_computed_width_prop
                   { \l__tblr_j_tl } { 0pt }
                 \dim_compare:nNnF { \l__tblr_b_tl pt } > { 0pt }
                   {
-                    \prop_put:Nxx \l__column_natural_width_prop
+                    \prop_put:Nee \l__tblr_column_natural_width_prop
                       { \l__tblr_j_tl } { \l__tblr_c_tl }
                   }
               }
           }
-          { \dim_sub:Nn \l__column_target_dim { \l__tblr_a_tl } }
-        \tl_set:Nx \l__tblr_a_tl
+          { \dim_sub:Nn \l__tblr_column_target_dim { \l__tblr_a_tl } }
+        \tl_set:Ne \l__tblr_a_tl
           { \__tblr_spec_item:ne { vline } { [\l__tblr_j_tl] / @vline-width } }
-        \tl_set:Nx \l__tblr_b_tl
+        \tl_set:Ne \l__tblr_b_tl
           { \__tblr_data_item:nen { column } { \l__tblr_j_tl } { leftsep } }
-        \tl_set:Nx \l__tblr_c_tl
+        \tl_set:Ne \l__tblr_c_tl
           { \__tblr_data_item:nen { column } { \l__tblr_j_tl } { rightsep } }
-        \dim_set:Nn \l__column_target_dim
-          { \l__column_target_dim - \l__tblr_a_tl - \l__tblr_b_tl - \l__tblr_c_tl }
+        \dim_set:Nn \l__tblr_column_target_dim
+          {
+            \l__tblr_column_target_dim
+            - \l__tblr_a_tl - \l__tblr_b_tl - \l__tblr_c_tl
+          }
       }
-    \tl_set:Nx \l__tblr_a_tl
+    \tl_set:Ne \l__tblr_a_tl
       {
         \__tblr_spec_item:ne { vline }
           { [\int_eval:n {\c at colcount + 1}] / @vline-width }
       }
     \tl_if_empty:NF \l__tblr_a_tl
-      { \dim_sub:Nn \l__column_target_dim { \l__tblr_a_tl } }
+      { \dim_sub:Nn \l__tblr_column_target_dim { \l__tblr_a_tl } }
     \LogTblrTracing { target }
   }
 
+%% Users may modify \hfuzz, so we use our hfuzz dim variable (see issue #445)
+\dim_new:N \l__tblr_hfuzz_dim
+\dim_set:Nn \l__tblr_hfuzz_dim { 0.1pt }
+
 %% If all columns have negative coefficients and small natural widths,
-%% \l__column_coefficient_prop will be empty after one or more rounds.
+%% \l__tblr_column_coefficient_prop will be empty after one or more rounds.
 %% We reset @row-height, etc for \linewidth graphics in X columns (issue #80)
 \cs_new_protected:Npn \__tblr_adjust_extendable_column_width:
   {
     \bool_while_do:nn
-      { \dim_compare_p:nNn { \l__column_target_dim } > { \hfuzz } }
+      { \dim_compare_p:nNn { \l__tblr_column_target_dim } > { \l__tblr_hfuzz_dim } }
       {
-        \prop_if_empty:NTF \l__column_coefficient_prop
+        \prop_if_empty:NTF \l__tblr_column_coefficient_prop
           { \__tblr_adjust_extendable_column_width_negative: }
           { \__tblr_adjust_extendable_column_width_once: }
       }
-    \prop_map_inline:Nn \l__column_computed_width_prop
+    \prop_map_inline:Nn \l__tblr_column_computed_width_prop
       {
         \__tblr_data_gput:nnne { column } {##1} { width } {##2}
         \__tblr_data_gput:nnnn { column } {##1} { @col-width } { 0pt }
@@ -4136,38 +5084,38 @@
 \cs_new_protected:Npn \__tblr_adjust_extendable_column_width_once:
   {
     \dim_zero:N \l_tmpa_dim
-    \prop_map_inline:Nn \l__column_coefficient_prop
+    \prop_map_inline:Nn \l__tblr_column_coefficient_prop
       {
         \dim_add:Nn \l_tmpa_dim { \dim_abs:n { ##2 pt } }
       }
-    \tl_set:Nx \l__tblr_w_tl
-      { \dim_ratio:nn { \l__column_target_dim } { \l_tmpa_dim } }
-    \dim_zero:N \l__column_target_dim
-    \prop_map_inline:Nn \l__column_coefficient_prop
+    \tl_set:Ne \l__tblr_w_tl
+      { \dim_ratio:nn { \l__tblr_column_target_dim } { \l_tmpa_dim } }
+    \dim_zero:N \l__tblr_column_target_dim
+    \prop_map_inline:Nn \l__tblr_column_coefficient_prop
       {
-        \tl_set:Nx \l__tblr_a_tl
+        \tl_set:Ne \l__tblr_a_tl
           { \dim_eval:n { \dim_abs:n { ##2 pt } * \l__tblr_w_tl } }
         \dim_compare:nNnTF { ##2 pt } > { 0pt }
           {
             \__tblr_add_dimen_value:Nnn
-              \l__column_computed_width_prop { ##1 } { \l__tblr_a_tl }
+              \l__tblr_column_computed_width_prop { ##1 } { \l__tblr_a_tl }
           }
           {
-            \tl_set:Nx \l__tblr_b_tl
-              { \prop_item:Nn \l__column_natural_width_prop { ##1 } }
-            \tl_set:Nx \l__tblr_c_tl
-              { \prop_item:Nn \l__column_computed_width_prop { ##1 } }
+            \tl_set:Ne \l__tblr_b_tl
+              { \prop_item:Nn \l__tblr_column_natural_width_prop { ##1 } }
+            \tl_set:Ne \l__tblr_c_tl
+              { \prop_item:Nn \l__tblr_column_computed_width_prop { ##1 } }
             \dim_compare:nNnTF { \l__tblr_a_tl + \l__tblr_c_tl } > { \l__tblr_b_tl }
               {
-                \prop_put:Nnx \l__column_computed_width_prop
+                \prop_put:Nne \l__tblr_column_computed_width_prop
                   { ##1 } { \l__tblr_b_tl }
-                \dim_add:Nn \l__column_target_dim
+                \dim_add:Nn \l__tblr_column_target_dim
                   { \l__tblr_a_tl + \l__tblr_c_tl - \l__tblr_b_tl }
-                \prop_remove:Nn \l__column_coefficient_prop { ##1 }
+                \prop_remove:Nn \l__tblr_column_coefficient_prop { ##1 }
               }
               {
                 \__tblr_add_dimen_value:Nnn
-                  \l__column_computed_width_prop { ##1 } { \l__tblr_a_tl }
+                  \l__tblr_column_computed_width_prop { ##1 } { \l__tblr_a_tl }
               }
           }
       }
@@ -4177,22 +5125,22 @@
 \cs_new_protected:Npn \__tblr_adjust_extendable_column_width_negative:
   {
     \dim_zero:N \l_tmpa_dim
-    \prop_map_inline:Nn \l__column_natural_width_prop
+    \prop_map_inline:Nn \l__tblr_column_natural_width_prop
       { \dim_add:Nn \l_tmpa_dim { ##2 } }
-    \tl_set:Nx \l_tmpa_tl
-      { \dim_ratio:nn { \l__column_target_dim } { \l_tmpa_dim } }
-    \dim_zero:N \l__column_target_dim
-    \prop_map_inline:Nn \l__column_natural_width_prop
+    \tl_set:Ne \l_tmpa_tl
+      { \dim_ratio:nn { \l__tblr_column_target_dim } { \l_tmpa_dim } }
+    \dim_zero:N \l__tblr_column_target_dim
+    \prop_map_inline:Nn \l__tblr_column_natural_width_prop
       {
-        \tl_set:Nx \l_tmpb_tl { \dim_eval:n { ##2 * \l_tmpa_tl } }
+        \tl_set:Ne \l_tmpb_tl { \dim_eval:n { ##2 * \l_tmpa_tl } }
         \__tblr_add_dimen_value:Nnn
-          \l__column_computed_width_prop { ##1 } { \l_tmpb_tl }
+          \l__tblr_column_computed_width_prop { ##1 } { \l_tmpb_tl }
       }
     \LogTblrTracing { target }
   }
 
 %%% --------------------------------------------------------
-%%> \section{Calculate and Adjust Multispan Cells}
+%%> \section{Calculate and adjust multispan cells}
 %%% --------------------------------------------------------
 
 %% Compute and adjust widths when there are some span cells.
@@ -4205,7 +5153,7 @@
     \__tblr_prop_if_in:nnT { inner } { colspan }
       {
         \__tblr_collect_column_widths_skips:
-        \str_if_eq:xnTF
+        \str_if_eq:enTF
           { \__tblr_prop_item:ne { inner } { hspan } } { minimal }
           {
             \__tblr_set_span_widths_from_column_widths:
@@ -4238,7 +5186,7 @@
       {
         \int_compare:nNnTF { \l__tblr_j_tl } > { 1 }
           {
-            \prop_put:Nxx \l__tblr_col_item_skip_size_prop { skip[\l__tblr_j_tl] }
+            \prop_put:Nee \l__tblr_col_item_skip_size_prop { skip[\l__tblr_j_tl] }
               {
                 \dim_eval:n
                   {
@@ -4253,10 +5201,10 @@
               }
           }
           {
-            \prop_put:Nxn \l__tblr_col_item_skip_size_prop { skip[\l__tblr_j_tl] }
+            \prop_put:Nen \l__tblr_col_item_skip_size_prop { skip[\l__tblr_j_tl] }
               { 0pt }
           }
-        \prop_put:Nxx \l__tblr_col_item_skip_size_prop { item[\l__tblr_j_tl] }
+        \prop_put:Nee \l__tblr_col_item_skip_size_prop { item[\l__tblr_j_tl] }
           { \__tblr_data_item:nen { column } { \l__tblr_j_tl } { @col-width } }
       }
     \__tblr_do_if_tracing:nn { cellspan }
@@ -4270,7 +5218,7 @@
       {
         \int_compare:nNnTF { \l__tblr_i_tl } > { 1 }
           {
-            \prop_put:Nxx \l__tblr_row_item_skip_size_prop { skip[\l__tblr_i_tl] }
+            \prop_put:Nee \l__tblr_row_item_skip_size_prop { skip[\l__tblr_i_tl] }
               {
                 \dim_eval:n
                   {
@@ -4285,11 +5233,11 @@
               }
           }
           {
-            \prop_put:Nxn \l__tblr_row_item_skip_size_prop { skip[\l__tblr_i_tl] }
+            \prop_put:Nen \l__tblr_row_item_skip_size_prop { skip[\l__tblr_i_tl] }
               { 0pt }
           }
         \__tblr_collect_one_row_height:NN \l__tblr_i_tl \l__tblr_h_tl
-        \prop_put:Nxx \l__tblr_row_item_skip_size_prop
+        \prop_put:Nee \l__tblr_row_item_skip_size_prop
           { item[\l__tblr_i_tl] } { \l__tblr_h_tl }
       }
     \__tblr_do_if_tracing:nn { cellspan }
@@ -4299,7 +5247,7 @@
 %% #1: row number; #2: tl with result
 \cs_new_protected:Npn \__tblr_collect_one_row_height:NN #1 #2
   {
-    \tl_set:Nx #2 { \__tblr_data_item:nen { row } {#1} { @row-height } }
+    \tl_set:Ne #2 { \__tblr_data_item:nen { row } {#1} { @row-height } }
   }
 
 \cs_new_protected:Npn \__tblr_collect_span_widths:
@@ -4309,7 +5257,7 @@
       {
         \int_step_variable:nNn { \c at rowcount } \l__tblr_i_tl
           {
-            \tl_set:Nx \l__tblr_a_tl
+            \tl_set:Ne \l__tblr_a_tl
               {
                 \__tblr_data_item:neen { cell }
                   { \l__tblr_i_tl } { \l__tblr_j_tl } { colspan }
@@ -4316,7 +5264,7 @@
               }
             \int_compare:nNnT { \l__tblr_a_tl } > {1}
               {
-                \__tblr_put_if_larger:Nxx \l__tblr_col_span_size_prop
+                \__tblr_put_if_larger:Nee \l__tblr_col_span_size_prop
                   {
                     ( \l__tblr_j_tl -
                       \int_eval:n {\l__tblr_j_tl + \l__tblr_a_tl - 1} )
@@ -4342,7 +5290,7 @@
       {
         \int_step_variable:nNn { \c at colcount } \l__tblr_j_tl
           {
-            \tl_set:Nx \l__tblr_a_tl
+            \tl_set:Ne \l__tblr_a_tl
               {
                 \__tblr_data_item:neen { cell }
                   { \l__tblr_i_tl } { \l__tblr_j_tl } { rowspan }
@@ -4349,7 +5297,7 @@
               }
             \int_compare:nNnT { \l__tblr_a_tl } > {1}
               {
-                \tl_set:Nx \l__tblr_v_tl
+                \tl_set:Ne \l__tblr_v_tl
                   {
                     \__tblr_data_item:neen { cell }
                       { \l__tblr_i_tl } { \l__tblr_j_tl } { valign }
@@ -4356,7 +5304,7 @@
                   }
                 \tl_if_eq:NnT \l__tblr_v_tl { h }
                   {
-                    \tl_set:Nx \l__tblr_h_tl
+                    \tl_set:Ne \l__tblr_h_tl
                       {
                         \__tblr_data_item:nen { row }
                           { \l__tblr_i_tl } { @row-head }
@@ -4367,7 +5315,7 @@
                   }
                 \tl_if_eq:NnT \l__tblr_v_tl { f }
                   {
-                    \tl_set:Nx \l__tblr_d_tl
+                    \tl_set:Ne \l__tblr_d_tl
                       {
                         \__tblr_data_item:nen
                           { row }
@@ -4378,7 +5326,7 @@
                       { \l__tblr_i_tl } { \l__tblr_j_tl } { @cell-depth }
                       \l__tblr_d_tl
                   }
-                \__tblr_put_if_larger:Nxx \l__tblr_row_span_size_prop
+                \__tblr_put_if_larger:Nee \l__tblr_row_span_size_prop
                   {
                     ( \l__tblr_i_tl -
                       \int_eval:n {\l__tblr_i_tl + \l__tblr_a_tl - 1} )
@@ -4393,7 +5341,7 @@
                           { \l__tblr_i_tl } { \l__tblr_j_tl } { @cell-depth }
                       }
                   }
-                \prop_put:Nxx \l__tblr_row_span_to_row_prop
+                \prop_put:Nee \l__tblr_row_span_to_row_prop
                   { [\l__tblr_i_tl][\l__tblr_j_tl] }
                   { \int_eval:n {\l__tblr_i_tl + \l__tblr_a_tl - 1} }
               }
@@ -4409,17 +5357,17 @@
 %% Compute and set column widths from span widths
 \cs_new_protected:Npn \__tblr_set_column_widths_from_span_widths:
   {
-    \str_if_eq:xnTF
+    \str_if_eq:enTF
       { \__tblr_prop_item:ne { inner } { hspan } }
       { even }
       {
-        \__tblr_distribute_span_sizes_even:xNN
+        \__tblr_distribute_span_sizes_even:eNN
           { \int_use:N \c at colcount }
           \l__tblr_col_item_skip_size_prop
           \l__tblr_col_span_size_prop
       }
       {
-        \__tblr_distribute_span_sizes_default:xNN
+        \__tblr_distribute_span_sizes_default:eNN
           { \int_use:N \c at colcount }
           \l__tblr_col_item_skip_size_prop
           \l__tblr_col_span_size_prop
@@ -4430,7 +5378,7 @@
 %% Compute and set row heights from span heights
 \cs_new_protected:Npn \__tblr_set_row_heights_from_span_heights:
   {
-    \str_if_eq:xnTF
+    \str_if_eq:enTF
       { \__tblr_prop_item:ne { inner } { vspan } }
       { even }
       {
@@ -4440,7 +5388,7 @@
           \l__tblr_row_span_size_prop
       }
       {
-        \__tblr_distribute_span_sizes_default:xNN
+        \__tblr_distribute_span_sizes_default:eNN
           { \int_use:N \c at rowcount }
           \l__tblr_row_item_skip_size_prop
           \l__tblr_row_span_size_prop
@@ -4461,7 +5409,7 @@
           }
         \int_step_variable:nNn { \l__tblr_j_tl - 1 } \l__tblr_i_tl
           {
-            \tl_set:Nx \l__tblr_a_tl
+            \tl_set:Ne \l__tblr_a_tl
               { \prop_item:Ne #3 { (\l__tblr_i_tl-\l__tblr_j_tl) } }
             \tl_if_empty:NF \l__tblr_a_tl
               {
@@ -4470,14 +5418,14 @@
                   {
                     \__tblr_do_if_tracing:nn { cellspan }
                       {
-                        \tl_log:x
+                        \tl_log:e
                           { \l__tblr_j_tl : \l__tblr_i_tl -> \l__tblr_k_tl }
                       }
-                    \tl_set:Nx \l_tmpa_tl
+                    \tl_set:Ne \l_tmpa_tl
                       {
                         \prop_item:Ne #2 { itemskip[\l__tblr_k_tl] }
                       }
-                    \tl_set:Nx \l__tblr_a_tl
+                    \tl_set:Ne \l__tblr_a_tl
                       { \dim_eval:n { \l__tblr_a_tl - \l_tmpa_tl } }
                   }
                 \dim_compare:nNnT { \l__tblr_a_tl } > { \l__tblr_w_dim }
@@ -4486,23 +5434,23 @@
                   }
               }
           }
-        \prop_put:Nxx #2
+        \prop_put:Nee #2
           { item[\l__tblr_j_tl] } { \dim_use:N \l__tblr_w_dim }
         \int_compare:nNnT { \l__tblr_j_tl } < { #1 }
           {
-            \tl_set:Nx \l_tmpb_tl
+            \tl_set:Ne \l_tmpb_tl
               {
                 \prop_item:Ne #2
                   { skip[\int_eval:n { \l__tblr_j_tl + 1} ] }
               }
             \dim_add:Nn \l__tblr_w_dim { \l_tmpb_tl }
-            \prop_put:Nxx #2
+            \prop_put:Nee #2
               { itemskip[\l__tblr_j_tl] } { \dim_use:N \l__tblr_w_dim }
           }
       }
     \__tblr_do_if_tracing:nn { cellspan } { \prop_log:N #2 }
   }
-\cs_generate_variant:Nn \__tblr_distribute_span_sizes_default:nNN { x }
+\cs_generate_variant:Nn \__tblr_distribute_span_sizes_default:nNN { e }
 
 %% #1: total number of items
 %% #2: prop list with item sizes and skip sizes; #3: prop list with span sizes
@@ -4523,11 +5471,11 @@
           }
         \__tblr_do_if_tracing:nn { cellspan }
           {
-            \tl_log:x { \l__tblr_a_tl -> \l__tblr_b_tl : ~ \dim_use:N \l_tmpa_dim }
+            \tl_log:e { \l__tblr_a_tl -> \l__tblr_b_tl : ~ \dim_use:N \l_tmpa_dim }
           }
         \dim_compare:nNnT {\l_tmpa_dim} > {0pt}
           {
-            \tl_set:Nx \l_tmpa_tl
+            \tl_set:Ne \l_tmpa_tl
               { \dim_eval:n { \l_tmpa_dim / (\l__tblr_b_tl - \l__tblr_a_tl + 1) } }
             \int_step_inline:nnn { \l__tblr_a_tl } { \l__tblr_b_tl }
               {
@@ -4542,7 +5490,7 @@
       }
     \__tblr_do_if_tracing:nn { cellspan } { \prop_log:N #2 }
   }
-\cs_generate_variant:Nn \__tblr_distribute_span_sizes_even:nNN { x }
+\cs_generate_variant:Nn \__tblr_distribute_span_sizes_even:nNN { e }
 
 \cs_new_protected:Npn \__tblr_get_span_from_to:w (#1-#2)
   {
@@ -4564,15 +5512,15 @@
   {
     \int_step_variable:nNn { \c at rowcount } \l__tblr_i_tl
       {
-        \tl_set:Nx \l__tblr_h_tl
+        \tl_set:Ne \l__tblr_h_tl
           {
             \__tblr_data_item:nen { row } { \l__tblr_i_tl } { @row-head }
           }
-        \tl_set:Nx \l__tblr_d_tl
+        \tl_set:Ne \l__tblr_d_tl
           {
             \__tblr_data_item:nen { row } { \l__tblr_i_tl } { @row-foot }
           }
-        \tl_set:Nx \l__tblr_a_tl
+        \tl_set:Ne \l__tblr_a_tl
           {
             \prop_item:Ne \l__tblr_row_item_skip_size_prop { item[\l__tblr_i_tl] }
           }
@@ -4589,7 +5537,7 @@
       {
         \int_step_variable:nNn { \c at rowcount } \l__tblr_i_tl
           {
-            \tl_set:Nx \l__tblr_a_tl
+            \tl_set:Ne \l__tblr_a_tl
               {
                 \__tblr_data_item:neen { cell }
                   { \l__tblr_i_tl } { \l__tblr_j_tl } { colspan }
@@ -4596,7 +5544,7 @@
               }
             \int_compare:nNnT { \l__tblr_a_tl } > {1}
               {
-                \__tblr_calc_span_widths:xxN
+                \__tblr_calc_span_widths:eeN
                   { \l__tblr_j_tl }
                   { \int_eval:n { \l__tblr_j_tl + \l__tblr_a_tl - 1 } }
                   \l__tblr_w_dim
@@ -4614,17 +5562,17 @@
     \dim_set:Nn #3 { \prop_item:Ne \l__tblr_col_item_skip_size_prop { item[#1] } }
     \int_step_inline:nnn { #1 + 1 } { #2 }
       {
-        \tl_set:Nx \l_tmpa_tl
+        \tl_set:Ne \l_tmpa_tl
           { \prop_item:Ne \l__tblr_col_item_skip_size_prop { skip[##1] } }
-        \tl_set:Nx \l_tmpb_tl
+        \tl_set:Ne \l_tmpb_tl
           { \prop_item:Ne \l__tblr_col_item_skip_size_prop { item[##1] } }
         \dim_add:Nn #3 { \dim_eval:n { \l_tmpa_tl + \l_tmpb_tl } }
       }
   }
-\cs_generate_variant:Nn \__tblr_calc_span_widths:nnN { xxN }
+\cs_generate_variant:Nn \__tblr_calc_span_widths:nnN { ee }
 
 %%% --------------------------------------------------------
-%%> \section{Header and Footer Styles}
+%%> \section{Header and footer styles}
 %%% --------------------------------------------------------
 
 \prop_new:N \l__tblr_element_styles_prop
@@ -4652,11 +5600,11 @@
 \NewDocumentCommand \SetTblrStyle { m +m }
   {
     \tl_set:Nn \l__tblr_element_styles_tl {#2}
-    \keys_set:nn { tblr-element } {#1}
+    \__tblr_keys_set:nn { template/element } {#1}
     \ignorespaces
   }
 
-\keys_define:nn { tblr-element }
+\__tblr_keys_define:nn { template/element }
   {
     head    .meta:n = { firsthead, middlehead, lasthead },
     foot    .meta:n = { firstfoot, middlefoot, lastfoot },
@@ -4666,11 +5614,11 @@
 \cs_new_protected:Npn \__tblr_set_element_styles:n #1
   {
     \tl_set:Nn \l__tblr_element_name_tl {#1}
-    \keys_set:nV { tblr-style } \l__tblr_element_styles_tl
+    \__tblr_keys_set:nV { template/style } \l__tblr_element_styles_tl
   }
 \cs_generate_variant:Nn \__tblr_set_element_styles:n { V }
 
-\keys_define:nn { tblr-style }
+\__tblr_keys_define:nn { template/style }
   {
     halign  .code:n = \__tblr_element_gput_style:nn { halign } {#1},
     l       .meta:n = { halign = l },
@@ -4691,7 +5639,7 @@
 
 \cs_new_protected:Npn \__tblr_element_unknown_key:nn #1 #2
   {
-    \regex_match:NnTF \c__tblr_is_color_key_regex {#1}
+    \__tblr_if_color_value:nTF {#1}
       { \__tblr_style_put:en { \l__tblr_element_name_tl / fg } {#1} }
       {
         %% unknown key name has been changed to string in \l_keys_key_str
@@ -4708,13 +5656,13 @@
 \cs_generate_variant:Nn \__tblr_element_unknown_key:nn { Vn }
 
 %%% --------------------------------------------------------
-%%> \section{Helper Functions for Templates}
+%%> \section{Helper functions for templates}
 %%% --------------------------------------------------------
 
 \tl_new:N \l__tblr_template_name_tl
 \tl_new:N \l__tblr_template_code_tl
 
-\keys_define:nn { tblr-def-template }
+\__tblr_keys_define:nn { template/def }
   {
     unknown .code:n = \__tblr_def_template:V \l_keys_key_str,
   }
@@ -4722,23 +5670,25 @@
 %% #1: head/foot element; #2: template name; #3: template code
 %% If the template name = default, we enable the template at once
 %% Otherwise, we may enable the template by using \SetTblrTemplate command
-\NewDocumentCommand \DefTblrTemplate { m m +m }
+\NewDocumentCommand \DeclareTblrTemplate { m m +m }
   {
     \tl_set:Nn \l__tblr_template_name_tl {#2}
     \tl_set:Nn \l__tblr_template_code_tl {#3}
-    \keys_set:nn { tblr-def-template } {#1}
+    \__tblr_keys_set:nn { template/def } {#1}
     \ignorespaces
   }
-\cs_new_eq:NN \DeclareTblrTemplate \DefTblrTemplate
+\cs_new_eq:NN \DefTblrTemplate \DeclareTblrTemplate
 
 \cs_new_protected:Npn \__tblr_def_template:n #1
   {
+    \tl_if_exist:cF { l__tblr_template_ #1 _ \l__tblr_template_name_tl _tl }
+      { \tl_new:c { l__tblr_template_ #1 _ \l__tblr_template_name_tl _tl } }
     \tl_set_eq:cN { l__tblr_template_ #1 _ \l__tblr_template_name_tl _tl }
       \l__tblr_template_code_tl
   }
 \cs_generate_variant:Nn \__tblr_def_template:n { V }
 
-\keys_define:nn { tblr-set-template }
+\__tblr_keys_define:nn { template/set }
   {
     unknown .code:n = \__tblr_set_template:V \l_keys_key_str,
   }
@@ -4747,14 +5697,27 @@
 \NewDocumentCommand \SetTblrTemplate { m m }
   {
     \tl_set:Nn \l__tblr_template_name_tl {#2}
-    \keys_set:nn { tblr-set-template } {#1}
+    \__tblr_keys_set:nn { template/set } {#1}
     \ignorespaces
   }
 
+\msg_new:nnn { tabularray } { template-undefined }
+  {
+    Undefined ~ template ~ "#2" ~ for ~ element ~ "#1".
+  }
 \cs_new_protected:Npn \__tblr_set_template:n #1
   {
-    \tl_set_eq:cc { l__tblr_template_ #1 _default_tl }
-      { l__tblr_template_ #1 _ \l__tblr_template_name_tl _tl }
+    \tl_if_exist:cTF { l__tblr_template_ #1 _ \l__tblr_template_name_tl _tl }
+      {
+        \tl_if_exist:cF { l__tblr_template_ #1 _default_tl }
+          { \tl_new:c { l__tblr_template_ #1 _default_tl } }
+        \tl_set_eq:cc { l__tblr_template_ #1 _default_tl }
+          { l__tblr_template_ #1 _ \l__tblr_template_name_tl _tl }
+      }
+      {
+        \msg_error:nnee { tabularray } { template-undefined }
+          { #1 } { \l__tblr_template_name_tl }
+      }
   }
 \cs_generate_variant:Nn \__tblr_set_template:n { V }
 
@@ -4772,7 +5735,7 @@
 
 \NewDocumentCommand \UseTblrColor { m }
   {
-    \tl_set:Nx \l__tblr_use_color_tl { \GetTblrStyle {#1} { fg } }
+    \tl_set:Ne \l__tblr_use_color_tl { \GetTblrStyle {#1} { fg } }
     \tl_if_empty:NF \l__tblr_use_color_tl { \color { \l__tblr_use_color_tl } }
   }
 
@@ -4786,7 +5749,7 @@
 
 \NewDocumentCommand \UseTblrHang { m }
   {
-    \tl_set:Nx \l__tblr_use_hang_tl { \GetTblrStyle {#1} { hang } }
+    \tl_set:Ne \l__tblr_use_hang_tl { \GetTblrStyle {#1} { hang } }
     \tl_if_empty:NF \l__tblr_use_hang_tl
       {
         \tl_put_left:Nn \l__tblr_use_hang_tl
@@ -4800,7 +5763,7 @@
 
 \NewDocumentCommand \UseTblrIndent { m }
   {
-    \tl_set:Nx \l__tblr_use_indent_tl { \GetTblrStyle {#1} { indent } }
+    \tl_set:Ne \l__tblr_use_indent_tl { \GetTblrStyle {#1} { indent } }
     \tl_if_empty:NF \l__tblr_use_indent_tl
       { \exp_args:NNV \setlength \parindent \l__tblr_use_indent_tl }
   }
@@ -4857,24 +5820,26 @@
   }
 
 %%% --------------------------------------------------------
-%%> \section{Table Continuation Templates}
+%%> \section{Table continuation templates}
 %%% --------------------------------------------------------
 
 \tl_if_exist:NF \tblrcontfootname
   {
+    \tl_new:N \tblrcontfootname
     \tl_set:Nn \tblrcontfootname { Continued ~ on ~ next ~ page }
   }
 
 \tl_if_exist:NF \tblrcontheadname
   {
+    \tl_new:N \tblrcontheadname
     \tl_set:Nn \tblrcontheadname { ( Continued ) }
   }
 
-\DefTblrTemplate { contfoot-text } { normal } { \tblrcontfootname }
+\DeclareTblrTemplate { contfoot-text } { normal } { \tblrcontfootname }
 \SetTblrTemplate { contfoot-text } { normal }
 
-\DefTblrTemplate { contfoot } { empty } { }
-\DefTblrTemplate { contfoot } { plain }
+\DeclareTblrTemplate { contfoot } { empty } { }
+\DeclareTblrTemplate { contfoot } { plain }
   {
     \noindent
     \raggedleft
@@ -4881,7 +5846,7 @@
     \UseTblrTemplate { contfoot-text } { default }
     \par
   }
-\DefTblrTemplate { contfoot } { normal }
+\DeclareTblrTemplate { contfoot } { normal }
   {
     %% need to set parindent after alignment
     \raggedleft
@@ -4894,15 +5859,15 @@
   }
 \SetTblrTemplate { contfoot } { normal }
 
-\DefTblrTemplate { conthead-pre } { empty } { }
-\DefTblrTemplate { conthead-pre } { normal } { \space }
+\DeclareTblrTemplate { conthead-pre } { empty } { }
+\DeclareTblrTemplate { conthead-pre } { normal } { \space }
 \SetTblrTemplate { conthead-pre } { normal }
 
-\DefTblrTemplate { conthead-text } { normal } { \tblrcontheadname }
+\DeclareTblrTemplate { conthead-text } { normal } { \tblrcontheadname }
 \SetTblrTemplate { conthead-text } { normal }
 
-\DefTblrTemplate { conthead } { empty } { }
-\DefTblrTemplate { conthead } { plain }
+\DeclareTblrTemplate { conthead } { empty } { }
+\DeclareTblrTemplate { conthead } { plain }
   {
     \noindent
     \raggedright
@@ -4909,7 +5874,7 @@
     \UseTblrTemplate { conthead-text } { default }
     \par
   }
-\DefTblrTemplate { conthead } { normal }
+\DeclareTblrTemplate { conthead } { normal }
   {
     %% need to set parindent after alignment
     \raggedright
@@ -4923,13 +5888,13 @@
 \SetTblrTemplate { conthead } { normal }
 
 %%% --------------------------------------------------------
-%%> \section{Table Caption Templates}
+%%> \section{Table caption templates}
 %%% --------------------------------------------------------
 
 \tl_new:N \l__tblr_caption_short_tl
 
-\DefTblrTemplate { caption-lot } { empty } { }
-\DefTblrTemplate { caption-lot } { normal }
+\DeclareTblrTemplate { caption-lot } { empty } { }
+\DeclareTblrTemplate { caption-lot } { normal }
   {
     \tl_if_empty:NTF \lTblrEntryTl
       { \tl_set_eq:NN \l__tblr_caption_short_tl \lTblrCaptionTl }
@@ -4942,23 +5907,23 @@
 %% We need to use \hspace and \enskip, but not ~ or \space,
 %% since we want a correct hangindent caption paragraph.
 
-\DefTblrTemplate { caption-tag } { empty } { }
-\DefTblrTemplate { caption-tag } { normal } { \tablename\hspace{0.25em}\thetable }
+\DeclareTblrTemplate { caption-tag } { empty } { }
+\DeclareTblrTemplate { caption-tag } { normal } { \tablename\hspace{0.25em}\thetable }
 \SetTblrTemplate { caption-tag } { normal }
 
-\DefTblrTemplate { caption-sep } { empty } { }
-\DefTblrTemplate { caption-sep } { normal } { : \enskip }
+\DeclareTblrTemplate { caption-sep } { empty } { }
+\DeclareTblrTemplate { caption-sep } { normal } { : \enskip }
 \SetTblrTemplate { caption-sep } { normal }
 
-\DefTblrTemplate { caption-text } { empty } { }
-\DefTblrTemplate { caption-text } { normal } { \InsertTblrText { caption } }
+\DeclareTblrTemplate { caption-text } { empty } { }
+\DeclareTblrTemplate { caption-text } { normal } { \InsertTblrText { caption } }
 \SetTblrTemplate { caption-text } { normal }
 
 \box_new:N \l__tblr_caption_box
 \box_new:N \l__tblr_caption_left_box
 
-\DefTblrTemplate { caption } { empty } { }
-\DefTblrTemplate { caption } { plain }
+\DeclareTblrTemplate { caption } { empty } { }
+\DeclareTblrTemplate { caption } { plain }
   {
     \hbox_set:Nn \l__tblr_caption_box
       {
@@ -4978,7 +5943,7 @@
         \par
       }
   }
-\DefTblrTemplate { caption } { normal }
+\DeclareTblrTemplate { caption } { normal }
   {
     \hbox_set:Nn \l__tblr_caption_box
       {
@@ -5008,7 +5973,7 @@
         \par
       }
   }
-\DefTblrTemplate { caption } { simple }
+\DeclareTblrTemplate { caption } { simple }
   {
     \UseTblrAlign { caption }
     \UseTblrIndent { caption }
@@ -5021,8 +5986,8 @@
   }
 \SetTblrTemplate { caption } { normal }
 
-\DefTblrTemplate { capcont } { empty } { }
-\DefTblrTemplate { capcont } { plain }
+\DeclareTblrTemplate { capcont } { empty } { }
+\DeclareTblrTemplate { capcont } { plain }
   {
     \hbox_set:Nn \l__tblr_caption_box
       {
@@ -5044,7 +6009,7 @@
         \par
       }
   }
-\DefTblrTemplate { capcont } { normal }
+\DeclareTblrTemplate { capcont } { normal }
   {
     \hbox_set:Nn \l__tblr_caption_box
       {
@@ -5076,7 +6041,7 @@
         \par
       }
   }
-\DefTblrTemplate { capcont } { simple }
+\DeclareTblrTemplate { capcont } { simple }
   {
     \UseTblrAlign { caption }
     \UseTblrIndent { caption }
@@ -5092,7 +6057,7 @@
 \SetTblrTemplate { capcont} { normal }
 
 %%% --------------------------------------------------------
-%%> \section{Table Notes Templates}
+%%> \section{Table notes templates}
 %%% --------------------------------------------------------
 
 %% By default the targets generated by \hypertarget are too low
@@ -5106,7 +6071,7 @@
         \use:c { Hy at raisedlink }
           {
             \hypertarget
-              { tblr / \int_use:N \g__tblr_table_count_int / \tl_to_str:n {#1} }
+              { tblr / \int_use:N \c at tblrcount / \tl_to_str:n {#1} }
               { }
           }
       }
@@ -5118,17 +6083,17 @@
     \cs_if_exist:NTF \hyperlink
       {
         \hyperlink
-          { tblr / \int_use:N \g__tblr_table_count_int / \tl_to_str:n {#1} }
+          { tblr / \int_use:N \c at tblrcount / \tl_to_str:n {#1} }
           { #2 }
       }
       { #2 }
   }
 
-\DefTblrTemplate { note-border } { empty }
+\DeclareTblrTemplate { note-border } { empty }
   {
     \hypersetup { pdfborder = { 0 ~ 0 ~ 0 } }
   }
-\DefTblrTemplate { note-border } { normal }
+\DeclareTblrTemplate { note-border } { normal }
   {
     \hypersetup { pdfborder = { 0 ~ 0 ~ 1 } }
   }
@@ -5146,29 +6111,29 @@
       }
   }
 
-\DefTblrTemplate { note-tag } { empty } { }
-\DefTblrTemplate { note-tag } { normal }
+\DeclareTblrTemplate { note-tag } { empty } { }
+\DeclareTblrTemplate { note-tag } { normal }
   {
     \textsuperscript { \sffamily \UseTblrFont { note-tag } \InsertTblrNoteTag }
   }
 \SetTblrTemplate { note-tag } { normal }
 
-\DefTblrTemplate { note-target } { normal }
+\DeclareTblrTemplate { note-target } { normal }
   {
     \__tblr_hyper_target:V \InsertTblrNoteTag
   }
 \SetTblrTemplate { note-target } { normal }
 
-\DefTblrTemplate { note-sep } { empty } { }
-\DefTblrTemplate { note-sep } { normal } { \space }
+\DeclareTblrTemplate { note-sep } { empty } { }
+\DeclareTblrTemplate { note-sep } { normal } { \space }
 \SetTblrTemplate { note-sep } { normal }
 
-\DefTblrTemplate { note-text } { empty } { }
-\DefTblrTemplate { note-text } { normal } { \InsertTblrNoteText }
+\DeclareTblrTemplate { note-text } { empty } { }
+\DeclareTblrTemplate { note-text } { normal } { \InsertTblrNoteText }
 \SetTblrTemplate { note-text } { normal }
 
-\DefTblrTemplate { note } { empty } { }
-\DefTblrTemplate { note } { plain }
+\DeclareTblrTemplate { note } { empty } { }
+\DeclareTblrTemplate { note } { plain }
   {
     \MapTblrNotes
       {
@@ -5180,7 +6145,7 @@
         \par
       }
   }
-\DefTblrTemplate { note } { normal }
+\DeclareTblrTemplate { note } { normal }
   {
     \UseTblrAlign { note }
     \UseTblrIndent { note }
@@ -5200,7 +6165,7 @@
         \par
       }
   }
-\DefTblrTemplate { note } { inline }
+\DeclareTblrTemplate { note } { inline }
   {
     \UseTblrAlign { note }
     \UseTblrIndent { note }
@@ -5219,26 +6184,26 @@
 \SetTblrTemplate { note } { normal }
 
 %%% --------------------------------------------------------
-%%> \section{Table Remarks Templates}
+%%> \section{Table remarks templates}
 %%% --------------------------------------------------------
 
-\DefTblrTemplate { remark-tag } { empty } { }
-\DefTblrTemplate { remark-tag } { normal }
+\DeclareTblrTemplate { remark-tag } { empty } { }
+\DeclareTblrTemplate { remark-tag } { normal }
   {
     \itshape \UseTblrFont { remark-tag } \InsertTblrRemarkTag
   }
 \SetTblrTemplate { remark-tag } { normal }
 
-\DefTblrTemplate { remark-sep } { empty } { }
-\DefTblrTemplate { remark-sep } { normal } { : \space }
+\DeclareTblrTemplate { remark-sep } { empty } { }
+\DeclareTblrTemplate { remark-sep } { normal } { : \space }
 \SetTblrTemplate { remark-sep } { normal }
 
-\DefTblrTemplate { remark-text } { empty } { }
-\DefTblrTemplate { remark-text } { normal } { \InsertTblrRemarkText }
+\DeclareTblrTemplate { remark-text } { empty } { }
+\DeclareTblrTemplate { remark-text } { normal } { \InsertTblrRemarkText }
 \SetTblrTemplate { remark-text } { normal }
 
-\DefTblrTemplate { remark } { empty } { }
-\DefTblrTemplate { remark } { plain }
+\DeclareTblrTemplate { remark } { empty } { }
+\DeclareTblrTemplate { remark } { plain }
   {
     \MapTblrRemarks
       {
@@ -5249,7 +6214,7 @@
         \par
       }
   }
-\DefTblrTemplate { remark } { normal }
+\DeclareTblrTemplate { remark } { normal }
   {
     \UseTblrAlign { remark }
     \UseTblrIndent { remark }
@@ -5265,7 +6230,7 @@
         \par
       }
   }
-\DefTblrTemplate { remark } { inline }
+\DeclareTblrTemplate { remark } { inline }
   {
     \UseTblrAlign { remark }
     \UseTblrIndent { remark }
@@ -5283,7 +6248,7 @@
 \SetTblrTemplate { remark } { normal }
 
 %%% --------------------------------------------------------
-%%> \section{Header and Footer Templates}
+%%> \section{Header and footer templates}
 %%% --------------------------------------------------------
 
 \tl_new:N \g__tblr_template_firsthead_default_tl
@@ -5293,37 +6258,37 @@
 \tl_new:N \g__tblr_template_middlefoot_default_tl
 \tl_new:N \g__tblr_template_lastfoot_default_tl
 
-\keys_define:nn { tblr-def-template }
+\__tblr_keys_define:nn { template/def }
   {
     head .meta:n = { firsthead, middlehead, lasthead },
     foot .meta:n = { firstfoot, middlefoot, lastfoot },
   }
 
-\keys_define:nn { tblr-set-template }
+\__tblr_keys_define:nn { template/set }
   {
     head .meta:n = { firsthead, middlehead, lasthead },
     foot .meta:n = { firstfoot, middlefoot, lastfoot },
   }
 
-\DefTblrTemplate { head } { empty } { }
-\DefTblrTemplate { foot } { empty } { }
+\DeclareTblrTemplate { head } { empty } { }
+\DeclareTblrTemplate { foot } { empty } { }
 
-\DefTblrTemplate { firsthead } { normal }
+\DeclareTblrTemplate { firsthead } { normal }
   {
     \UseTblrTemplate { caption } { default }
   }
 
-\DefTblrTemplate { middlehead, lasthead } { normal }
+\DeclareTblrTemplate { middlehead, lasthead } { normal }
   {
     \UseTblrTemplate { capcont } { default }
   }
 
-\DefTblrTemplate { firstfoot, middlefoot } { normal }
+\DeclareTblrTemplate { firstfoot, middlefoot } { normal }
   {
     \UseTblrTemplate { contfoot } { default }
   }
 
-\DefTblrTemplate { lastfoot } { normal }
+\DeclareTblrTemplate { lastfoot } { normal }
   {
     \UseTblrTemplate { note } { default }
     \UseTblrTemplate { remark } { default }
@@ -5333,7 +6298,7 @@
 \SetTblrTemplate { foot } { normal }
 
 %%% --------------------------------------------------------
-%%> \section{Build the Whole Table}
+%%> \section{Build the whole table}
 %%% --------------------------------------------------------
 
 \cs_new:Npn \__tblr_box_height:N #1
@@ -5347,8 +6312,8 @@
     \__tblr_build_table_head_foot:
   }
 
-\tl_new:N \l__tblr_row_head_tl
-\tl_new:N \l__tblr_row_foot_tl
+\int_new:N \lTblrRowHeadInt
+\int_new:N \lTblrRowFootInt
 \box_new:N \l__tblr_row_head_box
 \box_new:N \l__tblr_row_foot_box
 \dim_new:N \l__tblr_row_head_foot_dim
@@ -5355,20 +6320,23 @@
 
 \cs_new_protected:Npn \__tblr_build_row_head_foot:
   {
-    %% \l__tblr_row_head_tl and \l__tblr_row_foot_tl may be empty
-    \tl_set:Nx \l__tblr_row_head_tl { \__tblr_prop_item:ne { inner } { rowhead } }
-    \int_compare:nNnTF { \l__tblr_row_head_tl + 0 } > { 0 }
+    %% \lTblrRowHeadInt could not be empty, so we append '+ 0'.
+    \int_set:Nn \lTblrRowHeadInt
+      { \__tblr_prop_item:ne { inner } { rowhead } + 0 }
+    \int_compare:nNnTF { \lTblrRowHeadInt } > { 0 }
       {
-        \__tblr_build_one_table:nnNN {1} { \l__tblr_row_head_tl }
+        \__tblr_build_one_table:nnNN {1} { \lTblrRowHeadInt }
           \c_true_bool \c_true_bool
       }
       { \__tblr_build_one_hline:n {1} }
     \box_set_eq:NN \l__tblr_row_head_box \l__tblr_table_box
-    \tl_set:Nx \l__tblr_row_foot_tl { \__tblr_prop_item:ne { inner } { rowfoot } }
-    \int_compare:nNnTF { \l__tblr_row_foot_tl + 0 } > { 0 }
+    %% \lTblrRowFootInt could not be empty, so we append '+ 0'.
+    \int_set:Nn \lTblrRowFootInt
+      { \__tblr_prop_item:ne { inner } { rowfoot } + 0 }
+    \int_compare:nNnTF { \lTblrRowFootInt } > { 0 }
       {
         \__tblr_build_one_table:nnNN
-          { \c at rowcount - \l__tblr_row_foot_tl + 1 } { \c at rowcount }
+          { \c at rowcount - \lTblrRowFootInt + 1 } { \c at rowcount }
           \c_true_bool \c_true_bool
       }
       { \__tblr_build_one_hline:n { \int_eval:n { \c at rowcount + 1 } } }
@@ -5380,14 +6348,15 @@
       }
   }
 
-\dim_new:N \tablewidth
+\dim_new:N \lTblrTableWidthDim
+\cs_set_eq:NN \tablewidth \lTblrTableWidthDim
 
 \cs_new_protected:Npn \__tblr_get_table_width:
   {
-    \dim_zero:N \tablewidth
+    \dim_zero:N \lTblrTableWidthDim
     \int_step_inline:nn { \c at colcount }
       {
-        \dim_add:Nn \tablewidth
+        \dim_add:Nn \lTblrTableWidthDim
           {
             \__tblr_spec_item:nn { vline } { [##1] / @vline-width }
             +
@@ -5398,7 +6367,7 @@
             \__tblr_data_item:nnn { column } {##1} { rightsep }
           }
       }
-    \dim_add:Nn \tablewidth
+    \dim_add:Nn \lTblrTableWidthDim
       {
         \__tblr_spec_item:ne { vline }
           { [\int_eval:n { \c at colcount + 1 }] / @vline-width }
@@ -5448,19 +6417,19 @@
 \bool_new:N \l__tblr_table_no_title_bool
 \bool_new:N \l__tblr_table_no_entry_bool
 \bool_new:N \l__tblr_table_no_label_bool
-\tl_const:Nn \c_tblr_none_tl { none }
+\tl_const:Nn \cTblrNoneTl { none }
 
 \cs_new_protected:Npn \__tblr_set_table_label_entry:
   {
-    \tl_set:Nx \lTblrCaptionTl { \InsertTblrText { caption } }
-    \tl_set:Nx \lTblrEntryTl { \InsertTblrText { entry } }
-    \tl_set:Nx \lTblrLabelTl { \InsertTblrText { label } }
+    \tl_set:Ne \lTblrCaptionTl { \InsertTblrText { caption } }
+    \tl_set:Ne \lTblrEntryTl { \InsertTblrText { entry } }
+    \tl_set:Ne \lTblrLabelTl { \InsertTblrText { label } }
     \bool_set:Nn \l__tblr_table_no_title_bool
       { \tl_if_empty_p:N \lTblrCaptionTl }
     \bool_set:Nn \l__tblr_table_no_entry_bool
-      { \tl_if_eq_p:NN \lTblrEntryTl \c_tblr_none_tl }
+      { \tl_if_eq_p:NN \lTblrEntryTl \cTblrNoneTl }
     \bool_set:Nn \l__tblr_table_no_label_bool
-      { \tl_if_eq_p:NN \lTblrLabelTl \c_tblr_none_tl }
+      { \tl_if_eq_p:NN \lTblrLabelTl \cTblrNoneTl }
     \bool_if:NT \l__tblr_table_no_title_bool
       {
         \SetTblrTemplate { conthead-pre } { empty }
@@ -5513,7 +6482,7 @@
   {
     \vbox_set:Nn #1
       {
-        \hsize = \tablewidth
+        \hsize = \lTblrTableWidthDim
         \TblrParboxRestore % it will set \linewidth = \hsize
         \vbox_set:Nn \l_tmpa_box {#2}
         \box_use:N \l_tmpa_box
@@ -5527,7 +6496,7 @@
   {
     \vbox_set:Nn #1
       {
-        \hsize = \tablewidth
+        \hsize = \lTblrTableWidthDim
         \TblrParboxRestore % it will set \linewidth = \hsize
         \vbox_set:Nn \l_tmpb_box {#2}
         \dim_compare:nNnT
@@ -5537,23 +6506,30 @@
       }
   }
 
+\tl_new:N \lTblrPortraitTypeTl
+
 \cs_new_protected:Npn \__tblr_build_whole:
   {
-    \__tblr_hook_use:n { tabularray/table/before }
-    \tl_if_eq:enTF { \__tblr_spec_item:nn { outer } { long } } { true }
-      { \__tblr_build_long_table:e { \__tblr_spec_item:nn { outer } { halign } } }
+    \__tblr_hook_use:n { table/before }
+    \tl_set:Ne \lTblrPortraitTypeTl
+      { \__tblr_spec_item:nn { outer } { portrait } }
+    \tl_if_eq:NnTF \lTblrPortraitTypeTl { long }
       {
-        \tl_if_eq:enTF { \__tblr_spec_item:nn { outer } { tall } } { true }
+        \__tblr_build_long_table:e { \__tblr_spec_item:nn { outer } { halign } }
+      }
+      {
+        \tl_if_eq:NnTF \lTblrPortraitTypeTl { tall }
           {
             \__tblr_build_tall_table:e
               { \__tblr_spec_item:nn { outer } { baseline } }
           }
           {
+            \tl_set:Nn \lTblrPortraitTypeTl { short }
             \__tblr_build_short_table:e
               { \__tblr_spec_item:nn { outer } { baseline } }
           }
       }
-    \__tblr_hook_use:n { tabularray/table/after }
+    \__tblr_hook_use:n { table/after }
   }
 
 \dim_new:N \l__tblr_remain_height_dim
@@ -5561,7 +6537,7 @@
 \int_new:N \l__tblr_long_to_int
 \int_new:N \l__tblr_curr_i_int
 \int_new:N \l__tblr_prev_i_int
-\int_new:N \l__tblr_table_page_int
+\int_new:N \lTblrTablePageInt
 \bool_new:N \l__tblr_page_break_curr_bool
 \bool_new:N \l__tblr_page_break_prev_bool
 
@@ -5584,12 +6560,12 @@
     \hrule height ~ 0pt
     \nobreak % prevent page break after \hrule (see issue #42)
     \LogTblrTracing { page }
-    \int_set:Nn \l__tblr_table_page_int {1}
+    \int_set:Nn \lTblrTablePageInt {1}
     \__tblr_build_head_foot:
     \dim_set:Nn \l__tblr_remain_height_dim
       { \pagegoal - \pagetotal - \l__tblr_row_head_foot_dim }
-    \int_set:Nn \l__tblr_long_from_int { \l__tblr_row_head_tl + 1 }
-    \int_set:Nn \l__tblr_long_to_int { \c at rowcount - ( \l__tblr_row_foot_tl + 0 ) }
+    \int_set:Nn \l__tblr_long_from_int { \lTblrRowHeadInt + 1 }
+    \int_set:Nn \l__tblr_long_to_int { \c at rowcount - \lTblrRowFootInt }
     \int_set:Nn \l__tblr_curr_i_int { \l__tblr_long_from_int - 1 }
     \int_do_while:nNnn { \l__tblr_curr_i_int } < { \l__tblr_long_to_int }
       {
@@ -5624,10 +6600,10 @@
                 \group_end:
               }
               {
-                \__tblr_build_page_table:nnx {#1}
+                \__tblr_build_page_table:nee {#1}
                   { \int_use:N \l__tblr_long_from_int }
                   { \int_use:N \l__tblr_prev_i_int }
-                \int_incr:N \l__tblr_table_page_int
+                \int_incr:N \lTblrTablePageInt
                 \int_set:Nn \l__tblr_long_from_int { \l__tblr_prev_i_int + 1 }
                 \TblrNewPage
               }
@@ -5640,10 +6616,10 @@
           {
             \bool_if:NTF \l__tblr_page_break_curr_bool
               {
-                \__tblr_build_page_table:nnx {#1}
+                \__tblr_build_page_table:nee {#1}
                   { \int_use:N \l__tblr_long_from_int }
                   { \int_use:N \l__tblr_curr_i_int }
-                \int_incr:N \l__tblr_table_page_int
+                \int_incr:N \lTblrTablePageInt
                 \TblrNewPage
                 \hbox{}\kern-\topskip\nobreak
                 \noindent
@@ -5655,7 +6631,7 @@
               { \dim_add:Nn \l__tblr_remain_height_dim { -\l_tmpa_dim } }
           }
       }
-    \int_compare:nNnTF { \l__tblr_table_page_int } = {1}
+    \int_compare:nNnTF { \lTblrTablePageInt } = {1}
       {
         \box_set_eq:NN \l__tblr_table_head_box \l__tblr_table_firsthead_box
         \box_set_eq:NN \l__tblr_table_foot_box \l__tblr_table_lastfoot_box
@@ -5664,8 +6640,9 @@
         \box_set_eq:NN \l__tblr_table_head_box \l__tblr_table_lasthead_box
         \box_set_eq:NN \l__tblr_table_foot_box \l__tblr_table_lastfoot_box
       }
-    \__tblr_build_page_table:nnn {#1}
-      { \int_use:N \l__tblr_long_from_int } { \int_use:N \l__tblr_long_to_int }
+    \__tblr_build_page_table:nee {#1}
+      { \int_use:N \l__tblr_long_from_int }
+      { \int_use:N \l__tblr_long_to_int }
     \skip_vertical:n { \__tblr_spec_item:nn { outer } { postsep } }
     % In the past we used "\hrule height ~ 0pt" to get strict postsep,
     % but the postsep was not discarded when page breaks, see issue #39.
@@ -5697,7 +6674,7 @@
           }
         \int_compare:nNnTF {#2} < {#1}
           {
-            \tl_set:Nx \l__tblr_b_tl
+            \tl_set:Ne \l__tblr_b_tl
               {
                 \__tblr_spec_item:ne { hline }
                   { [ \int_eval:n { #2 + 1 } ] / @pagebreak }
@@ -5723,7 +6700,7 @@
 %% #1: remain dimension; #2: row dimension; #3: break page or not
 \cs_new_protected:Npn \__tblr_check_table_page_break:NNN #1 #2 #3
   {
-    \int_compare:nNnTF { \l__tblr_table_page_int } = {1}
+    \int_compare:nNnTF { \lTblrTablePageInt } = {1}
       {
         \dim_set:Nn \l__tblr_table_head_body_foot_dim
           {
@@ -5757,10 +6734,14 @@
   }
 
 \box_new:N \l__tblr_table_box
+\int_new:N \lTblrRowFirstInt
+\int_new:N \lTblrRowLastInt
 
 %% #1: table alignment; #2: row from; #3: row to
 \cs_new_protected:Npn \__tblr_build_page_table:nnn #1 #2 #3
   {
+    \int_set:Nn \lTblrRowFirstInt {#2}
+    \int_set:Nn \lTblrRowLastInt {#3}
     \__tblr_build_one_table:nnNN {#2} {#3} \c_false_bool \c_false_bool
     \vbox_set:Nn \l__tblr_table_box
       {
@@ -5770,9 +6751,11 @@
         \hrule height ~ 0pt
         \box_use:N \l__tblr_table_foot_box
       }
+    \__tblr_hook_use:n { private/output/before }
     \__tblr_halign_whole:Nn \l__tblr_table_box {#1}
+    \__tblr_hook_use:n { private/output/after }
   }
-\cs_generate_variant:Nn \__tblr_build_page_table:nnn { nnx }
+\cs_generate_variant:Nn \__tblr_build_page_table:nnn { nee }
 
 %% To solve the problem of missing hlines of long tables in some PDF readers,
 %% We need to draw body rows before head rows (see issue #88).
@@ -5804,7 +6787,7 @@
 %% #1: table alignment
 %% For tall table, we need to leave vmode first.
 %% Since there may be \centering in table environment,
-%% We use \raggedright to reset alignement for table head/foot.
+%% We use \raggedright to reset alignment for table head/foot.
 \cs_new_protected:Npn \__tblr_build_tall_table:n #1
   {
     \mode_leave_vertical:
@@ -5818,7 +6801,9 @@
         \hrule height ~ 0pt
         \box_use:N \l__tblr_table_lastfoot_box
       }
+    \__tblr_hook_use:n { private/output/before }
     \__tblr_valign_whole:Nn \l__tblr_table_box {#1}
+    \__tblr_hook_use:n { private/output/after }
   }
 \cs_generate_variant:Nn \__tblr_build_tall_table:n { e }
 
@@ -5828,7 +6813,9 @@
   {
     \mode_leave_vertical:
     \__tblr_build_one_table:nnNN {1} {\c at rowcount} \c_true_bool \c_true_bool
+    \__tblr_hook_use:n { private/output/before }
     \__tblr_valign_whole:Nn \l__tblr_table_box {#1}
+    \__tblr_hook_use:n { private/output/after }
   }
 \cs_generate_variant:Nn \__tblr_build_short_table:n { e }
 
@@ -5898,18 +6885,15 @@
     \vbox_set:Nn \l__tblr_table_box { \hbox:n { \__tblr_build_hline:n { #1 } } }
   }
 
-\tl_new:N \__tblr_vbox_align_tl
-\tl_const:Nn \__tblr_vbox_t_tl {t}
-\tl_const:Nn \__tblr_vbox_T_tl {T}
-\tl_const:Nn \__tblr_vbox_m_tl {m}
-\tl_const:Nn \__tblr_vbox_M_tl {M}
-\tl_const:Nn \__tblr_vbox_c_tl {c}
-\tl_const:Nn \__tblr_vbox_b_tl {b}
-\tl_const:Nn \__tblr_vbox_B_tl {B}
+\tl_new:N \l__tblr_vbox_align_tl
+\tl_const:Nn \c__tblr_vbox_t_tl {t}
+\tl_const:Nn \c__tblr_vbox_T_tl {T}
+\tl_const:Nn \c__tblr_vbox_m_tl {m}
+\tl_const:Nn \c__tblr_vbox_M_tl {M}
+\tl_const:Nn \c__tblr_vbox_c_tl {c}
+\tl_const:Nn \c__tblr_vbox_b_tl {b}
+\tl_const:Nn \c__tblr_vbox_B_tl {B}
 
-\regex_const:Nn \c__tblr_is_positive_integer_regex { ^ \d+ $ }
-\regex_const:Nn \c__tblr_is_negative_integer_regex { ^ - \d+ $ }
-
 \tl_new:N \l__tblr_delim_left_tl
 \tl_new:N \l__tblr_delim_right_tl
 
@@ -5916,41 +6900,40 @@
 \cs_new_protected:Npn \__tblr_valign_whole:Nn #1 #2
   {
     \group_begin:
-    \tl_set:Nx \l__tblr_delim_left_tl
+    \tl_set:Ne \l__tblr_delim_left_tl
       { \__tblr_prop_item:nn { inner } { delim-left } }
-    \tl_set:Nx \l__tblr_delim_right_tl
+    \tl_set:Ne \l__tblr_delim_right_tl
       { \__tblr_prop_item:nn { inner } { delim-right } }
-    \tl_set:Nn \__tblr_vbox_align_tl {#2}
+    \tl_set:Nn \l__tblr_vbox_align_tl {#2}
     \dim_set:Nn \l__tblr_t_dim { \box_ht:N #1 + \box_dp:N #1 }
-    \tl_case:NnF \__tblr_vbox_align_tl
+    \tl_case:NnF \l__tblr_vbox_align_tl
       {
-        \__tblr_vbox_m_tl
+        \c__tblr_vbox_m_tl
           { \__tblr_valign_whole_middle:N #1 }
-        \__tblr_vbox_c_tl
+        \c__tblr_vbox_c_tl
           { \__tblr_valign_whole_middle:N #1 }
-        \__tblr_vbox_M_tl
+        \c__tblr_vbox_M_tl
           { \__tblr_valign_whole_middle_row_or_border:N #1 }
-        \__tblr_vbox_t_tl
+        \c__tblr_vbox_t_tl
           { \__tblr_valign_whole_top:N #1 }
-        \__tblr_vbox_T_tl
+        \c__tblr_vbox_T_tl
           {
-            \tl_set:Nn \__tblr_vbox_align_tl {1}
+            \tl_set:Nn \l__tblr_vbox_align_tl {1}
             \__tblr_valign_whole_at_row_from_above:N #1
           }
-        \__tblr_vbox_b_tl
+        \c__tblr_vbox_b_tl
           { \__tblr_valign_whole_bottom:N #1 }
-        \__tblr_vbox_B_tl
+        \c__tblr_vbox_B_tl
           {
-            \tl_set:Nx \__tblr_vbox_align_tl { \int_use:N \c at rowcount }
+            \tl_set:Ne \l__tblr_vbox_align_tl { \int_use:N \c at rowcount }
             \__tblr_valign_whole_at_row_from_below:N #1
           }
       }
       {
-        \regex_match:NVTF \c__tblr_is_positive_integer_regex \__tblr_vbox_align_tl
+        \__tblr_if_positive_value:VTF \l__tblr_vbox_align_tl
           { \__tblr_valign_whole_at_row:N #1 }
           {
-            \regex_match:NVTF
-              \c__tblr_is_negative_integer_regex \__tblr_vbox_align_tl
+            \__tblr_if_negative_value:VTF \l__tblr_vbox_align_tl
               { \__tblr_valign_whole_at_border:N #1 }
               { \__tblr_valign_whole_middle:N #1 }
           }
@@ -6018,11 +7001,11 @@
   {
     \int_if_odd:nTF { \c at rowcount }
       {
-        \tl_set:Nx \__tblr_vbox_align_tl { \int_eval:n { (\c at rowcount + 1) / 2 } }
+        \tl_set:Ne \l__tblr_vbox_align_tl { \int_eval:n { (\c at rowcount + 1) / 2 } }
         \__tblr_valign_whole_at_row_from_above:N #1
       }
       {
-        \tl_set:Nx \__tblr_vbox_align_tl { \int_eval:n { \c at rowcount / 2 + 1 } }
+        \tl_set:Ne \l__tblr_vbox_align_tl { \int_eval:n { \c at rowcount / 2 + 1 } }
         \__tblr_valign_whole_at_border_from_above:N #1
       }
   }
@@ -6029,7 +7012,7 @@
 
 \cs_new_protected:Npn \__tblr_valign_whole_at_row:N #1
   {
-    \int_compare:nNnTF { 2 * \__tblr_vbox_align_tl } > { \c at rowcount }
+    \int_compare:nNnTF { 2 * \l__tblr_vbox_align_tl } > { \c at rowcount }
       { \__tblr_valign_whole_at_row_from_below:N #1 }
       { \__tblr_valign_whole_at_row_from_above:N #1 }
   }
@@ -6037,10 +7020,10 @@
 \cs_new_protected:Npn \__tblr_valign_whole_at_row_from_above:N #1
   {
     \dim_set:Nn \l__tblr_h_dim
-      { \__tblr_valign_get_hline_total:n { \__tblr_vbox_align_tl } }
+      { \__tblr_valign_get_hline_total:n { \l__tblr_vbox_align_tl } }
     \dim_add:Nn \l__tblr_h_dim
-      { \__tblr_valign_get_row_height:n { \__tblr_vbox_align_tl } }
-    \int_step_inline:nn { \__tblr_vbox_align_tl - 1 }
+      { \__tblr_valign_get_row_height:n { \l__tblr_vbox_align_tl } }
+    \int_step_inline:nn { \l__tblr_vbox_align_tl - 1 }
       {
         \dim_add:Nn \l__tblr_h_dim { \__tblr_valign_get_hline_total:n {##1} }
         \dim_add:Nn \l__tblr_h_dim { \__tblr_valign_get_row_total:n {##1} }
@@ -6054,8 +7037,8 @@
     \dim_set:Nn \l__tblr_d_dim
       { \__tblr_valign_get_hline_total:n { \int_eval:n {\c at rowcount + 1} } }
     \dim_add:Nn \l__tblr_d_dim
-      { \__tblr_valign_get_row_depth:n { \__tblr_vbox_align_tl } }
-    \int_step_inline:nnn { \__tblr_vbox_align_tl + 1 } { \c at rowcount }
+      { \__tblr_valign_get_row_depth:n { \l__tblr_vbox_align_tl } }
+    \int_step_inline:nnn { \l__tblr_vbox_align_tl + 1 } { \c at rowcount }
       {
         \dim_add:Nn \l__tblr_d_dim { \__tblr_valign_get_hline_total:n {##1} }
         \dim_add:Nn \l__tblr_d_dim { \__tblr_valign_get_row_total:n {##1} }
@@ -6066,8 +7049,8 @@
 
 \cs_new_protected:Npn \__tblr_valign_whole_at_border:N #1
   {
-    \tl_set:Nx \__tblr_vbox_align_tl { \int_eval:n { - \__tblr_vbox_align_tl } }
-    \int_compare:nNnTF { 2 * \__tblr_vbox_align_tl - 2 } > { \c at rowcount }
+    \tl_set:Ne \l__tblr_vbox_align_tl { \int_eval:n { - \l__tblr_vbox_align_tl } }
+    \int_compare:nNnTF { 2 * \l__tblr_vbox_align_tl - 2 } > { \c at rowcount }
       { \__tblr_valign_whole_at_border_from_below:N #1 }
       { \__tblr_valign_whole_at_border_from_above:N #1 }
   }
@@ -6075,8 +7058,8 @@
 \cs_new_protected:Npn \__tblr_valign_whole_at_border_from_above:N #1
   {
     \dim_set:Nn \l__tblr_h_dim
-      { \__tblr_valign_get_hline_total:n { \__tblr_vbox_align_tl } }
-    \int_step_inline:nn { \__tblr_vbox_align_tl - 1 }
+      { \__tblr_valign_get_hline_total:n { \l__tblr_vbox_align_tl } }
+    \int_step_inline:nn { \l__tblr_vbox_align_tl - 1 }
       {
         \dim_add:Nn \l__tblr_h_dim { \__tblr_valign_get_hline_total:n {##1} }
         \dim_add:Nn \l__tblr_h_dim { \__tblr_valign_get_row_total:n {##1} }
@@ -6088,7 +7071,7 @@
 \cs_new_protected:Npn \__tblr_valign_whole_at_border_from_below:N #1
   {
     \dim_zero:N \l__tblr_d_dim
-    \int_step_inline:nnn { \__tblr_vbox_align_tl } { \c at rowcount }
+    \int_step_inline:nnn { \l__tblr_vbox_align_tl } { \c at rowcount }
       {
         \dim_add:Nn \l__tblr_d_dim { \__tblr_valign_get_row_total:n {##1} }
         \dim_add:Nn \l__tblr_d_dim
@@ -6105,22 +7088,22 @@
 
 \cs_new_nopar:Npn \__tblr_valign_get_row_total:n #1
   {
-    \__tblr_data_item:nnn { row } {#1} { abovesep }
+    \__tblr_data_item:nen { row } {#1} { abovesep }
     +
-    \__tblr_data_item:nnn { row } {#1} { @row-height }
+    \__tblr_data_item:nen { row } {#1} { @row-height }
     +
-    \__tblr_data_item:nnn { row } {#1} { belowsep }
+    \__tblr_data_item:nen { row } {#1} { belowsep }
   }
 
 \cs_new_nopar:Npn \__tblr_valign_get_row_height:n #1
   {
-    \__tblr_data_item:nnn { row } {#1} { abovesep }
+    \__tblr_data_item:nen { row } {#1} { abovesep }
     +
-    ( \__tblr_data_item:nnn { row } {#1} { @row-height }
+    ( \__tblr_data_item:nen { row } {#1} { @row-height }
       +
-      \__tblr_data_item:nnn { row } {#1} { @row-upper }
+      \__tblr_data_item:nen { row } {#1} { @row-upper }
       -
-      \__tblr_data_item:nnn { row } {#1} { @row-lower }
+      \__tblr_data_item:nen { row } {#1} { @row-lower }
     ) / 2
   }
 
@@ -6133,11 +7116,11 @@
       \__tblr_data_item:nen { row } {#1} { @row-lower }
     ) / 2
     +
-    \__tblr_data_item:nnn { row } {#1} { belowsep }
+    \__tblr_data_item:nen { row } {#1} { belowsep }
   }
 
 %%% --------------------------------------------------------
-%%> \section{Build Table Components}
+%%> \section{Build table components}
 %%% --------------------------------------------------------
 
 \dim_new:N \l__tblr_col_o_wd_dim
@@ -6154,9 +7137,9 @@
 %% #1: row number, #2: column number
 \cs_new_protected:Npn \__tblr_build_hline_segment:nn #1 #2
   {
-    \tl_set:Nx \l__tblr_n_tl
+    \tl_set:Ne \l__tblr_n_tl
       { \__tblr_spec_item:ne { hline } { [#1] / @hline-count } }
-    \tl_set:Nx \l__tblr_o_tl
+    \tl_set:Ne \l__tblr_o_tl
       { \__tblr_spec_item:ne { hline } { [#1][#2] / omit } }
     \__tblr_get_col_outer_width_border_width:nNN {#2}
       \l__tblr_col_o_wd_dim \l__tblr_col_b_wd_dim
@@ -6174,10 +7157,12 @@
     \skip_horizontal:n { \l__tblr_col_o_wd_dim - \l__tblr_col_b_wd_dim }
   }
 
+\tl_new:N \lTblrDefaultHruleColorTl
+
 %% #1: row number, #2: column number
 \cs_new_protected:Npn \__tblr_build_hline_segment_real:nn #1 #2
   {
-    \tl_set:Nx \l__tblr_s_tl
+    \tl_set:Ne \l__tblr_s_tl
       { \__tblr_prop_item:ne { inner } { rulesep } }
     \vbox_set:Nn \l__tblr_c_box
       {
@@ -6185,7 +7170,7 @@
         \tex_hbox:D to \l__tblr_col_o_wd_dim {}
         \int_step_inline:nn { \l__tblr_n_tl }
           {
-            \tl_set:Nx \l__tblr_h_tl
+            \tl_set:Ne \l__tblr_h_tl
               { \__tblr_spec_item:ne { hline } { [#1](##1) / @hline-height } }
             \hrule height ~ 0pt % remove lineskip
             \hbox_set_to_wd:Nnn \l__tblr_b_box { \l__tblr_col_o_wd_dim }
@@ -6192,9 +7177,14 @@
               {
                 \__tblr_get_hline_left_right_skips:nnn {#1} {#2} {##1}
                 \skip_horizontal:N \l__tblr_hline_leftskip_dim
-                \tl_set:Nx \l__tblr_f_tl
+                \tl_set:Ne \l__tblr_f_tl
                   { \__tblr_spec_item:ne { hline } { [#1][#2](##1) / fg } }
-                \tl_if_empty:NF \l__tblr_f_tl { \color{\l__tblr_f_tl} }
+                \tl_if_empty:NTF \l__tblr_f_tl
+                  {
+                    \tl_if_empty:NF \lTblrDefaultHruleColorTl
+                      { \color { \lTblrDefaultHruleColorTl } }
+                  }
+                  { \color { \l__tblr_f_tl } }
                 \__tblr_get_hline_segment_child:nnn {#1} {#2} {##1}
                 \skip_horizontal:N \l__tblr_hline_rightskip_dim
               }
@@ -6237,11 +7227,11 @@
 %% #1: row number; #2: column number; #3: hline index;
 \cs_new_protected:Npn \__tblr_get_hline_left_right_skips:nnn #1 #2 #3
   {
-    \tl_set:Nx \l__tblr_hline_leftpos_tl
+    \tl_set:Ne \l__tblr_hline_leftpos_tl
       { \__tblr_spec_item:ne { hline } { [#1][#2](#3) / leftpos } }
     \tl_if_empty:NT \l__tblr_hline_leftpos_tl
       { \tl_set:Nn \l__tblr_hline_leftpos_tl {1} } % default position
-    \tl_set:Nx \l__tblr_hline_rightpos_tl
+    \tl_set:Ne \l__tblr_hline_rightpos_tl
       { \__tblr_spec_item:ne { hline } { [#1][#2](#3) / rightpos } }
     \tl_if_empty:NT \l__tblr_hline_rightpos_tl
       { \tl_set:Nn \l__tblr_hline_rightpos_tl {1} } % default position
@@ -6300,7 +7290,7 @@
     \__tblr_get_row_inner_height_depth:VNNNN #1
       \l__tblr_row_ht_dim \l__tblr_row_dp_dim
       \l__tblr_row_abovesep_dim \l__tblr_row_belowsep_dim
-    \__tblr_hook_use:n { tabularray/row/before }
+    \__tblr_hook_use:n { row/before }
     \tblr_vrule_wd_ht_dp:nnn {0pt} {\l__tblr_row_ht_dim} {\l__tblr_row_dp_dim}
     \hbox_set:Nn \l__tblr_row_vlines_box
       {
@@ -6316,7 +7306,7 @@
       { \__tblr_build_vline_segment:nn {#1} { \int_eval:n {\c at colcount + 1} } }
     \skip_horizontal:n { - \box_wd:N \l__tblr_row_vlines_box }
     \box_use:N \l__tblr_row_vlines_box
-    \__tblr_hook_use:n { tabularray/row/after }
+    \__tblr_hook_use:n { row/after }
   }
 
 %% Read from table specifications and calculate inner height/depth of the row
@@ -6325,9 +7315,9 @@
 %% #1: the row number; #2: resulting inner height; #3: resulting inner depth;
 %% #4: restulting abovesep; #5: restulting belowsep.
 
-\dim_new:N \l__row_upper_dim
-\dim_new:N \l__row_lower_dim
-\dim_new:N \l__row_vpace_dim
+\dim_new:N \l__tblr_row_upper_dim
+\dim_new:N \l__tblr_row_lower_dim
+\dim_new:N \l__tblr_row_vpace_dim
 
 \cs_new_protected:Npn \__tblr_get_row_inner_height_depth:nNNNN #1 #2 #3 #4 #5
   {
@@ -6335,17 +7325,17 @@
       { \__tblr_data_item:nen { row } {#1} { abovesep } }
     \dim_set:Nn #5
       { \__tblr_data_item:nen { row } {#1} { belowsep } }
-    \dim_set:Nn \l__row_upper_dim
+    \dim_set:Nn \l__tblr_row_upper_dim
       {  \__tblr_data_item:nen { row } {#1} { @row-upper } }
-    \dim_set:Nn \l__row_lower_dim
+    \dim_set:Nn \l__tblr_row_lower_dim
       {  \__tblr_data_item:nen { row } {#1} { @row-lower } }
-    \dim_set:Nn \l__row_vpace_dim
+    \dim_set:Nn \l__tblr_row_vpace_dim
       {
         ( \__tblr_data_item:nen { row } {#1} { @row-height }
-          - \l__row_upper_dim - \l__row_lower_dim ) / 2
+          - \l__tblr_row_upper_dim - \l__tblr_row_lower_dim ) / 2
       }
-    \dim_set:Nn #2 { #4 + \l__row_vpace_dim + \l__row_upper_dim }
-    \dim_set:Nn #3 { \l__row_lower_dim + \l__row_vpace_dim + #5 }
+    \dim_set:Nn #2 { #4 + \l__tblr_row_vpace_dim + \l__tblr_row_upper_dim }
+    \dim_set:Nn #3 { \l__tblr_row_lower_dim + \l__tblr_row_vpace_dim + #5 }
   }
 \cs_generate_variant:Nn \__tblr_get_row_inner_height_depth:nNNNN { V }
 
@@ -6374,9 +7364,9 @@
 %% #1: row number, #2: column number
 \cs_new_protected:Npn \__tblr_build_vline_segment:nn #1 #2
   {
-    \tl_set:Nx \l__tblr_n_tl
+    \tl_set:Ne \l__tblr_n_tl
       { \__tblr_spec_item:ne { vline } { [#2] / @vline-count } }
-    \tl_set:Nx \l__tblr_o_tl
+    \tl_set:Ne \l__tblr_o_tl
       { \__tblr_spec_item:ne { vline } { [#1][#2] / omit } }
     \tl_if_empty:NTF \l__tblr_o_tl
       {
@@ -6389,33 +7379,40 @@
 %% #1: row number, #2: column number
 \cs_new_protected:Npn \__tblr_build_vline_segment_omit:nn #1 #2
   {
-    \tl_set:Nx \l__tblr_w_tl
+    \tl_set:Ne \l__tblr_w_tl
       { \__tblr_spec_item:ne { vline } { [#2] / @vline-width } }
     \skip_horizontal:N \l__tblr_w_tl
   }
 
+\tl_new:N \lTblrDefaultVruleColorTl
+
 %% #1: row number, #2: column number
 %% We make every vline segment intersect with first hline below
 %% to remove gaps in vlines around multirow cells
 \cs_new_protected:Npn \__tblr_build_vline_segment_real:nn #1 #2
   {
-    \tl_set:Nx \l__tblr_s_tl
+    \tl_set:Ne \l__tblr_s_tl
       { \__tblr_prop_item:ne { inner } { rulesep } }
     \hbox_set:Nn \l__tblr_a_box
       {
         \int_step_inline:nn { \l__tblr_n_tl }
           {
-            \tl_set:Nx \l__tblr_w_tl
+            \tl_set:Ne \l__tblr_w_tl
               { \__tblr_spec_item:ne { vline } { [#2](##1) / @vline-width } }
             \vbox_set_to_ht:Nnn \l__tblr_b_box
               { \dim_eval:n { \l__tblr_row_ht_dim + \l__tblr_row_dp_dim } }
               {
-                \tl_set:Nx \l__tblr_f_tl
+                \tl_set:Ne \l__tblr_f_tl
                   { \__tblr_spec_item:ne { vline } { [#1][#2](##1) / fg } }
-                \tl_if_empty:NF \l__tblr_f_tl { \color{\l__tblr_f_tl} }
+                \tl_if_empty:NTF \l__tblr_f_tl
+                  {
+                    \tl_if_empty:NF \lTblrDefaultVruleColorTl
+                      { \color { \lTblrDefaultVruleColorTl } }
+                  }
+                  { \color { \l__tblr_f_tl } }
                 \__tblr_get_vline_above_below_skips:nnn {#1} {#2} {##1}
                 \skip_vertical:N \l__tblr_vline_aboveskip_dim
-                \__tblr_get_vline_segment_child:nnnxx {#1} {#2} {##1}
+                \__tblr_get_vline_segment_child:nnnee {#1} {#2} {##1}
                   { \dim_eval:n { \l__tblr_row_ht_dim } }
                   { \dim_eval:n { \l__tblr_row_dp_dim } }
                 \skip_vertical:N \l__tblr_vline_belowskip_dim
@@ -6439,7 +7436,7 @@
 %% #1: row number; #2: column number; #3: vline index;
 \cs_new_protected:Npn \__tblr_get_vline_above_below_skips:nnn #1 #2 #3
   {
-    \tl_set:Nx \l__tblr_vline_abovepos_tl
+    \tl_set:Ne \l__tblr_vline_abovepos_tl
       { \__tblr_spec_item:ne { vline } { [#1][#2](#3) / abovepos } }
     \tl_if_empty:NT \l__tblr_vline_abovepos_tl
       {
@@ -6461,7 +7458,7 @@
       }
     %% To join two vline segment above and below a cline,
     %% we choose to extend every vline downwards a little (#55, #272).
-    \tl_set:Nx \l__tblr_vline_belowpos_tl
+    \tl_set:Ne \l__tblr_vline_belowpos_tl
       { \__tblr_spec_item:ne { vline } { [#1][#2](#3) / belowpos } }
     \tl_if_empty:NTF \l__tblr_vline_belowpos_tl
       {
@@ -6491,8 +7488,8 @@
   }
 
 %% These public variables are updated by default before building a cell
-\tl_new:N \lTblrCellRowSpanTl
-\tl_new:N \lTblrCellColSpanTl
+\int_new:N \lTblrCellRowSpanInt
+\int_new:N \lTblrCellColSpanInt
 \tl_new:N \lTblrCellBackgroundTl
 \bool_new:N \lTblrCellOmittedBool
 
@@ -6504,25 +7501,25 @@
     \int_set:Nn \c at colnum {#2}
     \__tblr_update_colsep_registers:
     \group_begin:
-    \tl_set:Nx \l__tblr_w_tl
+    \tl_set:Ne \l__tblr_w_tl
       { \__tblr_data_item:nen { column } {#2} { @col-width } }
-    \tl_set:Nx \l__tblr_h_tl
+    \tl_set:Ne \l__tblr_h_tl
       { \__tblr_data_item:nen { row } {#1} { @row-height } }
-    \tl_set:Nx \l__tblr_x_tl
+    \tl_set:Ne \l__tblr_x_tl
       { \__tblr_data_item:nen { column } {#2} { leftsep} }
-    \tl_set:Nx \l__tblr_y_tl
+    \tl_set:Ne \l__tblr_y_tl
       { \__tblr_data_item:nen { column } {#2} { rightsep } }
-    \tl_set:Nx \lTblrCellColSpanTl
+    \int_set:Nn \lTblrCellColSpanInt
       { \__tblr_data_item:neen { cell } {#1} {#2} { colspan } }
-    \int_compare:nNnTF { \lTblrCellColSpanTl } < {2}
+    \int_compare:nNnTF { \lTblrCellColSpanInt } < {2}
       { \dim_set:Nn \l__tblr_cell_wd_dim { \l__tblr_w_tl } }
       {
         \__tblr_get_span_horizontal_sizes:NNNNN #1 #2
           \l__tblr_o_dim \l__tblr_cell_wd_dim \l__tblr_q_dim
       }
-    \tl_set:Nx \lTblrCellRowSpanTl
+    \int_set:Nn \lTblrCellRowSpanInt
       { \__tblr_data_item:neen { cell } {#1} {#2} { rowspan } }
-    \int_compare:nNnTF { \lTblrCellRowSpanTl } < {2}
+    \int_compare:nNnTF { \lTblrCellRowSpanInt } < {2}
       { \dim_set:Nn \l__tblr_cell_ht_dim { \l__tblr_h_tl } }
       {
         \__tblr_get_span_vertical_sizes:NNNNN #1 #2
@@ -6536,17 +7533,17 @@
 
 %% These public variables are updated by html library before building a cell
 \tl_new:N \lTblrCellAboveBorderStyleTl
-\tl_new:N \lTblrCellAboveBorderWidthTl
+\dim_new:N \lTblrCellAboveBorderWidthDim
 \tl_new:N \lTblrCellAboveBorderColorTl
 \tl_new:N \lTblrCellBelowBorderStyleTl
-\tl_new:N \lTblrCellBelowBorderWidthTl
+\dim_new:N \lTblrCellBelowBorderWidthDim
 \tl_new:N \lTblrCellBelowBorderColorTl
 \tl_new:N \lTblrCellLeftBorderStyleTl
-\tl_new:N \lTblrCellLeftBorderWidthTl
+\dim_new:N \lTblrCellLeftBorderWidthDim
 \tl_new:N \lTblrCellLeftBorderColorTl
 \tl_new:N \lTblrCellRightBorderStyleTl
-\tl_new:N \lTblrCellRightBorderWidthTl
-\tl_new:N \lTblrCellRihgtBorderColorTl
+\dim_new:N \lTblrCellRightBorderWidthDim
+\tl_new:N \lTblrCellRightBorderColorTl
 
 %% #1: row number in tl; #2: column number in tl
 %% This function is called only when html library is loaded.
@@ -6554,16 +7551,14 @@
 \cs_new_protected:Npn \__tblr_expose_cell_properties:NN #1 #2
   {
     \__tblr_expose_cell_border:NNnn #1 #2 { hline } { Above }
-    \tl_set:Nx \l_tmpa_tl { \int_eval:n { #1 + \lTblrCellRowSpanTl } }
+    \tl_set:Ne \l_tmpa_tl { \int_eval:n { #1 + \lTblrCellRowSpanInt } }
     \__tblr_expose_cell_border:NNnn \l_tmpa_tl #2 { hline } { Below }
     \__tblr_expose_cell_border:NNnn #1 #2 { vline } { Left }
-    \tl_set:Nx \l_tmpb_tl { \int_eval:n { #2 + \lTblrCellColSpanTl } }
+    \tl_set:Ne \l_tmpb_tl { \int_eval:n { #2 + \lTblrCellColSpanInt } }
     \__tblr_expose_cell_border:NNnn #1 \l_tmpb_tl { vline } { Right }
   }
 
 \tl_new:N \l__tblr_dash_value_tl
-\tl_new:N \l__tblr_dash_value_head_tl
-\tl_new:N \l__tblr_dash_value_tail_tl
 \tl_new:N \l__tblr_width_value_tl
 \tl_new:N \l__tblr_color_value_tl
 
@@ -6572,26 +7567,28 @@
 \cs_new_protected:Npn \__tblr_expose_cell_border:NNnn #1 #2 #3 #4
   {
     %% get border style
-    \tl_set:Nx \l__tblr_dash_value_tl %% may be empty
+    \tl_set:Ne \l__tblr_dash_value_tl %% may be empty
       { \__tblr_spec_item:ne { #3 } { [#1][#2](1) / @dash } }
-    \tl_set:Nx \l__tblr_dash_value_head_tl { \tl_head:N \l__tblr_dash_value_tl }
-    \tl_set:Nx \l__tblr_dash_value_tail_tl { \tl_tail:N \l__tblr_dash_value_tl }
-    \exp_args:NV \tl_if_eq:NNTF \l__tblr_dash_value_head_tl \@tblr at dash
+    \tl_if_head_eq_meaning:VNTF \l__tblr_dash_value_tl \q__tblr_dash
       {
-        \tl_set_eq:cN { lTblrCell #4 BorderStyleTl } \l__tblr_dash_value_tail_tl
+        \tl_set:ce { lTblrCell #4 BorderStyleTl }
+          { \tl_tail:N \l__tblr_dash_value_tl }
         %% get border width
-        \tl_set:Nx \l__tblr_width_value_tl
+        \tl_set:Ne \l__tblr_width_value_tl
           { \__tblr_spec_item:ne { #3 } { [#1][#2](1) / wd } }
         \tl_if_empty:NTF \l__tblr_width_value_tl
-          { \tl_set:cn { lTblrCell #4 BorderWidthTl } { 0.4pt } }
-          { \tl_set_eq:cN { lTblrCell #4 BorderWidthTl } \l__tblr_width_value_tl }
+          { \dim_set:cn { lTblrCell #4 BorderWidthDim } { 0.4pt } }
+          {
+            \dim_set:cn { lTblrCell #4 BorderWidthDim }
+              { \l__tblr_width_value_tl }
+          }
         %% get border color
-        \tl_set:cx { lTblrCell #4 BorderColorTl }
+        \tl_set:ce { lTblrCell #4 BorderColorTl }
           { \__tblr_spec_item:ne { #3 } { [#1][#2](1) / fg } }
       }
       {
         \tl_clear:c { lTblrCell #4 BorderStyleTl }
-        \tl_set:cn { lTblrCell #4 BorderWidthTl } { 0pt }
+        \dim_set:cn { lTblrCell #4 BorderWidthDim } { 0pt }
         \tl_clear:c { lTblrCell #4 BorderColorTl }
       }
   }
@@ -6600,7 +7597,7 @@
   {
     \bool_if:NT \l__tblr_html_variables_bool
       { \__tblr_expose_cell_properties:NN #1 #2 }
-    \__tblr_hook_use:n { tabularray/cell/before }
+    \__tblr_hook_use:n { cell/before }
     \hbox_set_to_wd:Nnn \l__tblr_a_box { \l__tblr_cell_wd_dim }
       {
         \tl_if_eq:NnTF \g__tblr_cell_halign_tl {j}
@@ -6619,7 +7616,7 @@
             \c__tblr_valign_m_tl
               {
                 \vfil
-                \int_compare:nNnT { \lTblrCellRowSpanTl } < {2}
+                \int_compare:nNnT { \lTblrCellRowSpanInt } < {2}
                   {
                     \box_set_ht:Nn \l__tblr_a_box
                       { \__tblr_data_item:nen { row } {#1} { @row-upper } }
@@ -6639,7 +7636,7 @@
             \c__tblr_valign_f_tl
               {
                 \vfil
-                \int_compare:nNnTF { \lTblrCellRowSpanTl } < {2}
+                \int_compare:nNnTF { \lTblrCellRowSpanInt } < {2}
                   {
                     \box_set_dp:Nn \l__tblr_a_box
                       { \__tblr_data_item:nen { row } {#1} { @row-foot } }
@@ -6649,7 +7646,7 @@
                       {
                         \__tblr_data_item:nen
                           { row }
-                          { \int_eval:n { #1 + \lTblrCellRowSpanTl - 1 } }
+                          { \int_eval:n { #1 + \lTblrCellRowSpanInt - 1 } }
                           { @row-foot }
                       }
                   }
@@ -6667,7 +7664,7 @@
     \skip_horizontal:n { \l__tblr_x_tl }
     \box_use:N \l__tblr_c_box
     \skip_horizontal:n { \l__tblr_y_tl - \l__tblr_cell_wd_dim + \l__tblr_w_tl }
-    \__tblr_hook_use:n { tabularray/cell/after }
+    \__tblr_hook_use:n { cell/after }
   }
 
 \cs_new_protected:Npn \__tblr_build_cell_background:NN #1 #2
@@ -6679,7 +7676,7 @@
       }
     \bool_if:NF \lTblrCellOmittedBool
       {
-        \tl_set:Nx \lTblrCellBackgroundTl
+        \tl_set:Ne \lTblrCellBackgroundTl
           { \__tblr_data_item:neen { cell } {#1} {#2} { background } }
         \group_begin:
         \tl_if_empty:NF \lTblrCellBackgroundTl
@@ -6699,7 +7696,7 @@
 %% #1: row number; #2: column number; #3 resulting dimension
 \cs_new_protected:Npn \__tblr_get_cell_background_width:NNN #1 #2 #3
   {
-    \int_compare:nNnTF { \lTblrCellColSpanTl } < {2}
+    \int_compare:nNnTF { \lTblrCellColSpanInt } < {2}
       { \dim_set:Nn #3 { \l__tblr_x_tl + \l__tblr_w_tl + \l__tblr_y_tl } }
       {
         \dim_set:Nn #3 { \l__tblr_o_dim + \l__tblr_cell_wd_dim + \l__tblr_q_dim }
@@ -6709,7 +7706,7 @@
 %% #1: row number; #2: column number; #3 resulting dimension
 \cs_new_protected:Npn \__tblr_get_cell_background_depth:NNN #1 #2 #3
   {
-    \int_compare:nNnTF { \lTblrCellRowSpanTl } < {2}
+    \int_compare:nNnTF { \lTblrCellRowSpanInt } < {2}
       { \dim_set_eq:NN #3 \l__tblr_row_dp_dim }
       {
         \dim_set:Nn #3
@@ -6745,7 +7742,7 @@
     \dim_zero:N #4
     \dim_add:Nn #4
       { \prop_item:Ne \l__tblr_row_item_skip_size_prop { item[#1] } }
-    \int_step_inline:nnn { #1 + 1 } { #1 + \lTblrCellRowSpanTl - 1 }
+    \int_step_inline:nnn { #1 + 1 } { #1 + \lTblrCellRowSpanInt - 1 }
       {
         \dim_add:Nn #4
           {
@@ -6757,9 +7754,9 @@
     \dim_set:Nn #5
       {
         \__tblr_data_item:nen { row }
-          { \int_eval:n { #1 + \lTblrCellRowSpanTl - 1 } } { belowsep }
+          { \int_eval:n { #1 + \lTblrCellRowSpanInt - 1 } } { belowsep }
       }
-    %\tl_log:x { cell[#1][#2] ~:~ \dim_use:N #3, \dim_use:N #4, \dim_use:N #5 }
+    %\tl_log:e { cell[#1][#2] ~:~ \dim_use:N #3, \dim_use:N #4, \dim_use:N #5 }
   }
 
 %% #1: row number; #2: column number; #3: dimen register for colsep left.
@@ -6774,7 +7771,7 @@
     \dim_zero:N #4
     \dim_add:Nn #4
       { \prop_item:Ne \l__tblr_col_item_skip_size_prop { item[#2] } }
-    \int_step_inline:nnn { #2 + 1 } { #2 + \lTblrCellColSpanTl - 1 }
+    \int_step_inline:nnn { #2 + 1 } { #2 + \lTblrCellColSpanInt - 1 }
       {
         \dim_add:Nn #4
           {
@@ -6786,18 +7783,18 @@
     \dim_set:Nn #5
       {
         \__tblr_data_item:nen { column }
-          { \int_eval:n {#2 + \lTblrCellColSpanTl - 1} } { rightsep }
+          { \int_eval:n {#2 + \lTblrCellColSpanInt - 1} } { rightsep }
       }
-    %\tl_log:x { cell[#1][#2] ~:~ \dim_use:N #3, \dim_use:N #4, \dim_use:N #5 }
+    %\tl_log:e { cell[#1][#2] ~:~ \dim_use:N #3, \dim_use:N #4, \dim_use:N #5 }
   }
 
 %%% --------------------------------------------------------
-%%> \section{Tracing Tabularray}
+%%> \section{Tracing tabularray}
 %%% --------------------------------------------------------
 
 \NewDocumentCommand \SetTblrTracing { m }
   {
-    \keys_set:nn { tblr-set-tracing } {#1}
+    \__tblr_keys_set:nn { tracing/set } {#1}
   }
 
 \bool_new:N \g__tblr_tracing_text_bool
@@ -6819,9 +7816,7 @@
 \bool_new:N \g__tblr_tracing_page_bool
 \bool_new:N \g__tblr_tracing_step_bool
 
-\bool_gset_true:N \g__tblr_tracing_step_bool
-
-\keys_define:nn { tblr-set-tracing }
+\__tblr_keys_define:nn { tracing/set }
   {
     +text .code:n = \bool_gset_true:N \g__tblr_tracing_text_bool,
     -text .code:n = \bool_gset_false:N \g__tblr_tracing_text_bool,
@@ -6909,10 +7904,10 @@
 
 \NewDocumentCommand \LogTblrTracing { m }
   {
-    \keys_set:nn { tblr-log-tracing } {#1}
+    \__tblr_keys_set:nn { tracing/log } {#1}
   }
 
-\keys_define:nn { tblr-log-tracing }
+\__tblr_keys_define:nn { tracing/log }
   {
     step .code:n = \__tblr_log_tracing_step:n {#1},
     unknown .code:n = \__tblr_log_tracing:N \l_keys_key_str
@@ -6983,22 +7978,22 @@
 
 \cs_new_protected:Npn \__tblr_log_tracing_colspec:
   {
-    \tl_if_eq:NnT \g__tblr_column_or_row_tl { column }
+    \tl_if_eq:NnT \g__tblr_column_or_row_tl { Column }
       { \tl_log:N \g__tblr_expanded_colrow_spec_tl }
   }
 
 \cs_new_protected:Npn \__tblr_log_tracing_rowspec:
   {
-    \tl_if_eq:NnT \g__tblr_column_or_row_tl { row }
+    \tl_if_eq:NnT \g__tblr_column_or_row_tl { Row }
       { \tl_log:N \g__tblr_expanded_colrow_spec_tl }
   }
 
 \cs_new_protected:Npn \__tblr_log_tracing_target:
   {
-    \dim_log:N \l__column_target_dim
-    \prop_log:N \l__column_coefficient_prop
-    \prop_log:N \l__column_natural_width_prop
-    \prop_log:N \l__column_computed_width_prop
+    \dim_log:N \l__tblr_column_target_dim
+    \prop_log:N \l__tblr_column_coefficient_prop
+    \prop_log:N \l__tblr_column_natural_width_prop
+    \prop_log:N \l__tblr_column_computed_width_prop
   }
 
 \cs_new_protected:Npn \__tblr_log_tracing_cellspan:
@@ -7018,7 +8013,7 @@
 
 \cs_new_protected:Npn \__tblr_log_tracing_step:n #1
   {
-    \bool_if:NT \g__tblr_tracing_step_bool { \tl_log:x {Step :~ #1} }
+    \bool_if:NT \g__tblr_tracing_step_bool { \tl_log:e {Step :~ #1} }
   }
 
 \cs_new_protected:Npn \__tblr_do_if_tracing:nn #1 #2
@@ -7027,7 +8022,7 @@
   }
 
 %%% --------------------------------------------------------
-%%> \section{Tabularray Libraries}
+%%> \section{Tabularray libraries}
 %%% --------------------------------------------------------
 
 %% \NewTblrLibrary and \UseTblrLibrary commands
@@ -7037,15 +8032,28 @@
     \cs_new_protected:cpn { __tblr_use_lib_ #1: } {#2}
   }
 
+%% Note that \prg_do_nothing: is an existing command.
 \NewDocumentCommand \UseTblrLibrary { m }
   {
     \clist_map_inline:nn {#1}
       {
-        \use:c { __tblr_use_lib_ ##1: }
-        \cs_undefine:c { __tblr_use_lib_ ##1: }
+        \cs_if_exist:cTF { __tblr_use_lib_ ##1: }
+          {
+            \use:c { __tblr_use_lib_ ##1: }
+            \cs_gset_eq:cN { __tblr_use_lib_ ##1: } \prg_do_nothing:
+          }
+          {
+            \RequirePackage { tblrlib##1 }
+          }
       }
   }
 
+\prg_set_conditional:Npnn \__tblr_lib_if_used:n #1 { p, T, F, TF }
+  {
+    \tl_if_eq:cNTF { __tblr_use_lib_ #1: } \prg_do_nothing:
+      { \prg_return_true: } { \prg_return_false: }
+  }
+
 %% Library amsmath and environments +array, +matrix, +cases, ...
 
 \NewTblrLibrary { amsmath }
@@ -7132,23 +8140,23 @@
       \SetHline [+] {##2} {##1}
       \hborder { abovespace = \aboverulesep, belowspace = \belowrulesep }
     }
-    \NewTableCommand \toprule [1] [] {
+    \NewTblrTableCommand \toprule [1] [] {
       \tblr at booktabs@hline [wd=\heavyrulewidth, ##1]
     }
-    \NewTableCommand \midrule [1] [] {
+    \NewTblrTableCommand \midrule [1] [] {
       \tblr at booktabs@hline [wd=\lightrulewidth, ##1]
     }
-    \NewTableCommand \bottomrule [1] [] {
+    \NewTblrTableCommand \bottomrule [1] [] {
       \tblr at booktabs@hline [wd=\heavyrulewidth, ##1]
     }
-    \NewTableCommand \cmidrule [2] [] {
+    \NewTblrTableCommand \cmidrule [2] [] {
       \tblr at booktabs@cline [wd=\cmidrulewidth, endpos, ##1] {##2}
     }
-    \NewTableCommand \cmidrulemore [2] [] {
+    \NewTblrTableCommand \cmidrulemore [2] [] {
       \tblr at booktabs@cline at more [wd=\cmidrulewidth, endpos, ##1] {##2}
     }
     \newcommand \tblr at booktabs@change at more [1] { \cmidrulemore }
-    \NewTableCommand \morecmidrules {
+    \NewTblrTableCommand \morecmidrules {
       \peek_meaning:NTF \cmidrule { \tblr at booktabs@change at more } { \relax }
     }
     \NewTblrEnviron { booktabs }
@@ -7169,18 +8177,18 @@
     \AtBeginEnvironment { booktabs } { \tblr at booktabs@begin at hook }
     \AtBeginEnvironment { longtabs } { \tblr at booktabs@begin at hook }
     \AtBeginEnvironment { talltabs } { \tblr at booktabs@begin at hook }
-    \NewTableCommand \specialrule [3]
+    \NewTblrTableCommand \specialrule [3]
       { \hline [##1] \hborder { abovespace = ##2, belowspace = ##3 } }
-    \NewTableCommand \addrowspace [1] [\defaultaddspace]
+    \NewTblrTableCommand \addrowspace [1] [\defaultaddspace]
       { \hborder { abovespace+ = (##1) / 2, belowspace+ = (##1) / 2 } }
-    \NewTableCommand \addlinespace [1] [\defaultaddspace]
+    \NewTblrTableCommand \addlinespace [1] [\defaultaddspace]
       { \hborder { abovespace+ = (##1) / 2, belowspace+ = (##1) / 2 } }
   }
 
 %% Library counter for resetting all counters
 
-\tl_new:N \__tblr_saved_trial_counters_tl
-\tl_new:N \__tblr_saved_cell_counters_tl
+\tl_new:N \l__tblr_saved_trial_counters_tl
+\tl_new:N \l__tblr_saved_cell_counters_tl
 
 \cs_new_protected:Npn \__tblr_save_counters:n #1 { }
 \cs_new_protected:Npn \__tblr_restore_counters:n #1 { }
@@ -7194,12 +8202,12 @@
     \cs_set_protected:Npn \__tblr_save_counters:n ##1
       {
         \def \@elt ####1 { \global\value{####1} = \the\value{####1} \relax }
-        \tl_set:cx { __tblr_saved_ ##1 _counters_tl } { \cl@@ckpt }
+        \tl_set:ce { l__tblr_saved_ ##1 _counters_tl } { \cl@@ckpt }
         \let \@elt = \relax
       }
     \cs_set_protected:Npn \__tblr_restore_counters:n ##1
       {
-        \tl_use:c { __tblr_saved_ ##1 _counters_tl }
+        \tl_use:c { l__tblr_saved_ ##1 _counters_tl }
       }
   }
 
@@ -7209,7 +8217,7 @@
   {
     \RequirePackage{ diagbox }
     \cs_set_eq:NN \__tblr_lib_saved_diagbox:w \diagbox
-    \NewContentCommand \diagbox [3] []
+    \NewTblrContentCommand \diagbox [3] []
       {
         \__tblr_lib_diagbox_fix:n
           {
@@ -7219,7 +8227,7 @@
               { \__tblr_lib_diagbox_math_or_text:n {##3} }
           }
       }
-    \NewContentCommand \diagboxthree [4] []
+    \NewTblrContentCommand \diagboxthree [4] []
       {
         \__tblr_lib_diagbox_fix:n
           {
@@ -7255,12 +8263,12 @@
   {
     \RequirePackage { functional }
     %% Add outer specification "evaluate"
-    \keys_define:nn { tblr-outer }
+    \__tblr_keys_define:nn { table/outer }
       { evaluate .code:n = \__tblr_outer_gput_spec:nn { evaluate } {##1} }
     \tl_new:N \l__tblr_evaluate_tl
     \cs_set_protected:Npn \__tblr_hook_split_before:
       {
-        \tl_set:Nx \l__tblr_evaluate_tl
+        \tl_set:Ne \l__tblr_evaluate_tl
           { \__tblr_spec_item:nn { outer } { evaluate } }
         \tl_if_empty:NF \l__tblr_evaluate_tl
           {
@@ -7275,7 +8283,7 @@
           }
       }
     %% Evaluate every occurrence of the specified function
-    %% Note that funtional package runs every return processor inside a group
+    %% Note that functional package runs every return processor inside a group
     %% #1: tl with table content; #2: function to be evaluated
     \tl_new:N \g__tblr_functional_result_tl
     \cs_new_protected:Npn \__tblr_evaluate_table_body:NN ##1 ##2
@@ -7297,8 +8305,7 @@
         \tl_set_eq:NN ##1 \g__tblr_functional_result_tl
       }
     %% Add inner specification "process"
-    \clist_put_right:Nn \g__tblr_table_known_keys_clist { process }
-    \keys_define:nn { tblr }
+    \__tblr_keys_define:nn { table/inner }
       { process .code:n = \__tblr_keys_gput:nn { process } {##1} }
     \cs_set:Npn \__tblr_functional_calculation:
       {
@@ -7325,19 +8332,48 @@
       {
         \tblr_set_column:nnn {##1} {} {##2}
       }
+    %% Evaluate every function in inner specification
+    %% But we need to protect the value of "process" key
+    \clist_new:N \l__tblr_fun_keyvalue_clist
+    \cs_new_protected:Npn \__tblr_fun_i:n ##1
+      {
+        \clist_put_right:Nn \l__tblr_fun_keyvalue_clist { ##1 }
+      }
+    \cs_new_protected:Npn \__tblr_fun_ii:nn ##1 ##2
+      {
+        \tl_if_eq:nnTF { ##1 } { process }
+          {
+            \clist_put_right:Nn \l__tblr_fun_keyvalue_clist
+              { ##1 = {\evalNone{##2}} }
+          }
+          {
+            \clist_put_right:Nn \l__tblr_fun_keyvalue_clist
+              { ##1 = {##2} }
+          }
+      }
+    \cs_set_protected:Npn \__tblr_hook_parse_inner_spec_before:
+      {
+        \clist_clear:N \l__tblr_fun_keyvalue_clist
+        \keyval_parse:NNV \__tblr_fun_i:n \__tblr_fun_ii:nn \l__tblr_inner_spec_tl
+        \tlSet \l__tblr_inner_spec_tl
+          { \evalWhole { \expValue \l__tblr_fun_keyvalue_clist } }
+      }
   }
 
 %% Library hook provides some public hooks
 
-\cs_new_protected:Npn \__tblr_hook_use:n #1 {}
-
 \NewTblrLibrary { hook }
   {
-    \cs_set_eq:NN \__tblr_hook_use:n \hook_use:n
-    \hook_new_pair:nn { tabularray/trial/before } { tabularray/trial/after }
-    \hook_new_pair:nn { tabularray/table/before } { tabularray/table/after }
-    \hook_new_pair:nn { tabularray/row/before } { tabularray/row/after }
-    \hook_new_pair:nn { tabularray/cell/before } { tabularray/cell/after }
+    %% We need varwidth to keep \lTblrMeasuringBool correct
+    \UseTblrLibrary { varwidth }
+    \tl_set:Nn \l__tblr_inner_spec_measure_tl { vstore }
+    \cs_set_eq:NN \__tblr_hook_use:n \__tblr_hook_use_true:n
+    %% Be careful lthooks will not remove any spaces in hook paths
+    \__tblr_hook_new_pair:nn { trial/before } { trial/after }
+    \__tblr_hook_new_pair:nn { table/before } { table/after }
+    \__tblr_hook_new_pair:nn {   row/before } {   row/after }
+    \__tblr_hook_new_pair:nn {  cell/before } {  cell/after }
+    \__tblr_hook_new_pair:nn { private/output/before } { private/output/after }
   }
 
 %% Library html provides more public variables
@@ -7358,7 +8394,7 @@
     \clist_if_in:NnF \lTblrRefMoreClist { nameref }
       {
         \clist_put_right:Nn \lTblrRefMoreClist { nameref }
-        \DefTblrTemplate { caption-ref }{ nameref }
+        \DeclareTblrTemplate { caption-ref }{ nameref }
           {
             \tl_if_eq:NnTF \lTblrEntryTl { none }
               { \exp_args:NV \GetTitleString \lTblrCaptionTl }
@@ -7377,10 +8413,10 @@
 \NewTblrLibrary { siunitx }
   {
     \RequirePackage { siunitx }
-    \NewColumnType { S } [1] [] { Q[si = {##1}, c] }
-    \NewColumnType { s } [1] [] { Q[si = {##1}, c, cmd = \TblrUnit] }
+    \NewTblrColumnType { S } [1] [] { Q[si = {##1}, c] }
+    \NewTblrColumnType { s } [1] [] { Q[si = {##1}, c, cmd = \TblrUnit] }
     \__tblr_data_new_key:nnn { cell } { si } { str }
-    \keys_define:nn { tblr-column }
+    \__tblr_keys_define:nn { column/inner }
       {
         si .code:n = \__tblr_siunitx_setcolumn:n {##1}
       }
@@ -7403,7 +8439,7 @@
           { ##2 }
           {
             \group_begin:
-            \tl_set:Nx \l_tmpa_tl
+            \tl_set:Ne \l_tmpa_tl
               {
                 \__tblr_data_item:neen { cell }
                   { \int_use:N \c at rownum } { \int_use:N \c at colnum } { si }
@@ -7413,18 +8449,325 @@
             \group_end:
           }
       }
-    \keys_define:nn { tblr-cell-spec } { guard .meta:n = { cmd = } }
-    \keys_define:nn { tblr-row }       { guard .meta:n = { cmd = } }
-    \keys_define:nn { tblr-column }    { guard .meta:n = { cmd = } }
+    \__tblr_keys_define:nn { cell/inner }   { guard .meta:n = { cmd = } }
+    \__tblr_keys_define:nn { row/inner }    { guard .meta:n = { cmd = } }
+    \__tblr_keys_define:nn { column/inner } { guard .meta:n = { cmd = } }
   }
 
+%% Library tikz to add table nodes, cell nodes and corner nodes with tikz
+
+\tl_new:N \g__tblr_tikz_below_code_tl
+\tl_new:N \g__tblr_tikz_above_code_tl
+\dim_new:N \l__tblr_cell_inner_wd_dim
+\dim_new:N \l__tblr_cell_inner_dp_dim
+\dim_new:N \l__tblr_cell_hanchor_dim
+\dim_new:N \l__tblr_last_cell_table_ht_dim
+\dim_new:N \l__tblr_last_cell_table_dp_dim
+\dim_new:N \l__tblr_last_vline_wd_dim
+\box_new:N \l__tblr_tikz_node_box
+
+\NewTblrLibrary { tikz }
+  {
+    \UseTblrLibrary { hook }
+    \RequirePackage { tikz }
+    \usetikzlibrary { calc }
+    \tikzset
+      {
+        tblr / cell ~ node / .style =
+          {
+            rectangle ,
+            inner ~ sep = 0pt ,
+            outer ~ sep = 0pt ,
+            draw = none ,
+            fill = none
+          } ,
+        tblr / cell / .style = { } ,
+        tblr / table ~ node / .style =
+          {
+            rectangle ,
+            inner ~ sep = 0pt ,
+            outer ~ sep = 0pt ,
+            draw = none ,
+            fill = none
+          } ,
+        tblr / table / .style = { } ,
+        tblr / overlay / .style = { }
+      }
+    \NewDocumentEnvironment { tblrtikzbelow } { +b }
+      {
+        \tl_gset:Nn \g__tblr_tikz_below_code_tl
+          {
+            \begin{tikzpicture}
+              [
+                remember ~ picture ,
+                overlay ,
+                name ~ prefix = tblr \g__tblr_name_str - ,
+                tblr / overlay
+              ]
+              ##1
+            \end{tikzpicture}
+          }
+      } { }
+    \NewDocumentEnvironment { tblrtikzabove } { +b }
+      {
+        \tl_gset:Nn \g__tblr_tikz_above_code_tl
+          {
+            \begin{tikzpicture}
+              [
+                remember ~ picture ,
+                overlay ,
+                name ~ prefix = tblr \g__tblr_name_str - ,
+                tblr / overlay
+              ]
+              ##1
+            \end{tikzpicture}
+          }
+      } { }
+    \cs_new_protected:Npn \__tblr_tikz_make_cell_node:
+      {
+        \hbox_set:Nn \l__tblr_tikz_node_box
+          {
+            \begin{tikzpicture}[remember ~ picture, overlay]
+              \coordinate
+                (
+                  tblr \g__tblr_name_str -
+                  \int_use:N \c at rownum - \int_use:N \c at colnum -
+                  single
+                )
+                at ( 0pt , -\l__tblr_row_dp_dim );
+              \node
+                [
+                  tblr / cell ~ node ,
+                  tblr / cell ,
+                  anchor = center ,
+                  text ~ width = \l__tblr_cell_inner_wd_dim ,
+                  text ~ height = \l__tblr_row_ht_dim ,
+                  text ~ depth = \l__tblr_cell_inner_dp_dim
+                ]
+                (
+                  tblr \g__tblr_name_str -
+                  \int_use:N \c at rownum - \int_use:N \c at colnum
+                )
+                at
+                (
+                  \l__tblr_cell_hanchor_dim - 0.5 \l__tblr_cell_inner_wd_dim
+                  ,
+                  0.5 \l__tblr_row_ht_dim - 0.5 \l__tblr_cell_inner_dp_dim
+                ) { } ;
+            \end{tikzpicture}
+          }
+      }
+    \cs_new_protected:Npn \__tblr_tikz_make_table_node:
+      {
+        \hbox_set:Nn \l__tblr_tikz_node_box
+          {
+            \begin{tikzpicture}[remember ~ picture, overlay]
+              \node
+                [
+                  tblr / table ~ node ,
+                  tblr / table ,
+                  anchor = center ,
+                  text ~ width = \lTblrTableWidthDim ,
+                  text ~ height = \l__tblr_last_cell_table_ht_dim ,
+                  text ~ depth = \l__tblr_last_cell_table_dp_dim
+                ]
+                (
+                  tblr \g__tblr_name_str - table
+                )
+                at
+                (
+                  \l__tblr_last_vline_wd_dim - 0.5 \lTblrTableWidthDim
+                  ,
+                  0.5 \l__tblr_last_cell_table_ht_dim
+                  - 0.5 \l__tblr_last_cell_table_dp_dim
+                ) { } ;
+            \end{tikzpicture}
+          }
+      }
+    \cs_new_protected:Npn \__tblr_tikz_enable_cell_node:
+      {
+        \__tblr_get_cell_background_width:NNN
+            \c at rownum \c at colnum \l__tblr_cell_inner_wd_dim
+        \__tblr_get_cell_background_depth:NNN
+            \c at rownum \c at colnum\l__tblr_cell_inner_dp_dim
+        \dim_zero:N \l__tblr_cell_hanchor_dim
+        \tl_set:Ne \l__tblr_c_tl
+          {
+            \__tblr_data_item:neen { cell }
+              { \int_use:N \c at rownum } { \int_use:N \c at colnum } { colspan }
+          }
+        \int_compare:nNnT { \l__tblr_c_tl } > { 1 }
+          {
+            \int_step_inline:nn { \l__tblr_c_tl - 1 }
+              {
+                \dim_add:Nn \l__tblr_cell_hanchor_dim
+                  {
+                    \__tblr_spec_item:ne { vline }
+                      { [ \int_eval:n { \c at colnum + ####1 } ] / @vline-width }
+                    +
+                    \__tblr_data_item:nen { column }
+                      { \int_eval:n { \c at colnum + ####1 } } { @col-width }
+                    +
+                    \__tblr_data_item:nen { column }
+                      { \int_eval:n { \c at colnum + ####1 } } { leftsep }
+                    +
+                    \__tblr_data_item:nen { column }
+                      { \int_eval:n { \c at colnum + ####1 } } { rightsep }
+                  }
+              }
+          }
+        \__tblr_tikz_make_cell_node:
+        \box_use:N \l__tblr_tikz_node_box
+      }
+    \cs_new_protected:Npn \__tblr_tikz_enable_table_node:
+      {
+        \int_compare:nNnT { \c at rownum } = { \c at rowcount }
+          {
+            \int_compare:nNnT { \c at colnum } = { \c at colcount }
+              {
+                \__tblr_get_table_width:
+                \__tblr_get_table_height:
+                \__tblr_get_table_depth:
+                \dim_set:Nn \l__tblr_last_vline_wd_dim
+                  {
+                    \__tblr_spec_item:ne { vline }
+                      { [ \int_eval:n { \c at colcount + 1 } ] / @vline-width }
+                  }
+                \__tblr_tikz_make_table_node:
+                \box_use:N \l__tblr_tikz_node_box
+              }
+          }
+      }
+    \tl_new:N \l__tblr_tikz_corner_node_code_tl
+    \cs_new_protected:Npn \__tblr_tikz_make_corner_node:
+      {
+        \tl_set:Nn \l__tblr_tikz_corner_node_code_tl
+          { \coordinate (h1) at ($(table.north~west)!0.5!(1-1.north~west)$); }
+        \int_step_inline:nnn { 2 } { \c at rowcount }
+          {
+            \tl_put_right:Ne \l__tblr_tikz_corner_node_code_tl
+              {
+                \exp_not:N \coordinate (h####1) at
+                  (
+                    h1 |-
+                    {
+                      $(\int_eval:n{####1-1}-1-single)!0.5!(####1-1.north~west)$
+                    }
+                  );
+              }
+          }
+        \tl_put_right:Ne \l__tblr_tikz_corner_node_code_tl
+          {
+            \exp_not:N \coordinate (h\int_eval:n{\c at rowcount+1}) at
+              (h1|-{$(\int_use:N\c at rowcount-1-single)!0.5!(table.south~east)$});
+          }
+        \tl_put_right:Nn \l__tblr_tikz_corner_node_code_tl
+          { \coordinate (v1) at (h1); }
+        \int_step_inline:nnn { 2 } { \c at colcount }
+          {
+            \tl_put_right:Ne \l__tblr_tikz_corner_node_code_tl
+              {
+                \exp_not:N \coordinate (v####1) at
+                  (
+                    v1 -|
+                    {
+                      $(1-\int_eval:n{####1-1}-single)!0.5!(1-####1.north~west)$
+                    }
+                  );
+              }
+          }
+        \tl_put_right:Ne \l__tblr_tikz_corner_node_code_tl
+          {
+            \exp_not:N \coordinate (v\int_eval:n{\c at colcount+1}) at
+              (v1-|{$(1-\int_use:N\c at colcount-single)!0.5!(table.south~east)$});
+          }
+      }
+    \tl_const:Nn \c__tblr_portrait_long_tl { long }
+    \cs_new_protected:Npn \__tblr_tikz_enable_corner_node:
+      {
+        \bool_lazy_or:nnT
+          { ! \tl_if_eq_p:NN \lTblrPortraitTypeTl \c__tblr_portrait_long_tl }
+          {
+            \bool_lazy_and_p:nn
+              { \int_compare_p:nNn { \lTblrRowFirstInt } = {1} }
+              { \int_compare_p:nNn { \lTblrRowLastInt } = { \c at rowcount } }
+          }
+          {
+            \__tblr_tikz_make_corner_node:
+            \begin{tikzpicture}
+              [
+                remember ~ picture, overlay,
+                name ~ prefix = tblr \g__tblr_name_str -
+              ]
+              \tl_use:N \l__tblr_tikz_corner_node_code_tl
+            \end{tikzpicture}
+          }
+      }
+    \AddToTblrHook { cell/after } [ tblrlibtikz ]
+      {
+        \bool_lazy_and:nnF
+          { \tl_if_empty_p:N \g__tblr_tikz_below_code_tl }
+          { \tl_if_empty_p:N \g__tblr_tikz_above_code_tl }
+          {
+            \__tblr_tikz_enable_cell_node:
+            \__tblr_tikz_enable_table_node:
+          }
+      }
+    \AddToTblrHook { private/output/before } [ tblrlibtikz ]
+      {
+        \bool_lazy_and:nnF
+          { \tl_if_empty_p:N \g__tblr_tikz_below_code_tl }
+          { \tl_if_empty_p:N \g__tblr_tikz_above_code_tl }
+          {
+            \__tblr_tikz_enable_corner_node:
+            \noindent
+            \tl_use:N \g__tblr_tikz_below_code_tl
+          }
+      }
+    \AddToTblrHook { private/output/after } [ tblrlibtikz ]
+      {
+        \tl_use:N \g__tblr_tikz_above_code_tl
+      }
+    \AddToTblrHook { table/after } [ tblrlibtikz ]
+      {
+        %% We clear them here since they are used several times in longtblr
+        \tl_gclear:N \g__tblr_tikz_below_code_tl
+        \tl_gclear:N \g__tblr_tikz_above_code_tl
+      }
+  }
+
+\cs_new_protected:Npn \__tblr_get_table_height:
+  {
+    \dim_zero:N \l__tblr_last_cell_table_ht_dim
+    \int_step_inline:nn { \c at rowcount - 1 }
+      {
+        \dim_add:Nn \l__tblr_last_cell_table_ht_dim
+          { \__tblr_valign_get_hline_total:n {##1} }
+        \dim_add:Nn \l__tblr_last_cell_table_ht_dim
+          { \__tblr_valign_get_row_total:n {##1} }
+      }
+    \dim_add:Nn \l__tblr_last_cell_table_ht_dim
+      { \__tblr_valign_get_hline_total:n { \int_use:N \c at rowcount } }
+    \dim_add:Nn \l__tblr_last_cell_table_ht_dim
+      { \__tblr_valign_get_row_height:n { \int_use:N \c at rowcount } }
+  }
+
+\cs_new_protected:Npn \__tblr_get_table_depth:
+  {
+    \dim_set:Nn \l__tblr_last_cell_table_dp_dim
+      { \__tblr_valign_get_row_depth:n { \int_use:N \c at rowcount } }
+    \dim_add:Nn \l__tblr_last_cell_table_dp_dim
+      { \__tblr_valign_get_hline_total:n { \int_eval:n { \c at rowcount + 1 } } }
+  }
+
 %% Library varwidth and measure option
 
 \NewTblrLibrary { varwidth }
   {
     \RequirePackage { varwidth }
-    \clist_gput_left:Nn \g__tblr_table_known_keys_clist { measure }
-    \keys_define:nn { tblr } { measure .tl_set:N = \l__tblr_inner_spec_measure_tl }
+    \tl_set:Nn \l__tblr_inner_spec_measure_tl { vbox }
+    \__tblr_keys_define:nn { table/inner }
+      { measure .tl_set:N = \l__tblr_inner_spec_measure_tl }
   }
 
 %% Library zref and its caption-ref template
@@ -7435,10 +8778,9 @@
     \clist_if_in:NnF \lTblrRefMoreClist { zref }
       {
         \clist_put_right:Nn \lTblrRefMoreClist { zref }
-        \DefTblrTemplate { caption-ref }{ zref }
+        \DeclareTblrTemplate { caption-ref }{ zref }
           {
             \exp_args:NV \zlabel \lTblrLabelTl
           }
       }
   }
-



More information about the tex-live-commits mailing list.