DEV Community

Cover image for pattern() in FSCSS: Write What You Mean, Let the Compiler Match It
FSCSS for FSCSS tutorial

Posted on

pattern() in FSCSS: Write What You Mean, Let the Compiler Match It

FSCSS v1.1.25 ships a new core method: pattern().

It's the first FSCSS feature that doesn't ask you to remember an exact name. Instead of calling @card() or @btn-primary(), you describe what you want in plain language, and FSCSS matches the closest pattern at compile time.

pattern(0.5: "Hello World Card", `
  border-radius: 12px;
  padding: 24px;
  background: linear-gradient(135deg, #667eea, #764ba2);
  color: #fff;
  box-shadow: 0 8px 24px rgba(0,0,0,0.25);
`)

.card {
  hello world card
}
Enter fullscreen mode Exit fullscreen mode

Compiles to:

.card {
  border-radius: 12px;
  padding: 24px;
  background: linear-gradient(135deg, #667eea, #764ba2);
  color: #fff;
  box-shadow: 0 8px 24px rgba(0,0,0,0.25);
}
Enter fullscreen mode Exit fullscreen mode

Notice .card doesn't call anything — it just contains a phrase close enough to "Hello World card" to trigger the match.

Why this exists

@define is precise but rigid. You call it by exact name, with exact arguments. That's great for a library you built yourself, but it breaks down in a few real situations:

  • Design handoffs. A designer or junior dev writes dark card with rounded corners inside a rule instead of hunting through your define library for the right call signature.
  • AI-generated markup. Tools that generate FSCSS from natural-language prompts can emit descriptive phrases instead of needing to know your exact mixin names.
  • Fast prototyping. You don't always remember what you named something six months ago. pattern() lets close-enough phrasing still resolve correctly.

pattern() doesn't replace other variable methods — it sits next to it, for cases where flexibility matters more than precision.

The syntax

pattern(threshold: "description", `
  css to inject
`)
Enter fullscreen mode Exit fullscreen mode
  • threshold — a number from 0 to 1. The minimum similarity score a phrase must reach to trigger this pattern. Omit it and it defaults to 1 (near-exact match only).
  • description — the plain-language phrase you're matching against.
  • css — what gets injected when a phrase matches. Use backticks so the CSS can freely contain single or double quotes.

Threshold uses a colon, deliberately — 0.5: reads as a labeled parameter, distinct from a plain comma-separated argument. It's there so the number's purpose is visually obvious to anyone reading your stylesheet.

How matching works

Every phrase in your code gets compared against every pattern description using similarity scoring (Dice coefficient, if you're curious — it compares word overlap between the phrase and the description). The pattern with the highest score that also clears its own threshold wins.

That means:

pattern(0.8: "dark background and light color", `
  background: #212121;
  color: #ffffff;
`)

.chip {
  style dark background and light color
}

.tooltip {
  dark background light color
}
Enter fullscreen mode Exit fullscreen mode

Both .chip and .tooltip resolve to the same CSS, even though neither phrase matches the description word-for-word. That's the point — you're not looking up a name, you're describing an outcome.

Tuning the threshold

  • High threshold (0.8–1) → strict. Good for short, generic descriptions where you don't want accidental matches ("button" alone would be too easy to trigger by accident).
  • Low threshold (0.4–0.6) → forgiving. Good for longer, more specific descriptions where a few words might vary but the intent is clearly the same.

If two patterns could plausibly match the same phrase, the highest-scoring one wins — so keep descriptions distinct from each other, especially at lower thresholds.

Property patterns vs. block patterns

Same split as @define:

Property patterns inject a group of declarations inside a rule:

pattern(0.6: "rounded primary button", `
  border: none;
  border-radius: 8px;
  padding: 10px 20px;
  background: #ffffff;
  color: #764ba2;
  font-weight: 600;
  cursor: pointer;
`)

.btn {
  rounded primary button
}
Enter fullscreen mode Exit fullscreen mode

Block patterns inject full structures — keyframes, media queries, whole at-rules — and can stand alone outside any selector:

pattern(0.7: "animated keyframe for spin", `
  @keyframes spin {
    0% { transform: rotate(0); }
    100% { transform: rotate(360deg); }
  }
`)

an Animated keyframe for spin
Enter fullscreen mode Exit fullscreen mode

Content injection example

Because the CSS argument is just a string, pattern() isn't limited to properties — it can inject anything valid at that point in your stylesheet, including pseudo-element content:

pattern(0.9: "hello world heading", `h1:before{content: 'Hello World';}`)

.title {
  hello world heading
}
Enter fullscreen mode Exit fullscreen mode

What's shipped in v1.1.25

  • CDN
  • API
  • CLI
  • VSCode Extention

Try it

pattern() is live now on v1.1.25

Docs: fscss.devtem.org/pattern


Top comments (0)