Build a small immutable record describing one border line —
width, style, and colour. A brdr() value is the stroke you hand
to the preset() rules knob (one entry per rule name, e.g.
rules = list(midrule = brdr(width = 0.75))) or to style()'s
border arguments (style(border_top = brdr(...), .at = cells_table(side = "rows"))). Successive preset() calls layer
cleanly, so a one-off override composes onto a house-style template
without disturbing the other rules.
Arguments
- width
Stroke width.
<numeric(1) | character(1)>: default"thin"*. Either a numeric in points (>= 0) or one of the four named keywords ("hairline","thin","medium","thick"`).- style
Line style.
<character(1)>: default"solid"*. One of"solid","dashed","dotted","double","dashdot","none"`.- color
Stroke colour.
<character(1)>: default"currentColor"*. Hex ("#RRGGBB"), CSS colour name, or"currentColor"` to inherit the surrounding text colour.- x
Any R object — tested by
is_brdr()for membership in thetabular_brdrS3 class.
Value
A tabular_brdr S3 object — a length-3 named list
suitable for preset(rules = list(<rule> = .)) or style(border_* = .).
Details
Surface. A single tabular_brdr value is a length-3 named
list with class "tabular_brdr": list(style, width, color).
The shape is identical to the bare triple
style()'s per-side scalars accept, so the resolver in
R/borders.R can ingest either form transparently. Construct
with brdr(); test with is_brdr().
Width keywords. width accepts either a numeric in points
(typical clinical values: 0.25, 0.5, 1, 1.5) or one of the four
named keywords:
| keyword | points |
"hairline" | 0.25 |
"thin" | 0.5 |
"medium" | 1 |
"thick" | 1.5 |
Keywords resolve to numeric points immediately; the constructed
value carries a numeric width. Numeric inputs pass through
unchanged after a non-negative check.
Style enum. style is one of "solid" (default),
"dashed", "dotted", "double", "dashdot", "none".
"none" is the explicit clear-this-rule sentinel: setting a rule
to brdr(style = "none") (or the bare string "none") in
preset()(rules = list(...)) suppresses the baseline rule that
backend would otherwise draw.
Color. Hex ("#212529"), CSS colour name ("black",
"slategray"), the "ink" token (default; resolves to the
primary rule ink #212529, decoupled from the surrounding text
colour so a recoloured header keeps a neutral rule), or
"currentColor" (inherit the surrounding text colour per backend
convention — w:color="auto" in DOCX, the document text colour in
RTF, the CSS currentColor keyword in HTML).
See also
Where to attach: preset()'s rules knob (one brdr() per
rule name) and style()'s border_* arguments.
Per-cell predicates: style() accepts the same per-side
border_<side>_{style,width,color} triples without going through
brdr().
Resolver internals: tabular_classes (style_node's 12
border scalars).
Examples
# ---- Example 1: A house-style rule set ----
#
# The `rules` knob takes one brdr() value per rule name. Here a
# thick column-label divider (midrule), a hairline dotted rule
# between body rows (rowrule), and the muted spanner rule dropped.
# Unlisted rules keep their booktabs defaults.
demo_n <- stats::setNames(cdisc_saf_n$n, cdisc_saf_n$arm_short)
tabular(
cdisc_saf_ae,
titles = c(
"Table 14.3.1",
"Overall Summary of Adverse Events",
"Safety Population"
),
footnotes = "Subjects counted once per category."
) |>
cols(
stat_label = col_spec(label = "Category"),
placebo = col_spec(label = "Placebo\nN={demo_n['placebo']}"),
drug_50 = col_spec(label = "Drug 50\nN={demo_n['drug_50']}"),
drug_100 = col_spec(label = "Drug 100\nN={demo_n['drug_100']}"),
Total = col_spec(label = "Total\nN={demo_n['Total']}")
) |>
preset(
rules = list(
midrule = brdr(width = "thick"),
rowrule = brdr(width = "hairline", style = "dotted"),
spanrule = "none"
)
)
Table 14.3.1
Overall Summary of Adverse Events
Safety Population
Category Placebo
N=86 Drug 50
N=96 Drug 100
N=72 Total
N=254 Any TEAE 65 (75.6) 84 (87.5) 68 (94.4) 217 (85.4) Any Serious AE (SAE) 0 (0.0) 2 (2.1) 1 (1.4) 3 (1.2) Any AE Related to Study Drug 43 (50.0) 77 (80.2) 64 (88.9) 184 (72.4) Any AE Leading to Death 2 (2.3) 1 (1.0) 0 (0.0) 3 (1.2) Any AE Recovered / Resolved 47 (54.7) 61 (63.5) 49 (68.1) 157 (61.8) Maximum severity: Mild 36 (41.9) 21 (21.9) 20 (27.8) 77 (30.3) Maximum severity: Moderate 24 (27.9) 47 (49.0) 40 (55.6) 111 (43.7) Maximum severity: Severe 5 (5.8) 16 (16.7) 8 (11.1) 29 (11.4)
Subjects counted once per category.
# ---- Example 2: Wrap a custom style into a reusable function ----
#
# The recommended way to share a rule style across many tables is to
# wrap the `preset()` call in a small function. A later `preset()` /
# `style()` call layers a one-off override cleanly on top.
custom_style <- function(spec) {
spec |>
preset(
rules = list(
toprule = brdr(width = "thin", color = "#212529"),
midrule = brdr(width = "thin", color = "#212529"),
bottomrule = brdr(width = "thin", color = "#212529")
)
)
}
tabular(cdisc_saf_n) |>
custom_style() |>
preset(rules = list(rowrule = brdr("hairline", "dashed")))
arm arm_short n Placebo placebo 86 Xanomeline Low Dose drug_50 96 Xanomeline High Dose drug_100 72 Total Total 254
# ---- Example 3: Width keyword vs numeric, every style enum value ----
#
# Width accepts both the four named keywords and a bare numeric
# in points; style accepts six enum values. Use `is_brdr()` to
# confirm the constructor returned a valid `tabular_brdr` rather
# than a fallback list.
for (w in c("hairline", "thin", "medium", "thick")) {
cat(w, "=", brdr(width = w)$width, "pt\n")
}
#> hairline = 0.25 pt
#> thin = 0.5 pt
#> medium = 1 pt
#> thick = 1.5 pt
is_brdr(brdr(width = 0.75))
#> [1] TRUE
lapply(
c("solid", "dashed", "dotted", "double", "dashdot", "none"),
function(s) brdr(style = s)
)
#> [[1]]
#> <tabular_brdr> 0.5pt solid ink
#>
#> [[2]]
#> <tabular_brdr> 0.5pt dashed ink
#>
#> [[3]]
#> <tabular_brdr> 0.5pt dotted ink
#>
#> [[4]]
#> <tabular_brdr> 0.5pt double ink
#>
#> [[5]]
#> <tabular_brdr> 0.5pt dashdot ink
#>
#> [[6]]
#> <tabular_brdr> 0.5pt none ink
#>
# ---- Example 4: A full grid via the body-edge style() path ----
#
# The `rules` knob covers the named booktabs anatomy; for the body
# outer frame and inter-column separators, hand brdr() to
# `style(.at = cells_table(side = ...))`. Here a medium outer frame
# plus hairline column separators on a demographics table.
tabular(cdisc_saf_demo, titles = "Demographics with a full grid") |>
cols(
variable = col_spec(usage = "group", label = "Characteristic"),
stat_label = col_spec(label = "Statistic"),
placebo = col_spec(label = "Placebo", align = "decimal"),
drug_50 = col_spec(label = "Drug 50", align = "decimal"),
drug_100 = col_spec(label = "Drug 100", align = "decimal"),
Total = col_spec(label = "Total", align = "decimal")
) |>
style(border = brdr(width = "medium"), .at = cells_table(side = "outer")) |>
style(border_left = brdr("hairline"), .at = cells_table(side = "cols"))
Demographics with a full grid
Statistic Placebo Drug 50 Drug 100 Total Age (years) n 86 96 72 254 Mean (SD) 75.2 (8.59) 76.0 (8.11) 73.8 (7.94) 75.1 (8.25) Median 76.0 78.0 75.5 77.0 Q1, Q3 69.2, 81.8 71.0, 82.0 70.5, 79.0 70.0, 81.0 Min, Max 52 , 89 51 , 88 56 , 88 51 , 89 Sex, n (%) F 53 (61.6) 55 (57.3) 35 (48.6) 143 (56.3) M 33 (38.4) 41 (42.7) 37 (51.4) 111 (43.7) Race, n (%) WHITE 78 (90.7) 90 (93.8) 62 (86.1) 230 (90.6) BLACK OR AFRICAN AMERICAN 8 ( 9.3) 6 ( 6.2) 9 (12.5) 23 ( 9.1) ASIAN 0 0 0 0 AMERICAN INDIAN OR ALASKA NATIVE 0 0 1 ( 1.4) 1 ( 0.4)