Skip to contents

ksTFL logo

Overview

This guide covers the complete styling system in ksTFL:

  • Style primitives (s_* helpers) for fonts, paragraphs, spacing, indentation, tables, and borders

  • Declaring named styles with add_style()

  • Referencing and combining styles with style references and f_combine()

  • Applying styles to columns, labels, stubs, and content

  • Best practices for maintainable, reusable style systems

For runnable reporting examples integrating styles see Reporting Examples. For a quick start see Getting Started.

Category and scope

This is the reference vignette for readers building reusable style systems for teams or studies. The focus is style primitives, composition, references, and maintainable conventions.


Styling philosophy and workflow

Why declarative styles?

Clinical documents live or die on consistency: one font decision, applied everywhere. ksTFL uses a named style system — declare styles once with add_style(), reference them by id, compose with f_combine(), and let create_report() consolidate the combinations into final render-time styles. One definition change updates every reference, and complex looks (font + alignment + background) are assembled from small blocks rather than hand-written per table.

Best practices workflow

  1. Define atomic styles first: Create small, focused styles (bold headers, right-aligned text, light gray background)
  2. Reference by name: In define_cols(), add_title(), add_span_header() (with tidyselect support), use labelStyleRef or valueStyleRef to point to styles by id
  3. Combine when needed: Use f_combine() to merge multiple styles on-the-fly for ad-hoc combinations
  4. Let create_report() consolidate: The report builder automatically merges combined styles so the renderer receives clean, consolidated styles
  5. Never inspect internal fields: Don’t manually look at spec$attribs$styles — let the package manage consolidation

Style primitives (s_* helpers)

Style primitives begin with s_. s_font(), s_paragraph() and s_table_style() go directly inside add_style(); s_spacing(), s_indents(), s_borders() and s_border() nest one level deeper (inside s_paragraph()/s_table_style(), or inside s_borders()). Calling any of them outside its context aborts with a clear message. They validate parameter values (colors, enums, length units) and abort with the allowed set in the message. Template JSON keys, by contrast, are silently ignored when unknown — check spelling there yourself.

s_font() — Font properties

Control font appearance (name, size, weight, color, decorations).

