CoordiationCSS
Menu
Docs/Core concepts/Custom variants

CORE CONCEPT · COMPLETE

Custom variants

Teach the compiler a project-owned context once, then compose it with every utility, breakpoint, state, relationship, and important modifier already supported by Coordiation.

Variant names do not inherit the utility prefix. A definition named theme-midnight is always used as theme-midnight:co-…, even when the project changes co- to another prefix.

01

Register a selector context

Put the generated class at the explicit & position. This keeps ownership and selector behavior visible in the CSS entry.

src/coordiation.css
@coordiation;

@co-variant theme-midnight (
  &:where([data-theme="midnight"] *)
);

@co-variant data-active (&[data-active]);
Template
<section class="theme-midnight:co-bg-black data-active:co-ring-2">
  ...
</section>
02

Register a conditional at-rule

Conditional shorthand accepts only media, feature-support, and container queries. A selector placeholder is unnecessary because the at-rule wraps the generated rule.

src/coordiation.css
@co-variant pointer-accurate (@media (pointer: fine));
@co-variant grid-ready (@supports (display: grid));
@co-variant card-wide (@container (width >= 32rem));
Template
<div class="grid-ready:co-grid card-wide:co-grid-cols-2"></div>
03

Compose wrappers around one slot

Use block form when one variant needs both a condition and selector. The wrapper chain must contain exactly one namespaced @co-slot;.

src/coordiation.css
@co-variant any-hover {
  @media (any-hover: hover) {
    &:hover {
      @co-slot;
    }
  }
}
Template
<a class="md:any-hover:!co-underline">Documentation</a>

COMPOSITION CONTRACT

One variant, the whole compiler

Custom definitions use the same candidate pipeline as built-ins.

  1. 01

    Stacking: combine custom variants with responsive, state, attribute, group, peer, and conditional variants.

  2. 02

    Important: leading or trailing important syntax still applies to every declaration.

  3. 03

    Override: an exact project variant name deliberately takes priority over the built-in registry.

  4. 04

    Static output: directives disappear after compilation; no browser runtime is added.

04

Reject ambiguous definitions early

The compiler fails on malformed or duplicate names, nested top-level directives, unsafe conditions, unsupported at-rules, selector formats without &, unclosed wrappers, missing slots, and sibling wrapper chains.

Allowed conditional wrappers

Use @media, @supports, or @container. Other at-rules remain authored CSS until they have an explicit compiler contract.

05

Track project variants programmatically

Every compile reports custom variants separately from the global built-in registries, preserving wrapper order for deterministic tools and agents.

JavaScript
const result = compile(inputCss, candidates);

console.log(result.customVariants);
// [{
//   name: "any-hover",
//   kind: "compound",
//   wrappers: [
//     { type: "at-rule", value: "@media (any-hover: hover)" },
//     { type: "selector", value: "&:hover" }
//   ]
// }]

AI GENERATION CONTRACT

Discover names before composing

Agents should treat the authored directives and compile manifest as the exact project-local variant API.

  • Never infer a custom variant from its visual intent or another framework's name.
  • Keep the full stacked candidate literal so the scanner can discover it.
  • Preserve selector and conditional wrapper order from the manifest.
  • Use @co-slot; once in compound definitions and never invent unsupported at-rules.
  • Report a rejected candidate instead of silently falling back to a built-in variant.