agentleFS
DatabricksKnown publisherdatabricks/appkit/packages/appkit/skills/appkit-ui-variants/SKILL.md

appkit-ui-variants

Skill118 starsChanged 23 days agoApache-2.0View on GitHub

Builds a piece of UI in multiple variants, lets the developer pick one live in the browser, then finalizes the chosen variant into source

The file

We split this skill into the rules it gives an agent

7 rules. About 0 are common, 6 are distinctive, and 1 is very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈6

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Ask for _what_ and _where_; build for _how it looks_: Before generating, ask at most one or two questions only if the answer changes the …

    line 48
  2. Very specific:

    MUST: treat one <Variants> block as one independent decision and default to one block per distinct section/region the user names.

    line 64
  3. Distinctive:

    SHOULD: default to 3 variants unless the user asks for a specific count.

    line 74
  4. Distinctive:

    Keep each variant self-contained: imports it needs should already be present so finalizing to any one of them leaves the file valid.

    line 76
  5. Distinctive:

    The signal is turn-based: Do not start a background watcher.

    line 115
  6. Distinctive:

    If your tool has an interactive question prompt and the developer is in an active session, you MAY ask via that prompt — options like "I've …

    line 118
  7. Distinctive:

    When the developer says they've chosen, discover the choices file (path is relative to the dev server's cwd):

    line 130

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

UI — Build in Variants, Pick Live, Finalize

User input: $ARGUMENTS

Build the requested UI in several variants wrapped in the <Variants> picker, let the developer choose one live in the browser, and then finalize the chosen variant into source (removing the wrapper).

The picker lets a developer choose between several candidate UIs live in the browser during local dev; you then finalize the chosen one into source.

These rules govern your behavior, not your narration. Apply every rule silently. To the developer, speak only about what you're building and what they need to do — never about the mechanism (the choices file, how a choice is stored, "I read it next turn", or whether you decided to ask).

The pieces

  • <Variants> / <Variant> — @databricks/appkit-ui/react. A dev-time wrapper that renders one candidate at a time with a hover-revealed switcher (prev/next, an index pill + label) and a Confirm tick.
  • The recorder — built into @databricks/appkit. Records the confirmed choice: Confirm POSTs { blockId, chosenIndex, label } to POST /api/ui-variants/confirm, upserted into a JSONL file keyed by blockId. It runs automatically in dev and drops out of production on its own — there's nothing to set up.
  • Choices file — node_modules/.databricks/appkit/.appkit-ui-choices.jsonl, gitignored. A keyed store: one line per <Variants> blockId (not an append log) — re-confirming a variant replaces that block's line, so the file always reflects the current choice: { "ts": "...", "blockId": "hero-cta", "chosenIndex": 1, "label": "Solid" }. Its path is relative to the dev server's cwd, not the repo root, so discover it (finalize step 4) rather than assuming a fixed location.

1. Understand the request

Work out what to build (a component, a section, a page) and where it lives, then pick the natural target file (e.g. a *.route.tsx or a component under src/components/…), and decide the block breakdown before authoring. Two rules shape this step:

  • Ask for what and where; build for how it looks. Before generating, ask at most one or two questions only if the answer changes the build — ambiguous target/surface, an unclear axis of variation, or real-vs-placeholder data on a data screen. Do not ask appearance/taste questions ("bold or minimal?", "which color?"); make one bold and one minimal instead. If the request is clear enough, skip questions. Never turn it into a checklist.
  • One <Variants> block = one independent decision — default to one block per distinct section the user names (a hero + about page → two blocks), and default 3 meaningfully different variants per block.

2. Author the variants

Wrap each section's candidates in its own <Variants> block, following the authoring rules below. Note the file path + every blockId — you need them to finalize.

  • MUST treat one <Variants> block as one independent decision and default to one block per distinct section/region the user names. A page with a hero and an about-us section is two blocks (blockId="hero", blockId="about"), so the developer chooses each section independently. Use a single whole-page block only when the user asks for whole-page options or the sections must move together as one unit.
  • MUST give every <Variants> block a stable, unique blockId within its file. Duplicate ids are ambiguous — refuse and disambiguate.
  • MUST wrap each candidate in <Variant label="…"> with a short, distinct label. The label is shown in the switcher and recorded on confirm.
  • SHOULD default to 3 variants unless the user asks for a specific count. Make them meaningfully different (layout / emphasis / density).
  • Keep each variant self-contained: imports it needs should already be present so finalizing to any one of them leaves the file valid.
  • Layout: <Variants> defaults to block layout (full-width, stacking) — correct for sections, heroes, and pages. Pass layout="inline" only when wrapping a small inline element such as a single button.

