This guide covers column width management in ksTFL: the LOCKED/UNLOCKED/VISIBLE model, automatic recalculation, invisible-column rules, and the tradeoffs involved in making tables fit cleanly on the page.
ksTFL partitions table columns into four categories that determine how widths are calculated:
isVisible != FALSE)
colWidth)
colWidth set)
isVisible = FALSE)
compute_cols())When you create a table, ksTFL automatically:
library(ksTFL)
# Create sample data
data <- data.frame(
id = 1:100,
patient_id = sprintf("PAT-%04d", 1:100),
age = round(rnorm(100, 45, 10)),
weight_kg = round(rnorm(100, 70, 15), 1),
treatment_group = sample(c("Placebo", "Treatment A", "Treatment B"), 100, replace = TRUE)
)
# Initial spec with auto-detected widths
spec <- create_table(data)
print(spec) # Shows auto-calculated widths for all columnsautoColWidth OptionThe autoColWidth option (default: TRUE)
controls whether widths are automatically recalculated when you lock
columns:
When you set colWidth for a column, it becomes
LOCKED:
You can lock multiple columns; unlocked columns fill the remaining space:
spec <- create_table(data) |>
define_cols(id, colWidth = "10%") |> # Lock at 10%
define_cols(patient_id, colWidth = "20%") |> # Lock at 20%
define_cols(treatment_group, colWidth = "25%") # Lock at 25%
# Result:
# - id: 10% (LOCKED)
# - patient_id: 20% (LOCKED)
# - treatment_group: 25% (LOCKED)
# - age, weight_kg: share remaining 45% proportionallyYou can mix percentage widths with absolute units:
spec <- create_table(data) |>
define_cols(id, colWidth = "2.5cm") |> # Fixed width (doesn't reduce % space)
define_cols(patient_id, colWidth = "20%") # Takes 20% of available
# Result:
# - id: 2.5cm (LOCKED, absolute)
# - patient_id: 20% (LOCKED, relative)
# - Other columns: share remaining 80% proportionallyImportant: Fixed-unit columns (cm, in, mm, pt) don’t reduce the available percentage space—only locked percentage columns do.
When autoColWidth = TRUE and you lock a column,
ksTFL:
# Initial auto-distribution (example values):
# id: 15%, patient_id: 25%, age: 20%, weight_kg: 20%, treatment_group: 20%
spec <- create_table(data) |>
define_cols(id, colWidth = "10%")
# After locking id at 10%:
# - Available space: 100% - 10% = 90%
# - Unlocked weights: patient_id=25, age=20, weight_kg=20, treatment_group=20 (sum=85)
# - Normalized: patient_id=26.5%, age=21.2%, weight_kg=21.2%, treatment_group=21.2%
# - Result sums to 100.1% (rounding), drift corrected to largest columnTo ensure widths sum exactly to 100%, ksTFL applies drift correction:
This guarantees valid output while maintaining proportions.
Use isVisible = FALSE to hide columns from output:
You cannot set colWidth for invisible
columns:
# This will error:
spec <- create_table(data) |>
define_cols(id, isVisible = FALSE, colWidth = "15%")
# Error message:
# "Cannot set colWidth for invisible column 'id'"Why? Invisible columns are always “0.0cm”—setting a width would be contradictory.
Invisible columns are perfect for conditional formatting:
For complete manual control:
# Disable auto-recalculation
tfl_set_options(autoColWidth = FALSE)
# Set exact widths - no automatic adjustment
spec <- create_table(data) |>
define_cols(
c(id, patient_id, age, weight_kg, treatment_group),
colWidth = c("10%", "25%", "20%", "20%", "25%")
)
# Widths stay exactly as specified (sum = 100%)
# Re-enable for other tables
tfl_set_options(autoColWidth = TRUE)ksTFL enforces minimum widths to prevent unreadable columns:
Relative widths (%): - Minimum: 0.5%
Absolute widths (cm, in, mm, pt): - Minimum: 0.2cm (~0.08in)
ksTFL prevents you from locking widths that leave insufficient space for other columns:
# 5 columns with minColWidth = 0.5% (default)
# Minimum space needed for 4 unlocked columns: 4 × 0.5% = 2%
# This will error:
spec <- create_table(data) |>
define_cols(id, colWidth = "99%") # Leaves only 1% for 4 columns
# Error: "Cannot set column 'id' to '99%'"
# "This would leave insufficient space for the remaining 4 unlocked columns"
# "Maximum allowed relative width for id: 98.0%"You can customize the minimum width threshold:
Cause: Locked columns leave <
minColWidth % per unlocked column
Solutions: 1. Reduce the locked width you’re trying
to set 2. Lower minColWidth via
tfl_set_options(minColWidth = 0.3) 3. Lock more columns
explicitly to reduce unlocked count 4. Make some columns invisible to
exclude them
Cause: Rounding errors from drift correction
Solution: This is expected and handled automatically. Drift is always ≤ 0.1% and applied to the largest column. The rendered output will be correct.
Cause: Trying to use colWidth with
isVisible = FALSE
Solution: Remove colWidth
parameter—invisible columns are always “0.0cm”
Cause: Auto-recalculation triggered by locking a column
Solution: - This is expected behavior when
autoColWidth = TRUE - Disable with
tfl_set_options(autoColWidth = FALSE) for manual control -
Or lock all columns explicitly
Note:
spec$.metadatais an internal field. Its structure may change between package versions. Useprint(spec)anddefine_cols()for all user-facing width inspection and control.
ksTFL stores width metadata internally in
spec$.metadata$colWidths. This is used by the package
itself to: - Recalculate widths without re-parsing width strings - Track
which columns are locked vs. unlocked - Preserve initial proportions for
normalization
You do not need to read or write this field directly. Use
print(spec) to inspect current widths and
define_cols(spec, col, colWidth = ...) to modify them.
define_cols() callFor more examples, see:
compute_cols() for conditional
logic