Elixir bindings for Tailwind CSS Oxide — the Rust-powered content scanner.
Oxide scans source files in parallel to extract Tailwind CSS candidate class names. It powers the fast incremental rebuilds in Tailwind CSS v4.
Usage
Create a scanner, scan for candidates, then feed them to the Tailwind compiler to generate CSS.
# Create a scanner for your project
scanner = Oxide.new(sources: [%{base: "lib/", pattern: "**/*.heex"}])
# Full scan — returns all candidates found across all files
candidates = Oxide.scan(scanner)
# ["flex", "sm:bg-red-500", "hover:text-white", ...]
# Incremental scan — only returns NEW candidates from changed files
new = Oxide.scan_files(scanner, [%{file: "lib/app_web/live/page.ex", extension: "ex"}])For one-off extraction without a scanner:
candidates = Oxide.extract(~s(class="flex bg-red-500"), "html")
# [%{value: "class", position: 0}, %{value: "flex", position: 7}, ...]
Summary
Functions
Extract candidates with byte positions from a string.
Get all files discovered by the scanner.
Get generated glob patterns for setting up file watchers.
Create a new scanner.
Full scan — walks filesystem, extracts all candidates.
Incremental scan — extract candidates from specific changed files.
Functions
@spec extract(String.t(), String.t()) :: [Oxide.Candidate.t()]
Extract candidates with byte positions from a string.
Stateless — does not require a scanner. Useful for one-off extraction.
Examples
candidates = Oxide.extract(~s(class="flex bg-red-500"), "html")
[%Oxide.Candidate{value: "class", position: 0}, ...]
Get all files discovered by the scanner.
@spec globs(reference()) :: [Oxide.Glob.t()]
Get generated glob patterns for setting up file watchers.
Create a new scanner.
Options
:sources— list of source entries, each a map or%Oxide.Source{}with:base,:pattern, and optional:negated(defaultfalse)
Examples
scanner = Oxide.new(sources: [
%{base: "lib/", pattern: "**/*.{ex,heex}"},
%{base: "assets/", pattern: "**/*.vue"}
])
Full scan — walks filesystem, extracts all candidates.
Returns a sorted, deduplicated list of candidate strings. Subsequent calls only return candidates from files changed since the last scan.
Incremental scan — extract candidates from specific changed files.
Returns only NEW candidates not seen in previous scans. This is what makes HMR fast.
Examples
new_candidates = Oxide.scan_files(scanner, [
%{file: "lib/app_web/live/page.ex", extension: "ex"}
])
# Or with inline content (no filesystem read):
new_candidates = Oxide.scan_files(scanner, [
%{content: ~s(class="flex mt-4"), extension: "html"}
])