One block per section, each candidate a labelled <Variant>:

import { Variants, Variant } from "@databricks/appkit-ui/react";

// "page with a hero and an about-us section" → one block per section
<Variants blockId="hero">
  <Variant label="Centered">…hero A…</Variant>
  <Variant label="Split with stats">…hero B…</Variant>
  <Variant label="Minimal">…hero C…</Variant>
</Variants>

<Variants blockId="about">
  <Variant label="Two column">…about A…</Variant>
  <Variant label="Timeline">…about B…</Variant>
  <Variant label="Team grid">…about C…</Variant>
</Variants>

3. Ensure the recorder is running

  • The recorder runs automatically in dev — no setup needed.
  • Confirm the dev server is running so the browser can POST the choice.

4. Hand off to the developer

Tell the developer to make the choice in the browser:

Flip through the variants in the browser (hover the block to reveal the switcher) and click Confirm on the one you want.

Then get the "I've chosen" signal — the developer's next message.

The signal is turn-based. Do not start a background watcher. The confirm is recorded to the file; read it on your next turn. How to prompt for it:

  • If your tool has an interactive question prompt and the developer is in an active session, you MAY ask via that prompt — options like "I've picked — finalize" / "Still deciding" / "Cancel" — to save them typing.
  • Otherwise, ask in plain text and read the file on a later turn.

Two hard rules: the question must carry a "still deciding / later" option so it never blocks the developer; and only ask when someone is there to answer — if unsure, use plain text. If the developer says "done" but no line exists for the block yet, they haven't clicked Confirm — ask them to, don't finalize nothing.

5. Finalize when the developer says they've chosen

  1. When the developer says they've chosen, discover the choices file (path is relative to the dev server's cwd):
    f=$(find . -path '*/node_modules/.databricks/appkit/.appkit-ui-choices.jsonl' 2>/dev/null | head -1)
    cat "$f"   # find the line for your block's blockId
  2. For the line matching your block's blockId, reconcile chosenIndex against label: check the <Variant> at chosenIndex (zero-based) still has the recorded label. If they don't match, the file was edited after confirm — prefer the <Variant> whose label matches; if none matches, stop and ask the developer to re-confirm.
  3. Find the <Variants blockId="<that id>"> block and replace the whole block with the chosen <Variant>'s inner JSX — remove the <Variants>/<Variant> wrapper, drop the now-unused import if nothing else uses it, reconcile surrounding code, then format/lint the file (pnpm check:fix, or pnpm exec oxlint --fix <file> && pnpm exec oxfmt --write <file>).
  4. Remove the consumed line for that blockId from the choices file. Match the blockId structurally (not a loose substring) so a label containing the text can't delete the wrong line: tmp=$(mktemp); jq -Rc 'fromjson? | select(.blockId != "<that id>")' "$f" > "$tmp" && mv "$tmp" "$f".
  5. Confirm the finalized UI back to the developer.

6. Wrap up

Offer to iterate (new variants, tweaks) if the developer wants another round.

Edge cases

  • Developer confirms before you ask. The choice waits in the file; read it whenever you next act.
  • Developer changed their mind. The store is keyed by blockId, so re-confirming overwrites the previous line. Read whatever line is there now.
  • Endpoint absent (prod build / feature off). The switcher still works as a viewer; Confirm shows "Recorder unavailable". Nothing is recorded — nothing to finalize.
  • Duplicate blockId in a file. Ambiguous — refuse to finalize automatically; ask which block, or re-author with unique ids.

Keeping it out of production

<Variants> is dev-time scaffolding. Always finalize (or remove) every block before a production build — a leftover block ships the dev-only picker to production.

Anti-patterns

  • Wrapping several sections in one <Variants> block (forces whole-page combos, hides most combinations) — one block per section instead.
  • Cosmetic-only variants (same layout, tweaked padding) — make them meaningfully different or don't offer a choice.
  • Starting a background watcher/monitor to catch the confirm — the flow is turn-based on purpose; read the file when the developer says they're done.
  • Leaving the <Variants> wrapper in source after a choice — always finalize (or remove) it; a leftover block ships the dev-only picker to production.
  • Forgetting to clear the consumed choices line after finalizing.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.