Skip to content

Options

lintAccessibility, lintStructure, and lintLinks accept the same LintOptions shape. Every field is optional.

ts
interface LintOptions {
  disabled?: boolean;
  locale?: string;
  accessibility?: false | AccessibilityLintOptions;
  structure?: false | StructureLintOptions;
  links?: false | LinksLintOptions;
}

interface AccessibilityLintOptions {
  rules?: Record<string, Severity>;
  thresholds?: Partial<LintThresholds>;
}

interface StructureLintOptions {
  rules?: Record<string, Severity>;
}

interface LinksLintOptions {
  rules?: Record<string, Severity>;
  nonProductionHosts?: string[];
}

type Severity = "error" | "warning" | "info" | "off";

The same object also gates the editor's init({ lint }) config — every option here applies to every linter via the shared useTemplateLint composable.

Severity overrides and tool-specific knobs (thresholds, nonProductionHosts) are namespaced under their owning linter, so each linter only sees the config that's relevant to it.

disabled

Defaultfalse

When true:

  • The editor does not lazy-import @templatical/quality — its chunk never downloads.
  • The Issues sidebar tab is not registered.
  • The inline canvas badges produce no DOM.

Use this when a tenant has explicitly opted out, or to keep the default OSS bundle minimal. There's no soft-disable — disabled: true is a complete, irreversible-per-instance shut-off.

Disabling every linter individually has the same effect

The editor treats { accessibility: false, structure: false, links: false } as equivalent to { disabled: true }: no chunk download, no sidebar tab, no canvas badges. So you don't need the global flag if every linter is already off.

locale

Default (headless)'en'
Editoralways matches init({ locale })

Drives the message templates the linter returns (one file per locale per linter) and is accepted by the locale-aware accessibility rules (a11y.link-vague-text, a11y.button-vague-label, a11y.img-linked-no-context). Falls back to en when the locale (or its base language) isn't bundled.

ts
// Headless — set explicitly
lintAccessibility(content, { locale: "de" });
lintStructure(content, { locale: "de" });
lintLinks(content, { locale: "de" });

// Editor — linter automatically follows the editor locale
init({ locale: "de" });

Editor mode ignores lint.locale

In editor mode the linter locale is forced to the editor's locale from init({ locale }). Setting lint.locale has no effect — it's overwritten on the way through.

Headless callers (lintAccessibility(...) / lintStructure(...) / lintLinks(...) directly) keep full control.

Vague-text dictionaries are cross-locale

The dictionary is a union of every registered locale, so a German-locale email with an English Click here button still flags a11y.link-vague-text / a11y.button-vague-label, and a German Jetzt kaufen alt on an English-locale linked image still satisfies a11y.img-linked-no-context's action-hint check. The locale option doesn't gate matching — it only drives message text.

accessibility

Default{} (linter enabled, defaults for every knob)

Configuration for lintAccessibility. Set to false to disable the entire accessibility linter without enumerating its rules.

ts
// Turn off the whole accessibility linter
lintAccessibility(content, { accessibility: false });

accessibility.rules

Default{}

Per-rule severity override for accessibility rules. Set a rule to 'off' to disable it entirely. Set to a different severity to bend the default classification:

ts
lintAccessibility(content, {
  accessibility: {
    rules: {
      "a11y.img-missing-alt": "warning",   // soften
      "a11y.text-all-caps": "off",         // disable
      "a11y.missing-preheader": "warning", // promote info → warning
    },
  },
});

Override keys use the full prefixed rule ID (a11y.*) — the same ID that appears on LintIssue.ruleId, so a value copied from an emitted issue pastes straight in. See the accessibility rule catalog for default severities.

accessibility.thresholds

DefaultSee below

Numeric knobs that some accessibility rules consult.

ThresholdDefaultUsed by
altMaxLength125a11y.img-alt-too-long
minFontSize14a11y.text-too-small
allCapsMinLength20a11y.text-all-caps
minTouchTargetPx44a11y.button-touch-target

Override one without losing the others — partial merging is built in:

ts
lintAccessibility(content, {
  accessibility: { thresholds: { minFontSize: 16 } },
});

The DEFAULT_A11Y_THRESHOLDS constant is also exported if you need to reference the baseline programmatically.

structure

Default{}

Configuration for lintStructure. Set to false to disable the entire structure linter.

ts
lintStructure(content, { structure: false });

structure.rules

Per-rule severity override for structure rules:

ts
lintStructure(content, {
  structure: {
    rules: {
      "structure.empty-column": "info",    // demote warning → info
    },
  },
});

Keys use the structure.* prefix. See the structure rule catalog for default severities.

Default{}

Configuration for lintLinks. Set to false to disable the entire links linter.

ts
lintLinks(content, { links: false });

Per-rule severity override for link rules:

ts
lintLinks(content, {
  links: {
    rules: {
      "link.localhost-or-staging": "error", // promote to error before send
    },
  },
});

Keys use the link.* prefix. See the links rule catalog for default severities.

Default['localhost', '127.0.0.1', '0.0.0.0', '*.local', '*.staging.*', '*.dev.*']

Glob-style patterns matched against the URL host. * matches any run of characters (including .) — so *.staging.* matches app.staging.example.com and *.local matches acme.local or a.b.c.local. Patterns are anchored, so *.local does NOT match acme.local-tools. Case-insensitive.

ts
lintLinks(content, {
  links: { nonProductionHosts: ["*.preview.*"] },
});

// Or silence the rule without disabling it:
lintLinks(content, { links: { nonProductionHosts: [] } });

DEFAULT_NON_PRODUCTION_HOSTS is exported as the baseline. See the links overview for more.