Parameters:

  • font_name: Font family — one of the seven allowed names: “Arial”, “Times New Roman”, “Courier New”, “Georgia”, “Verdana”, “Trebuchet MS”, “Liberation Sans” (see Font Management)
  • font_size: Size with units (e.g., “12pt”, “11pt”)
  • bold: Logical (TRUE/FALSE)
  • italic: Logical (TRUE/FALSE)
  • underline: Logical (TRUE/FALSE)
  • strikethrough: Logical (TRUE/FALSE)
  • color: Color as hex code (e.g., “#000000”, “#FF0000”) or color name (e.g., “red”, “black”, “blue”)
  • highlight: Background highlight color as hex code or color name

Example:

spec <- create_table(mtcars)

# Bold, 12pt Arial, red text (using hex code)
spec <- add_style(spec, id = "header_bold_red",
  s_font(font_name = "Arial", font_size = "12pt", bold = TRUE, color = "#CC0000"))

# Underlined, 10pt monospace, blue text (using color name)
spec <- add_style(spec, id = "code_style",
  s_font(font_name = "Courier New", font_size = "10pt", underline = TRUE, color = "blue"))

# Yellow highlight with black text
spec <- add_style(spec, id = "highlighted",
  s_font(color = "black", highlight = "yellow"))

s_paragraph() — Paragraph properties

Control text alignment, spacing before/after, line spacing, and indentation.

Parameters:

  • alignment: Text alignment — “left”, “right”, “center”, “justify”, “distributed”
  • spacing: Spacing before/after (use s_spacing(...))
  • indents: Left/right indentation, first-line indent (use s_indents(...))
  • word_style: base Word style to inherit from (“Normal”, “Heading 1”, “Heading 2”, “Title”, “Subtitle”, “No Spacing”, “Strong”, “Quote”, “Intense Quote”)
  • borders: paragraph-level borders via s_borders(...) (see Paragraph-level borders below)

Example:

spec <- create_table(mtcars)

# Centered text with 6pt spacing before/after
spec <- add_style(spec, id = "centered_spaced",
  s_paragraph(alignment = "center", 
              spacing = s_spacing(before = "6pt", after = "6pt")))

# Right-aligned with left indent
spec <- add_style(spec, id = "right_indented",
  s_paragraph(alignment = "right",
              indents = s_indents(left = "10pt")))

s_spacing() — Line and paragraph spacing

Controls spacing before a paragraph, after a paragraph, and between lines.

Parameters:

  • before: Space before paragraph (e.g., “6pt”, “12pt”)

  • after: Space after paragraph (e.g., “6pt”, “12pt”)

  • line_spacing: Line spacing multiplier (e.g., 1.0, 1.5, 2.0 for single/1.5-line/double spacing)

Note: s_spacing() is always used inside s_paragraph(), never standalone.

Example:

spec <- create_table(mtcars)

# Double-spaced with 12pt spacing after each paragraph
spec <- add_style(spec, id = "double_spaced",
  s_paragraph(spacing = s_spacing(after = "12pt", line_spacing = 2.0)))

s_indents() — Indentation

Controls left/right margins and first-line indentation within a paragraph.

Parameters:

  • left: Left indent (e.g., “10pt”, “1cm”)

  • right: Right indent (e.g., “10pt”)

  • first_line: First-line indent (e.g., “20pt” for hanging indent)

Note: s_indents() is always used inside s_paragraph(), never standalone.

Example:

spec <- create_table(mtcars)

# Hanging indent (first line outdented, rest indented)
spec <- add_style(spec, id = "hanging_indent",
  s_paragraph(indents = s_indents(left = "20pt", first_line = "-20pt")))

# Left and right margins with left indent
spec <- add_style(spec, id = "block_indent",
  s_paragraph(indents = s_indents(left = "30pt", right = "30pt")))

s_table_style() — Table cell properties

Control cell background, row height, vertical alignment, text orientation, and borders.

Parameters:

  • background_color: Cell background color (hex, e.g., “#E8E8E8”)

  • row_height: Height of table row (e.g., “25pt”)

  • topEmptyLine: Optional empty spacer row after header (e.g., “6pt”, use NULL or 0pt to disable)

  • bottomEmptyLine: Optional empty spacer row before the bottom border (e.g., “6pt”, use NULL or 0pt to disable)

  • vertical_alignment: “top”, “center”, “bottom”

  • text_orientation: “horizontal”, “vertical_90”, “vertical_270”

  • borders: Border specification (use s_borders(...))

Example:

spec <- create_table(mtcars)

# Light gray background, centered vertically, fixed row height
spec <- add_style(spec, id = "header_cell",
  s_table_style(background_color = "#F2F2F2",
                row_height = "30pt",
                vertical_alignment = "center"))

# Vertically rotated text (90 degrees)
spec <- add_style(spec, id = "rotated_header",
  s_table_style(text_orientation = "vertical_90"))

# Add table-level spacer rows via set_document()
spec <- set_document(
  spec,
  topEmptyLine = "6pt",
  bottomEmptyLine = "6pt"
)

s_borders() — Border specifications

Define borders for all four sides of a cell. Each side takes s_border() with line style, width, and color.

Parameters (each side):

  • top: Top border (use s_border(...))

  • bottom: Bottom border (use s_border(...))

  • left: Left border (use s_border(...))

  • right: Right border (use s_border(...))

Note: s_borders() can be used inside s_table_style() for cell-level borders, or inside s_paragraph() for paragraph-level borders.

s_border() — Individual border line

Defines a single border line with style, width, and color.

Parameters:

  • color: Color as hex code (e.g., “#000000”) or color name (e.g., “black”, “red”)

  • width: Line width (e.g., “1pt”, “2pt”, “0.5pt”)

  • line_style: “single”, “double”, “dashed”, “dotted”, “thick”, “none”

Example:

spec <- create_table(mtcars)

# All borders: thin single lines in dark gray
spec <- add_style(spec, id = "all_borders",
  s_table_style(
    borders = s_borders(
      top = s_border(color = "grey40", width = "1pt", line_style = "single"),
      bottom = s_border(color = "grey40", width = "1pt", line_style = "single"),
      left = s_border(color = "grey40", width = "1pt", line_style = "single"),
      right = s_border(color = "grey40", width = "1pt", line_style = "single")
    )
  )
)

# Heavy bottom border in dark color (using hex code)
spec <- add_style(spec, id = "bottom_border_heavy",
  s_table_style(
    borders = s_borders(
      bottom = s_border(color = "#333333", width = "2pt", line_style = "thick")
    )
  )
)

Paragraph-level borders

MS Word distinguishes between cell borders (<w:tcBorders>) and paragraph borders (<w:pBdr>). Cell borders always span the full cell width, while paragraph borders follow the text within the cell. This is particularly useful for spanning headers where a cell border would stretch across all merged columns, but a paragraph border only underlines the header label.

Paragraph borders are set via s_borders() inside s_paragraph():

demo <- data.frame(
  trt_a = c(12, 15), trt_b = c(14, 13), age = c(45, 52), sex = c("M", "F"),
  stringsAsFactors = FALSE)
spec <- create_table(demo)

# Paragraph bottom border — underlines only the text, not the full cell
spec <- add_style(spec, id = "span_underline",
  s_font(bold = TRUE),
  s_paragraph(
    alignment = "center",
    borders = s_borders(
      bottom = s_border(color = "#000000", width = "0.5pt", line_style = "single")
    )
  )
)

# Apply to a spanning header
spec <- add_span_header(spec,
  cols = c("trt_a", "trt_b"),
  label = "Treatment Arms",
  stubOrder = 1,
  labelStyleRef = "span_underline"
)

# Combine paragraph border atom with other styles
# (disjoint column set, so it can share stubOrder = 1 with the band above)
spec <- add_span_header(spec,
  cols = c("age", "sex"),
  label = "Demographics",
  stubOrder = 1,
  labelStyleRef = f_combine("b", "ac", "pb_th")
)

Key difference: structural borders — template keys tableStyle.structural.header_top_border / header_bottom_border — replace the header row’s CELL borders outright and always win over labelStyleRef cell-border atoms. Paragraph borders (the pb/pb_th atoms, OOXML <w:pBdr>) live in the paragraph itself and are untouched by that override, which is why the spanning-header underline trick above survives templates with bold header rules.


Declaring named styles with add_style()

Named styles are the foundation of the ksTFL styling system. Each style has a unique id and contains one or more style primitives.

Basic syntax

spec <- create_table(mtcars)

spec <- add_style(spec, id = "style_name",
  s_font(bold = TRUE),
  s_paragraph(alignment = "center"),
  s_table_style(background_color = "#EFEFEF")
)

Parameters:

  • spec: A TFL_spec object

  • id: Unique name for the style (e.g., “header_bold”, “numeric_right”)

  • ...: One or more style primitives (s_font(), s_paragraph(), s_table_style(), etc.)

Example: Define a complete style

spec <- create_table(mtcars)

# Comprehensive header style: bold white text on gray background, centered
spec <- add_style(spec, id = "table_header",
  s_font(font_name = "Arial", font_size = "12pt", bold = TRUE, color = "#FFFFFF"),
  s_paragraph(alignment = "center", spacing = s_spacing(before = "6pt", after = "6pt")),
  s_table_style(background_color = "#333333", row_height = "30pt", vertical_alignment = "center")
)

# Simple numeric style: right-aligned
spec <- add_style(spec, id = "numeric_right",
  s_paragraph(alignment = "right")
)

# ID/key style: bold
spec <- add_style(spec, id = "id_bold",
  s_font(bold = TRUE)
)

Multiple calls merge with last-win strategy

Calling add_style() multiple times with the same id merges the styles: a later call overrides only the properties it sets — everything else is preserved. For conflict resolution inside f_combine(), see below.

spec <- create_table(mtcars)

# First call: defines font
spec <- add_style(spec, id = "emphasis", s_font(bold = TRUE))

# Second call: adds paragraph alignment; bold is preserved
spec <- add_style(spec, id = "emphasis", s_paragraph(alignment = "center"))

# Result: "emphasis" has both bold font AND center alignment

Referencing and applying styles

Once you declare styles with add_style(), reference them by id in various places:

Column labels — labelStyleRef in define_cols()

Apply styles to column header labels:

spec <- create_table(mtcars)

spec <- add_style(spec, id = "header_bold", s_font(bold = TRUE, font_size = "12pt"))
spec <- add_style(spec, id = "header_italic", s_font(italic = TRUE))

# Apply to single column
spec <- define_cols(spec, mpg, label = "MPG (miles/gallon)", labelStyleRef = "header_bold")

# Apply to multiple columns with recycling
spec <- define_cols(spec, c(hp, cyl), label = c("HP", "Cylinders"), labelStyleRef = "header_bold")

# Different styles for different columns
spec <- define_cols(spec, c(mpg, hp),
  label = c("MPG", "HP"),
  labelStyleRef = c("header_bold", "header_italic"))

Column values — valueStyleRef in define_cols()

Apply styles to data values in a column:

spec <- create_table(mtcars)

spec <- add_style(spec, id = "numeric_right", s_paragraph(alignment = "right"))

# Right-align all numeric values in the mpg column
spec <- define_cols(spec, mpg, type = "numeric", valueStyleRef = "numeric_right")

Spanning headers — labelStyleRef in add_span_header()

Apply styles to stub (spanning header) labels:

spec <- create_table(mtcars)

spec <- add_style(spec, id = "stub_label",
  s_font(bold = TRUE, font_size = "11pt"),
  s_table_style(background_color = "#EFEFEF"))

spec <- add_span_header(spec, cols = c("mpg", "cyl"), label = "Engine",
  labelStyleRef = "stub_label")

Content — styleRef in add_title(), add_footnote()

Apply styles to titles, subtitles, footnotes:

spec <- create_table(mtcars)

spec <- add_style(spec, id = "title_style",
  s_font(bold = TRUE, font_size = "14pt"),
  s_paragraph(alignment = "center"))

spec <- add_title(spec, "Motor Trends Analysis", styleRef = "title_style")

Combining styles with f_combine()

f_combine() lets you apply multiple styles to a single element without pre-defining a combined style. When two combined styles set the same property, the LAST argument wins — so f_combine("b", "nrm") is not bold, and f_combine("nrm", "b") is. Argument order is semantic.

Basic usage

spec <- create_table(mtcars)

spec <- add_style(spec, id = "bold", s_font(bold = TRUE))
spec <- add_style(spec, id = "red", s_font(color = "red"))  # Using color name
spec <- add_style(spec, id = "centered", s_paragraph(alignment = "center"))

# Apply bold + red + centered to a column header
spec <- define_cols(spec, mpg, label = "MPG",
  labelStyleRef = f_combine("bold", "red", "centered"))

When to use f_combine() vs named styles

Use Case Approach
Reusable style (used in 3+ places) Define a named style with add_style()
One-off combination (used once or twice) Use f_combine() inline
Complex style (many properties) Define named style, then optionally combine with others
Per-column variations Use f_combine() with per-column vectors

Combining with per-column mapping

Apply different combinations to different columns:

spec <- create_table(mtcars)

# Define base styles
spec <- add_style(spec, id = "bold", s_font(bold = TRUE))
spec <- add_style(spec, id = "italic", s_font(italic = TRUE))
spec <- add_style(spec, id = "centered", s_paragraph(alignment = "center"))
spec <- add_style(spec, id = "right", s_paragraph(alignment = "right"))

# Apply different combinations per column
spec <- define_cols(spec, c(mpg, cyl, hp),
  label = c("MPG", "Cylinders", "HP"),
  labelStyleRef = c(
    f_combine("bold", "centered"),      # mpg: bold + centered
    f_combine("italic", "right"),       # cyl: italic + right
    f_combine("bold", "italic", "centered")  # hp: bold + italic + centered
  )
)

Allowed values (enumerations)

Length units by parameter family

Parameter family Accepted units
Font size (s_font(font_size = ...)) pt only
Border width (s_border(width = ...)) pt only
Paragraph spacing (s_spacing) pt, cm, in, mm
Indents (s_indents), margins (p_margins) in, cm, mm, pt
Row height (row_height) pt, in, cm, mm, or "auto"
Spacer rows (topEmptyLine/bottomEmptyLine) pt, in, cm, mm
Column widths (colWidth), content width (contentWidth) %, in, cm
Figure sizes (figureWidth/figureHeight) %, in, cm, mm, pt

Color names

Color parameters accept both hex codes (e.g., "#FF0000") and named colors:

Basic colors: black, white, red, green, blue, yellow, orange, purple, pink, brown, gray/grey (medium, #808080), cyan, magenta, navy, teal, lightblue, lightgreen, lightred, lime, maroon, olive, silver, gold, coral, salmon, turquoise, violet, indigo, khaki, lavender, plum, tan

Grayscale: grey10 through grey90 in steps of 10 (or gray with ‘a’)

Font names (common)

  • Arial
  • Courier New
  • Times New Roman
  • Georgia
  • Verdana
  • Trebuchet MS
  • Liberation Sans

These are the exact names the font resolver accepts; which of them are physically available on your machine is shown by tfl_font_status() (missing faces fall back — see the Font Management vignette).

Alignment

  • “left”
  • “right”
  • “center”
  • “justify”
  • “distributed”

Border line styles

  • “single” (solid line)
  • “double” (two lines)
  • “dashed” (dashed line)
  • “dotted” (dotted line)
  • “thick” (thicker solid line)
  • “none” (no border)

Vertical alignment

  • “top”
  • “center”
  • “bottom”

Text orientation

  • “horizontal” (default)
  • “vertical_90” (rotated 90° counter-clockwise)
  • “vertical_270” (rotated 270° counter-clockwise)

Common styling patterns

Pattern 1: Clinical table headers

demo <- data.frame(
  PARAM = c("Systolic BP", "Diastolic BP", "Pulse Rate"),
  N = c(96, 96, 95), MEAN = c(128.4, 78.1, 72.6),
  stringsAsFactors = FALSE)

spec <- create_table(demo)

spec <- add_style(spec, id = "clinical_header",
  s_font(font_name = "Arial", font_size = "11pt", bold = TRUE, color = "#FFFFFF"),
  s_paragraph(alignment = "center"),
  s_table_style(background_color = "#003366", row_height = "28pt", vertical_alignment = "center")
)

spec <- define_cols(spec, c(PARAM, N, MEAN), labelStyleRef = "clinical_header")

Pattern 2: Right-aligned numeric columns

demo2 <- data.frame(age = 45, weight = 70.5, dose = 250)
spec <- create_table(demo2)

spec <- add_style(spec, id = "numeric_format",
  s_font(font_name = "Courier New", font_size = "10pt"),
  s_paragraph(alignment = "right")
)

spec <- define_cols(spec, c(age, weight, dose), type = "numeric", valueStyleRef = "numeric_format")

Pattern 3: ID/key columns (bold, wide)

demo3 <- data.frame(subject_id = c("S001", "S002"), age = c(45, 52))
spec <- create_table(demo3)

spec <- add_style(spec, id = "id_column",
  s_font(bold = TRUE, font_size = "11pt"),
  s_paragraph(alignment = "left")
)

spec <- define_cols(spec, subject_id, 
  label = "Subject ID",
  labelStyleRef = "id_column",
  colWidth = "15%",
  isID = TRUE)

Pattern 4: Multi-level headers with styled stubs

demo4 <- data.frame(var1 = 1, var2 = 2, var3 = 3)
spec <- create_table(demo4)

spec <- add_style(spec, id = "level1_stub",
  s_font(bold = TRUE, font_size = "12pt", color = "#FFFFFF"),
  s_table_style(background_color = "#666666", vertical_alignment = "center"))

spec <- add_style(spec, id = "level2_stub",
  s_font(bold = TRUE, font_size = "11pt"),
  s_table_style(background_color = "#CCCCCC", vertical_alignment = "center"))

# The umbrella band gets the HIGHER stubOrder -> renders above the sub-group
# band (stacking order decides, not column width)
spec <- add_span_header(spec, cols = c("var1", "var2"), label = "Safety",
  stubOrder = 1, labelStyleRef = "level2_stub")

spec <- add_span_header(spec, cols = c("var1", "var2", "var3"), label = "Baseline",
  stubOrder = 2, labelStyleRef = "level1_stub")

Troubleshooting

Error: “Context error” or “can only be used inside add_style()”

Problem: You used an s_* helper outside add_style().

# WRONG — s_font() outside add_style() aborts with
# "`s_font()` can only be used inside `add_style()`":
# my_style <- s_font(bold = TRUE)

# CORRECT
spec <- create_table(mtcars)
spec <- add_style(spec, id = "my_style", s_font(bold = TRUE))

Error: “Invalid parameter” or “Allowed values are…”

Problem: You used an invalid parameter value (e.g., “bolded” instead of TRUE).

# WRONG — invalid parameter values abort with the allowed set in the
# message (e.g. bold must be TRUE/FALSE):
# spec <- add_style(spec, id = "bad", s_font(bold = "bolded"))

# CORRECT
spec <- create_table(mtcars)
spec <- add_style(spec, id = "good", s_font(bold = TRUE))

Styles not applied after create_report()

Problem: If you used f_combine(), you must call create_report() before the styles are consolidated.

# Style ids referenced via f_combine() MUST exist in the spec; otherwise
# create_report() aborts with "Referenced styles not found in spec".
spec <- create_table(mtcars) |>
  add_style(id = "bold", s_font(bold = TRUE)) |>
  add_style(id = "red", s_font(color = "red")) |>
  define_cols(mpg, labelStyleRef = f_combine("bold", "red"))

# create_report() consolidates "bold" + "red" into one resolved style
report <- create_report(spec)
write_doc(report, name = "out", outDir = "./output", metaPath = tempdir())

Styles looking different in renderer than expected

Problem: the same property is set at several layers and a higher layer wins.

Solution: check the cascade — for one property, the row action (c_style) beats your styleRef, which beats the template row defaults, which beat the region style. Structural header borders always win over cell-border atoms on header rows; switch to the paragraph atoms (pb, pb_th) instead. When a property still will not move, save_report() the spec and inspect the resolved style JSON.


Advanced notes

Style consolidation in create_report()

When you call create_report(), the package:

1. Collects all specs

2. For each spec, finds all style references used with f_combine()

3. Merges those combined styles into single consolidated styles

4. Generates unique hash-based names for consolidated styles

5. Updates all references to point to the consolidated style

You don’t need to inspect or manipulate spec$attribs$styles — the consolidation is automatic and transparent.

Style reference resolution

Every style name — whether you passed an id to add_style() or an atom like "b", "tw_80", "grp_hdr" — resolves against the spec’s single style table, which starts pre-populated with the built-in atoms. Defining add_style() with an atom’s name shadows the built-in. An unresolved name, user style or atom alike, aborts create_report() with “Referenced styles not found in spec” and lists the missing ids.


Built-in style atoms

ksTFL ships a library of single-property style atoms accessible via f_combine() or directly as styleRef values. Each atom sets exactly one visual property; compose them freely with f_combine().

Discovering atoms programmatically: Use tfl_print_style_atoms() (or its alias tfl_style_atoms_catalog()) to print all available atoms grouped by category with colour-coded output in the console:

# Print all built-in atoms categorised and colour-coded
tfl_print_style_atoms()

Complete atom reference

Atom Effect
Font — decoration
b / font_bold Bold
i / font_italic Italic
u / font_underline Underline
Font — family
font_arial Set font_name = "Arial"
font_courier_new Set font_name = "Courier New"
font_times_new_roman Set font_name = "Times New Roman"
font_georgia Set font_name = "Georgia"
font_verdana Set font_name = "Verdana"
font_trebuchet_ms Set font_name = "Trebuchet MS"
Font — size
fs_7 … fs_12 Font size 7 pt … 12 pt
Font — colour
fc_black, fc_red, fc_blue, fc_green Pure text colours
fc_gray / fc_grey Secondary / reference text (#595959)
fc_navy, fc_teal, fc_olive, fc_rust, fc_plum, fc_slate Muted clinical palette
Text highlight (cell shading)
hl_yellow, hl_red, hl_green, hl_gray / hl_grey Strong highlight colours
hl_peach, hl_mint, hl_sky, hl_lemon, hl_lilac Pastel highlight palette
Paragraph — alignment
al / text_left Left-align
ar / text_right Right-align
ac / text_center Center-align
Paragraph — left indentation
ind0 / indent_0 No indent (reset to left margin)
ind1 / indent_1 0.5 cm left indent (top-level category)
ind2 / indent_2 1.0 cm left indent (first sub-group)
ind3 / indent_3 1.5 cm left indent (second sub-group)
ind4 / indent_4 2.0 cm left indent (detail)
Paragraph — right indentation
rind0 / rindent_0 No right indent (reset to right margin)
rind1 / rindent_1 0.5 cm right indent
rind2 / rindent_2 1.0 cm right indent
rind3 / rindent_3 1.5 cm right indent
rind4 / rindent_4 2.0 cm right indent
Paragraph — table-width shrink
tw_95 … tw_50 Symmetric left+right indent to match table at 95 %…50 % width (5 % steps)
Paragraph — spacing
sp_0 No space before/after paragraph
sp_2 2 pt space before and after
sp_4 4 pt space before and after
Paragraph — pagination
kl Keep all lines of a cell on the same page
kn Keep this row on the same page as the next row
Group / category header composites
grp_hdr Bold + 4 pt space above + left indent reset (category header)
grp_hdr_i Bold + italic + 4 pt space above + left indent reset
Cell — vertical alignment
va_t / va_top Top
va_m / va_center Middle
va_b / va_bottom Bottom
Cell — text orientation
to_h / text_horizontal Horizontal (default)
to_90 / text_vertical_90 Rotated 90° (bottom-to-top)
to_270 / text_vertical_270 Rotated 270° (top-to-bottom)
Cell — background colour
bg_blue, bg_gray / bg_grey Standard backgrounds
bg_peach, bg_mint, bg_sky, bg_lemon, bg_lilac Pastel backgrounds
bg_navy, bg_slate, bg_steel Dark/medium header backgrounds
Row height
row_h2, row_h4, row_h6 Row height 2 / 4 / 6 pt (separator rows)
Border — sides (1 pt black)
bt, bb, bl, br Top / bottom / left / right border
Border — thin sides (0.5 pt black)
bt_th, bb_th Thin top / bottom border
Border — colour override
bc_gray / bc_grey All sides → medium gray (#AAAAAA)
bc_white All sides → white / none (suppress borders)
Border — thick white sides (column separation)
brw_thick Right border 4 pt white (visual column gap)
blw_thick Left border 4 pt white (visual column gap)
Paragraph border — bottom
pb Paragraph bottom border 1 pt black
pb_th Paragraph bottom border 0.5 pt black (thin)

Usage:

demo5 <- data.frame(group = c("A", "A", "B"), value = c(1.5, 2.7, 3.1),
                    stringsAsFactors = FALSE)
spec <- create_table(demo5)

# Single atom as styleRef
spec <- add_footnote(spec, "Source: database.", styleRef = "fs_8")

# Combine atoms with f_combine()
spec <- define_cols(spec, value,
  valueStyleRef = f_combine("font_verdana", "ar", "fs_9"))

# Combine atoms with named styles
spec <- add_style(spec, id = "group_hdr",
  s_font(bold = TRUE),
  s_paragraph(spacing = s_spacing(before = "4pt")))
spec <- compute_cols(spec, firstOf(group),
  c_style(everything(), styleRef = f_combine("group_hdr", "bg_gray")))

Table-width shrink atoms (tw_*)

When a table is narrower than the full content width, titles, subtitles, footnotes, and body text will span the full page width by default — wider than the table itself. The tw_* atoms apply symmetric left and right paragraph indentation so that text blocks visually align with the table edges.

Indent values are calculated for A4 landscape with 0.5 in left/right page margins (content width ≈ 27.16 cm). Each 5 % step corresponds to 0.68 cm per side.

Atom Table width Indent each side
tw_95 95 % 0.68 cm
tw_90 90 % 1.36 cm
tw_85 85 % 2.04 cm
tw_80 80 % 2.72 cm
tw_75 75 % 3.40 cm
tw_70 70 % 4.07 cm
tw_65 65 % 4.75 cm
tw_60 60 % 5.43 cm
tw_55 55 % 6.11 cm
tw_50 50 % 6.79 cm

Usage — match footnotes and titles to a narrower table:

spec <- create_table(mtcars) |>
  set_document(contentWidth = "80%") |>
  add_title("Demographics Table",   styleRef = "tw_80") |>
  add_subtitle("Safety Population", styleRef = "tw_80") |>
  add_footnote("Source: study database.", styleRef = "tw_80")

Combine with other atoms using f_combine():

spec <- create_table(mtcars)
# Bold title, indented to match a 75 % table
spec <- add_title(spec, "Efficacy Summary",
                  styleRef = f_combine("b", "tw_75"))

# Small italic footnote, indented to match a 70 % table
spec <- add_footnote(spec, "Values are least-squares means.",
                     styleRef = f_combine("i", "fs_8", "tw_70"))

Apply via a named style for reuse across multiple specs:

spec1 <- create_table(mtcars)
spec2 <- create_table(iris)
# Define the style on each spec (or use a helper function to apply it)
add_footnote_80 <- function(spec, text) {
  spec <- add_style(spec, id = "fn_80",
    s_font(font_size = "8pt", italic = TRUE),
    s_paragraph(alignment = "left",
                indents = s_indents(left = "2.72cm", right = "2.72cm")))
  add_footnote(spec, text, styleRef = "fn_80")
}

spec1 <- add_footnote_80(spec1, "a. p < 0.05")
spec2 <- add_footnote_80(spec2, "b. LOCF imputation")

Note: The indent values assume the default A4 landscape page with 0.5 in margins. If you use a different page size, orientation, or margins via set_page_style(), calculate your own indents with s_indents(left = ..., right = ...) inside add_style().


See also