Skip to content
Back to skills

2549 Inline Markup 1ed5cb03

BSecurity

1. [Introduction](#introduction) 2. [Basic Syntax](#basic-syntax) 3. [Code & Syntax Classes](#code--syntax-classes) 4. [Files & Paths Classes](#files--paths-classes) 5. [Communication Classes](#communication-classes) 6. [Values & Data Classes](#values--data-classes) 7. [Emphasis Classes](#emphasis-classes) 8. [Documentation Classes](#documentation-classes) 9. [Special Classes](#special-classes) 10. [Vector Collapsing](#vector-collapsing) 11. [Advanced Patterns](#advanced-patterns) 12. [Pluraliza

  • 9 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 11, 2026
documentationgoexpressgitapidatabaseperformancedocumentation

Works with

  • terminal
  • cli
  • api

Security analysis

B88/100
  • criticalAccesses sensitive system or user directories

Pro shows the line behind each finding and how to fix it

Scanned October 11, 2026

npx -y skills add tools-only/X-Skills --skill 2549-inline-markup_1ed5cb03 --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 2549 Inline Markup 1ed5cb03?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 2549 Inline Markup 1ed5cb03
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-2549-inline-markup-1ed5cb03/badge)](https://www.skillsdirectory.com/skills/tools-only-2549-inline-markup-1ed5cb03)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
# CLI Inline Markup Reference

## Table of Contents

1. [Introduction](#introduction)
2. [Basic Syntax](#basic-syntax)
3. [Code & Syntax Classes](#code--syntax-classes)
4. [Files & Paths Classes](#files--paths-classes)
5. [Communication Classes](#communication-classes)
6. [Values & Data Classes](#values--data-classes)
7. [Emphasis Classes](#emphasis-classes)
8. [Documentation Classes](#documentation-classes)
9. [Special Classes](#special-classes)
10. [Vector Collapsing](#vector-collapsing)
11. [Advanced Patterns](#advanced-patterns)
12. [Pluralization](#pluralization)
13. [Performance Considerations](#performance-considerations)
14. [Quick Reference Table](#quick-reference-table)

## Introduction

Read this file when you need to:

- Look up the correct inline markup class for specific content types
- Understand how to nest and combine markup classes
- Learn vector collapsing behavior and customization
- Master pluralization patterns beyond basic `{?s}`
- Optimize performance for high-frequency messaging
- Troubleshoot unexpected formatting behavior

Inline markup classes format text within cli messages using `{.class content}` syntax. They integrate with glue string interpolation and work across all cli functions: `cli_text()`, `cli_abort()`, `cli_warn()`, `cli_inform()`, `cli_alert_*()`, etc.

## Basic Syntax

Inline markup uses curly braces with a period-prefixed class name:

```r
cli_text("Function {.fn mean} calculates {.field average}")
#> Function `mean()` calculates average
```

**Key syntax rules:**

- Format: `{.class content}`
- Content is interpolated if it contains `{}`
- Double braces `{{` and `}}` escape literal braces
- Whitespace inside `{}` is preserved in output
- Classes can be nested (see [Advanced Patterns](#advanced-patterns))

## Code & Syntax Classes

### .code

Generic code formatting for expressions, statements, or syntax:

```r
cli_text("Use {.code sum(x, na.rm = TRUE)} to ignore NA values")
cli_text("Set {.code options(width = 120)} in your .Rprofile")
cli_text("The {.code return()} statement exits early")
```

**When to use:**
- Multi-token code expressions
- Syntax examples
- Configuration snippets
- When other code classes are too specific

### .fn and .fun

Function names with automatic parentheses:

```r
cli_text("Call {.fn mean} to calculate average")
#> Call `mean()` to calculate average

cli_text("Use {.fun base::mean} for namespace clarity")
#> Use `base::mean()` for namespace clarity
```

**Automatic formatting:**
- Adds `()` suffix automatically
- Supports `package::function` notation
- Both `.fn` and `.fun` are equivalent

**When to use:**
- Referring to functions by name
- Suggesting which function to call
- Error messages about function usage

### .arg

Function argument names:

```r
cli_abort(c(
  "{.arg x} must be numeric",
  "i" = "Set {.arg na.rm = TRUE} to handle missing values"
))
```

**When to use:**
- Parameter validation errors
- Documenting function arguments
- Explaining argument behavior

### .cls

S3/S4/R6 class names:

```r
cli_abort("Expected {.cls data.frame}, got {.cls {class(x)}}")
cli_text("Object is {.cls tbl_df}, a tibble subclass")
```

**When to use:**
- Type checking errors
- Documenting expected types
- Explaining inheritance

### .type

Broader type descriptions (base types, generic categories):

```r
cli_abort("Input must be {.type integer}, not {.type {typeof(x)}}")
cli_text("Coercing from {.type character} to {.type numeric}")
```

**When to use:**
- Base R types: integer, double, character, logical
- Generic type categories
- Type coercion messages

### .obj_type_friendly

User-friendly type descriptions with indefinite articles:

```r
x <- data.frame()
cli_text("{.var x} must be a vector, not {.obj_type_friendly {x}}")
#> `x` must be a vector, not a data frame

y <- 1:10
cli_text("You provided {.obj_type_friendly {y}}")
#> You provided an integer vector
```

**Automatic features:**
- Adds "a"/"an" article automatically
- Uses friendly names ("data frame" not "data.frame")
- Handles pluralization for vectors

## Files & Paths Classes

### .file

File names and paths with appropriate formatting:

```r
cli_text("Reading {.file data/input.csv}")
cli_warn("File {.file ~/.ssh/config} has insecure permissions")
cli_inform("Created {.file output/results.xlsx}")
```

**Best practices:**
- Use for any file reference
- Works with relative and absolute paths
- Handles home directory expansion

### .path

Directory paths and file system locations:

```r
cli_text("Installing to {.path /usr/local/lib/R}")
cli_text("Working directory: {.path {getwd()}}")
cli_abort("Directory {.path {dir}} does not exist")
```

**When to use .file vs .path:**
- `.file` - Files, scripts, documents
- `.path` - Directories, installation locations, system paths

## Communication Classes

### .email

Email addresses:

```r
cli_text("Contact maintainer at {.email user@example.com}")
cli_inform("Send bug reports to {.email bugs@r-project.org}")
```

**Features:**
- Creates `mailto:` links in supported terminals
- Formatted distinctly from regular text

### .url

Web URLs and URIs:

```r
cli_text("Visit {.url https://cli.r-lib.org} for documentation")
cli_text("API endpoint: {.url https://api.example.com/v1}")
```

**Features:**
- Creates clickable hyperlinks in supported terminals
- Formats protocol, domain, and path distinctly

### .href

Custom hyperlinks with separate text and URL:

```r
cli_text("See {.href [documentation](https://cli.r-lib.org)}")
cli_text("Read {.href [vignette](vignette:cli::inline-markup)}")
```

**Syntax:**
- Format: `{.href [text](url)}`
- Markdown-style link syntax
- Text and URL can be styled differently

**Link types:**
- HTTP/HTTPS: `https://example.com`
- Help topics: `help:topic`
- Vignettes: `vignette:package::topic`

## Values & Data Classes

### .val

Data values, constants, and literals:

```r
n <- 42
cli_text("Found {.val {n}} records")
cli_text("Default timeout is {.val 30} seconds")
cli_text("Status: {.val 'complete'}")
```

**Automatic formatting:**
- Quoted for character values
- Unquoted for numeric values
- Handles vectors with collapsing

### .var

Variable names in code or data:

```r
cli_abort("Variable {.var x} must be numeric")
cli_text("Column {.var age} contains missing values")
cli_inform("Using {.var Sepal.Length} as predictor")
```

**When to use:**
- Variable names in R code
- Column names in data frames
- Field names in objects

### .envvar

Environment variable names:

```r
cli_text("Set {.envvar R_LIBS_USER} to customize library location")
cli_abort("{.envvar HOME} is not defined")
cli_inform("Using {.envvar PATH}: {.val {Sys.getenv('PATH')}}")
```

**When to use:**
- System environment variables
- R-specific environment variables
- Configuration via environment

### .field

Object fields, slots, or attributes:

```r
cli_text("The {.field name} field is required")
cli_abort("Invalid {.field status} value")
cli_text("Access {.field @data} slot in S4 object")
```

**When to use:**
- Named list elements
- Data frame columns (prefer `.var` for analysis context)
- S4 slots
- Object attributes

### .str

String literals and text values:

```r
cli_text("Message starts with {.str 'Error:'}")
cli_text("Pattern {.str '^[0-9]+$'} matches digits")
```

**When to use:**
- Literal string values
- Pattern strings
- Format strings
- When `.val` formatting is too generic

## Emphasis Classes

### .emph

Emphasis for important concepts or terms:

```r
cli_text("This function is {.emph deprecated}")
cli_text("The file is {.emph locked} by another process")
cli_inform("{.emph Note}: This may take several minutes")
```

**Rendering:**
- Typically italic or colored
- Lighter emphasis than `.strong`

### .strong

Strong emphasis for critical information:

```r
cli_warn("{.strong Warning}: This action cannot be undone")
cli_text("This parameter is {.strong required}")
cli_abort("{.strong Error}: Database connection failed")
```

**Rendering:**
- Typically bold or brightly colored
- Stronger emphasis than `.emph`

## Documentation Classes

### .help

R help topic references:

```r
cli_text("See {.help stats::lm} for details")
cli_inform("More info: {.help base::sum}")
```

**Features:**
- Creates link to help topic in RStudio
- Supports `package::topic` notation
- Fallback to plain text in terminals

### .topic

Generic topic or section references:

```r
cli_text("See {.topic 'Error Handling'} section")
cli_inform("Refer to {.topic 'Advanced Usage'}")
```

**When to use:**
- Internal documentation sections
- Vignette sections
- General topic references

### .vignette

Vignette references:

```r
cli_text("Read {.vignette cli::semantic-cli} for examples")
cli_inform("See {.vignette dplyr::programming}")
```

**Features:**
- Links to package vignettes
- Format: `package::vignette-name`

### .run

Runnable R code examples:

```r
cli_text("Try: {.run install.packages('cli')}")
cli_inform("Debug with: {.run options(error = recover)}")
```

**Features:**
- Creates executable link in RStudio
- Click to run code in console
- Formatted as code in terminals

## Special Classes

### .kbd

Keyboard keys and shortcuts:

```r
cli_text("Press {.kbd Ctrl+C} to cancel")
cli_text("Use {.kbd Enter} to confirm")
cli_inform("Save with {.kbd Cmd+S} (Mac) or {.kbd Ctrl+S} (Windows)")
```

**Rendering:**
- Typically in keyboard key style
- May show as boxed or distinct formatting

### .key

Alternative to `.kbd` for key names:

```r
cli_text("Press the {.key RETURN} key")
cli_text("Hold {.key SHIFT} while clicking")
```

### .or

Logical OR separator for alternatives:

```r
cli_text("Use {.val 'yes'} {.or} {.val 'no'}")
cli_abort("Type must be {.val 'auto'} {.or} {.val 'manual'}")
```

**Rendering:**
- Formats as " or " with appropriate styling
- Maintains class formatting for surrounding elements

### .pkg

Package names:

```r
cli_text("Install {.pkg dplyr} for data manipulation")
cli_inform("Loading {.pkg ggplot2}")
cli_abort("{.pkg httr2} is required but not installed")
```

**Features:**
- Distinct package name formatting
- May include CRAN/GitHub links in supported environments

### .dt

Definition term in definition lists:

```r
cli_dl(c(
  "{.dt name}" = "User's full name",
  "{.dt email}" = "Contact email address"
))
```

**When to use:**
- Definition list terms
- Glossary entries
- Key-value pair keys

### .dd

Definition description in definition lists:

```r
cli_dl(c(
  "name" = "{.dd User's full name}",
  "email" = "{.dd Contact email address}"
))
```

**When to use:**
- Definition list descriptions
- Glossary definitions
- Key-value pair values

## Vector Collapsing

Vectors are automatically collapsed with appropriate separators:

### Default Behavior

```r
pkgs <- c("dplyr", "tidyr", "ggplot2")
cli_text("Loading packages: {.pkg {pkgs}}")
#> Loading packages: dplyr, tidyr, and ggplot2

files <- c("a.R", "b.R")
cli_text("Modified: {.file {files}}")
#> Modified: a.R and b.R

single <- "data.csv"
cli_text("Found: {.file {single}}")
#> Found: data.csv
```

**Rules:**
- Length 1: no separators
- Length 2: " and " separator
- Length 3+: ", " separators with " and " before last item

### Collapsing Empty Vectors

```r
empty <- character()
cli_text("Files: {.file {empty}}")
#> Files:

cli_text("Files: {?none/one/some}: {.file {empty}}")
#> Files: none:
```

### Custom Collapse Separators

Control collapsing with glue transformers:

```r
items <- c("apple", "banana", "cherry")

# Custom separator
cli_text("Items: {.val {items}}", .transformer = function(text, envir) {
  glue::glue_collapse(text, sep = " | ", last = " | ")
})
#> Items: 'apple' | 'banana' | 'cherry'

# OR separator
cli_text("Choose: {.val {items}}", .transformer = function(text, envir) {
  glue::glue_collapse(text, sep = ", ", last = " or ")
})
#> Choose: 'apple', 'banana' or 'cherry'
```

### Truncating Long Vectors

```r
many <- letters[1:20]

cli_text("Variables: {.var {many}}")
#> Variables: a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, and t

# Truncate with custom transformer
cli_text("Variables: {.var {head(many, 5)}} and {length(many) - 5} more")
#> Variables: a, b, c, d, e and 15 more
```

## Advanced Patterns

### Nested Markup

Classes can be nested for combined formatting:

```r
cli_text("Call {.fn {.pkg dplyr}::filter} to subset data")
cli_abort("File {.file {.path {dir}}/data.csv} not found")
cli_text("Set {.arg timeout} to {.val {default_timeout}} by default")
```

**Nesting guidelines:**
- Outer class determines primary formatting
- Inner classes add semantic meaning
- Keep nesting depth reasonable (2-3 levels max)

### Combining with Glue Expressions

```r
n <- 5
cli_text("Processing {n} file{?s} from {.path {getwd()}}")

status <- "complete"
cli_inform("Status: {.val {toupper(status)}}")

pkg <- "dplyr"
cli_text("Version: {.pkg {pkg}} {.val {packageVersion(pkg)}}")
```

### Custom Glue Transformers

Full control over formatting with transformers:

```r
# Highlight differences
cli_text(
  "Expected {.val {expected}}, got {.val {actual}}",
  .transformer = function(text, envir) {
    # Custom logic here
    text
  }
)

# Add prefixes
my_transformer <- function(text, envir) {
  if (startsWith(text, ".pkg")) {
    paste0("R package: ", glue::glue(text, .envir = envir))
  } else {
    glue::identity_transformer(text, envir)
  }
}

cli_text("Install {.pkg cli}", .transformer = my_transformer)
```

### Conditional Formatting

```r
status <- "error"
cli_text("Status: {.{if(status == 'error') 'strong' else 'emph'} {status}}")

type <- class(x)
cli_text("Type: {if(is.numeric(x)) .val else .cls} {type}")
```

### Multiple Classes on Same Content

Combine semantic classes:

```r
# Package function
cli_text("{.fn {.pkg stats}::median}")

# File in path
cli_text("{.file {.path /usr/local}/script.R}")

# Variable value
cli_text("{.var x} = {.val {x}}")
```

### Working with Lists

```r
config <- list(host = "localhost", port = 8080, ssl = TRUE)

cli_dl(c(
  "{.field host}" = "{.val {config$host}}",
  "{.field port}" = "{.val {config$port}}",
  "{.field ssl}" = "{.val {config$ssl}}"
))
```

### Escaping Markup

Prevent interpretation with double braces:

```r
cli_text("In glue, use {{variable}} syntax")
#> In glue, use {variable} syntax

cli_text("Literal: {{.code not_markup}}")
#> Literal: {.code not_markup}
```

## Pluralization

Pluralization adapts text based on quantities using `{?}` syntax.

### Single Alternative Pattern

Add "s" for plural:

```r
n <- 1
cli_text("{n} file{?s} found")
#> 1 file found

n <- 5
cli_text("{n} file{?s} found")
#> 5 files found
```

### Two Alternative Pattern

Specify singular/plural forms:

```r
n <- 1
cli_text("{n} director{?y/ies}")
#> 1 directory

n <- 3
cli_text("{n} director{?y/ies}")
#> 3 directories
```

**Common patterns:**
- `{?y/ies}` - directory/directories
- `{?/s}` - item/items
- `{?is/are}` - is/are
- `{?/es}` - box/boxes
- `{?ex/ices}` - index/indices

### Three Alternative Pattern

Handle zero/one/many:

```r
n <- 0
cli_text("{?No/One/Some} file{?s} {?is/is/are} ready")
#> No files are ready

n <- 1
cli_text("{?No/One/Some} file{?s} {?is/is/are} ready")
#> One file is ready

n <- 5
cli_text("{?No/One/Some} file{?s} {?is/is/are} ready")
#> Some files are ready
```

### Setting Quantity with qty()

Control pluralization explicitly:

```r
updated <- 3
total <- 10
cli_text("{updated}/{total} {qty(updated)} file{?s} {?needs/need} update{?s}")
#> 3/10 files need updates
```

**When to use qty():**
- When quantity appears elsewhere
- Complex expressions with multiple numbers
- Explicit pluralization control

### no() Helper

Display "no" instead of 0:

```r
n <- 0
cli_text("Found {no(n)} error{?s}")
#> Found no errors

n <- 3
cli_text("Found {no(n)} error{?s}")
#> Found 3 errors
```

### Advanced Pluralization Patterns

**Multiple quantities in one message:**

```r
nerr <- 2
nwarn <- 1
cli_text(
  "{nerr} error{?s} and {nwarn} {qty(nwarn)} warning{?s} found"
)
#> 2 errors and 1 warning found
```

**Conditional articles:**

```r
n <- 1
cli_text("Found {?a /}{n} file{?s}")
#> Found a 1 file

n <- 5
cli_text("Found {?a /}{n} file{?s}")
#> Found 5 files
```

**Complex pluralization:**

```r
nfile <- 3
ndir <- 1
cli_text(
  "{nfile} file{?s} in {ndir} {qty(ndir)} director{?y/ies}"
)
#> 3 files in 1 directory
```

**Verb agreement:**

```r
n <- 1
cli_text("File {?was/were} modified")
#> File was modified

n <- 3
cli_text("Files {?was/were} modified")
#> Files were modified
```

**Possessives:**

```r
n <- 1
cli_text("User{?'s/'s'} setting{?s}")
#> User's setting

n <- 3
cli_text("Users{?'s/'s'} settings")
#> Users' settings
```

## Performance Considerations

### High-Frequency Messages

For loops with many iterations:

```r
# Expensive - creates cli context each iteration
for (i in 1:10000) {
  cli_text("Processing {.val {i}}")
}

# Better - batch messages
if (i %% 1000 == 0) {
  cli_text("Processed {.val {i}} items")
}

# Best - use progress bar
cli_progress_bar("Processing", total = 10000)
for (i in 1:10000) {
  # work here
  cli_progress_update()
}
```

### Expensive Interpolation

Avoid expensive computations in markup:

```r
# Expensive - computes every time
cli_text("Processing {.file {slow_path_computation()}}")

# Better - compute once
path <- slow_path_computation()
cli_text("Processing {.file {path}}")
```

### Conditional Messages

Use conditions to avoid unnecessary formatting:

```r
# Inefficient
if (verbose) cli_text("Status: {.val {expensive_status_check()}}")

# Better
if (verbose) {
  status <- expensive_status_check()
  cli_text("Status: {.val {status}}")
}
```

### Large Vectors

Truncate large vectors before formatting:

```r
# Problematic with huge vectors
vars <- names(huge_dataframe)
cli_text("Variables: {.var {vars}}")

# Better
n_vars <- length(vars)
if (n_vars > 10) {
  cli_text("Variables: {.var {head(vars, 10)}} and {n_vars - 10} more")
} else {
  cli_text("Variables: {.var {vars}}")
}
```

## Quick Reference Table

| Class | Use For | Example | Output |
|-------|---------|---------|--------|
| `.code` | Code expressions | `{.code sum(x)}` | `sum(x)` |
| `.fn`, `.fun` | Functions | `{.fn mean}` | `mean()` |
| `.arg` | Function arguments | `{.arg na.rm}` | `na.rm` |
| `.cls` | Class names | `{.cls data.frame}` | data.frame |
| `.type` | Base types | `{.type integer}` | integer |
| `.obj_type_friendly` | Friendly types | `{.obj_type_friendly {x}}` | an integer vector |
| `.file` | File names | `{.file script.R}` | script.R |
| `.path` | Directory paths | `{.path /usr/local}` | /usr/local |
| `.email` | Email addresses | `{.email user@example.com}` | user@example.com |
| `.url` | Web URLs | `{.url https://example.com}` | https://example.com |
| `.href` | Custom links | `{.href [text](url)}` | text (linked) |
| `.val` | Data values | `{.val {x}}` | 42 or 'text' |
| `.var` | Variable names | `{.var column}` | column |
| `.envvar` | Environment vars | `{.envvar PATH}` | PATH |
| `.field` | Object fields | `{.field name}` | name |
| `.str` | String literals | `{.str 'pattern'}` | 'pattern' |
| `.emph` | Emphasis | `{.emph important}` | *important* |
| `.strong` | Strong emphasis | `{.strong critical}` | **critical** |
| `.help` | Help topics | `{.help stats::lm}` | stats::lm (linked) |
| `.topic` | Topic references | `{.topic 'Intro'}` | 'Intro' |
| `.vignette` | Vignettes | `{.vignette pkg::name}` | pkg::name (linked) |
| `.run` | Runnable code | `{.run code}` | code (executable) |
| `.kbd`, `.key` | Keyboard keys | `{.kbd Ctrl+C}` | Ctrl+C |
| `.or` | Logical OR | `{.val x} {.or} {.val y}` | x or y |
| `.pkg` | Package names | `{.pkg dplyr}` | dplyr |
| `.dt` | Definition term | `{.dt term}` | term (in definition list) |
| `.dd` | Definition desc | `{.dd description}` | description (in definition list) |

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.