From 20a0d6a6e6356aa68c135dc28c72b9a1458034f6 Mon Sep 17 00:00:00 2001 From: Alex Grozav Date: Wed, 8 Apr 2026 12:09:23 +0700 Subject: [PATCH 1/2] chore: Add Caliber config, Claude hooks, skills, and CLAUDE.md Set up Caliber for automatic AI agent context sync with session hooks, freshness checks, and shared skills for Claude, Cursor, and agents. --- .agents/skills/find-skills/SKILL.md | 54 ++ .agents/skills/save-learning/SKILL.md | 60 ++ .agents/skills/setup-caliber/SKILL.md | 210 +++++++ .caliber/cache/fingerprint.json | 674 +++++++++++++++++++++ .caliber/error-log.md | 10 + .caliber/score-history.jsonl | 5 + .claude/hooks/caliber-check-sync.sh | 11 + .claude/hooks/caliber-freshness-notify.sh | 11 + .claude/hooks/caliber-session-freshness.sh | 11 + .claude/settings.json | 96 +++ .claude/skills/find-skills/SKILL.md | 54 ++ .claude/skills/save-learning/SKILL.md | 60 ++ .claude/skills/setup-caliber/SKILL.md | 210 +++++++ .cursor/hooks.json | 25 + .cursor/skills/find-skills/SKILL.md | 54 ++ .cursor/skills/save-learning/SKILL.md | 60 ++ .cursor/skills/setup-caliber/SKILL.md | 210 +++++++ CLAUDE.md | 130 ++++ 18 files changed, 1945 insertions(+) create mode 100644 .agents/skills/find-skills/SKILL.md create mode 100644 .agents/skills/save-learning/SKILL.md create mode 100644 .agents/skills/setup-caliber/SKILL.md create mode 100644 .caliber/cache/fingerprint.json create mode 100644 .caliber/error-log.md create mode 100644 .caliber/score-history.jsonl create mode 100755 .claude/hooks/caliber-check-sync.sh create mode 100755 .claude/hooks/caliber-freshness-notify.sh create mode 100755 .claude/hooks/caliber-session-freshness.sh create mode 100644 .claude/skills/find-skills/SKILL.md create mode 100644 .claude/skills/save-learning/SKILL.md create mode 100644 .claude/skills/setup-caliber/SKILL.md create mode 100644 .cursor/hooks.json create mode 100644 .cursor/skills/find-skills/SKILL.md create mode 100644 .cursor/skills/save-learning/SKILL.md create mode 100644 .cursor/skills/setup-caliber/SKILL.md create mode 100644 CLAUDE.md diff --git a/.agents/skills/find-skills/SKILL.md b/.agents/skills/find-skills/SKILL.md new file mode 100644 index 00000000..98ff5cfb --- /dev/null +++ b/.agents/skills/find-skills/SKILL.md @@ -0,0 +1,54 @@ +--- +name: find-skills +description: Discovers and installs community skills from the public registry. Use when the user mentions a technology, framework, or task that could benefit from specialized skills not yet installed, asks 'how do I do X', 'find a skill for X', or starts work in a new technology area. Proactively suggest when the user's task involves tools or frameworks without existing skills. +--- + +# Find Skills + +Search the public skill registry for community-contributed skills +relevant to the user's current task and install them into this project. + +## Instructions + +1. Identify the key technologies, frameworks, or task types from the + user's request that might have community skills available +2. Ask the user: "Would you like me to search for community skills + for [identified technologies]?" +3. If the user agrees, run: + ```bash + caliber skills --query "" + ``` + This outputs the top 5 matching skills with scores and descriptions. +4. Present the results to the user and ask which ones to install +5. Install the selected skills: + ```bash + caliber skills --install , + ``` +6. Read the installed SKILL.md files to load them into your current + context so you can use them immediately in this session +7. Summarize what was installed and continue with the user's task + +## Examples + +User: "let's build a web app using React" +-> "I notice you want to work with React. Would you like me to search + for community skills that could help with React development?" +-> If yes: run `caliber skills --query "react frontend"` +-> Show the user the results, ask which to install +-> Run `caliber skills --install ` +-> Read the installed files and continue + +User: "help me set up Docker for this project" +-> "Would you like me to search for Docker-related skills?" +-> If yes: run `caliber skills --query "docker deployment"` + +User: "I need to write tests for this Python ML pipeline" +-> "Would you like me to find skills for Python ML testing?" +-> If yes: run `caliber skills --query "python machine-learning testing"` + +## When NOT to trigger + +- The user is working within an already well-configured area +- You already suggested skills for this technology in this session +- The user is in the middle of urgent debugging or time-sensitive work +- The technology is too generic (e.g. just "code" or "programming") diff --git a/.agents/skills/save-learning/SKILL.md b/.agents/skills/save-learning/SKILL.md new file mode 100644 index 00000000..aef5039d --- /dev/null +++ b/.agents/skills/save-learning/SKILL.md @@ -0,0 +1,60 @@ +--- +name: save-learning +description: Saves user instructions as persistent learnings for future sessions. Use when the user says 'remember this', 'always do X', 'from now on', 'never do Y', or gives any instruction they want persisted across sessions. Proactively suggest when the user states a preference, convention, or rule they clearly want followed in the future. +--- + +# Save Learning + +Save a user's instruction or preference as a persistent learning that +will be applied in all future sessions on this project. + +## Instructions + +1. Detect when the user gives an instruction to remember, such as: + - "remember this", "save this", "always do X", "never do Y" + - "from now on", "going forward", "in this project we..." + - Any stated convention, preference, or rule +2. Refine the instruction into a clean, actionable learning bullet with + an appropriate type prefix: + - `**[convention]**` — coding style, workflow, git conventions + - `**[pattern]**` — reusable code patterns + - `**[anti-pattern]**` — things to avoid + - `**[preference]**` — personal/team preferences + - `**[context]**` — project-specific context +3. Show the refined learning to the user and ask for confirmation +4. If confirmed, run: + ```bash + caliber learn add "" + ``` + For personal preferences (not project-level), add `--personal`: + ```bash + caliber learn add --personal "" + ``` +5. Stage the learnings file for the next commit: + ```bash + git add CALIBER_LEARNINGS.md + ``` + +## Examples + +User: "when developing features, push to next branch not master, remember it" +-> Refine: `**[convention]** Push feature commits to the \`next\` branch, not \`master\`` +-> "I'll save this as a project learning: + **[convention]** Push feature commits to the \`next\` branch, not \`master\` + Save for future sessions?" +-> If yes: run `caliber learn add "**[convention]** Push feature commits to the next branch, not master"` +-> Run `git add CALIBER_LEARNINGS.md` + +User: "always use bun instead of npm" +-> Refine: `**[preference]** Use \`bun\` instead of \`npm\` for package management` +-> Confirm and save + +User: "never use any in TypeScript, use unknown instead" +-> Refine: `**[convention]** Use \`unknown\` instead of \`any\` in TypeScript` +-> Confirm and save + +## When NOT to trigger + +- The user is giving a one-time instruction for the current task only +- The instruction is too vague to be actionable +- The user explicitly says "just for now" or "only this time" diff --git a/.agents/skills/setup-caliber/SKILL.md b/.agents/skills/setup-caliber/SKILL.md new file mode 100644 index 00000000..49d0a6a0 --- /dev/null +++ b/.agents/skills/setup-caliber/SKILL.md @@ -0,0 +1,210 @@ +--- +name: setup-caliber +description: Sets up Caliber for automatic AI agent context sync. Installs pre-commit hooks so CLAUDE.md, Cursor rules, and Copilot instructions update automatically on every commit. Use when Caliber hooks are not yet installed or when the user asks about keeping agent configs in sync. +--- + +# Setup Caliber + +Dynamic onboarding for Caliber — automatic AI agent context sync. +Run all diagnostic steps below on every invocation to determine what's already +set up and what still needs to be done. + +## Instructions + +Run these checks in order. For each step, check the current state first, +then only act if something is missing. + +### Step 1: Check if Caliber is installed + +```bash +command -v caliber >/dev/null 2>&1 && caliber --version || echo "NOT_INSTALLED" +``` + +- If a version prints → Caliber is installed globally. Set `CALIBER="caliber"` and move to Step 2. +- If NOT_INSTALLED → Install it globally (faster for daily use since the pre-commit hook runs on every commit): + ```bash + npm install -g @rely-ai/caliber + ``` + Set `CALIBER="caliber"`. + + If npm fails (permissions, no sudo, etc.), fall back to npx: + ```bash + npx @rely-ai/caliber --version 2>/dev/null || echo "NO_NODE" + ``` + - If npx works → Set `CALIBER="npx @rely-ai/caliber"`. This works but adds ~500ms per invocation. + - If NO_NODE → Tell the user: "Caliber requires Node.js >= 20. Install Node first, then run /setup-caliber again." Stop here. + +### Step 2: Check if pre-commit hook is installed + +```bash +grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "HOOK_ACTIVE" || echo "NO_HOOK" +``` + +- If HOOK_ACTIVE → Tell the user: "Pre-commit hook is active — configs sync on every commit." Move to Step 3. +- If NO_HOOK → Tell the user: "I'll install the pre-commit hook so your agent configs sync automatically on every commit." + ```bash + $CALIBER hooks --install + ``` + +### Step 3: Detect agents and check if configs exist + +First, detect which coding agents are configured in this project: +```bash +AGENTS="" +[ -d .claude ] && AGENTS="claude" +[ -d .cursor ] && AGENTS="${AGENTS:+$AGENTS,}cursor" +[ -d .agents ] || [ -f AGENTS.md ] && AGENTS="${AGENTS:+$AGENTS,}codex" +[ -f .github/copilot-instructions.md ] && AGENTS="${AGENTS:+$AGENTS,}github-copilot" +echo "DETECTED_AGENTS=${AGENTS:-none}" +``` + +If no agents are detected, ask the user which coding agents they use (Claude Code, Cursor, Codex, GitHub Copilot). +Build the agent list from their answer as a comma-separated string (e.g. "claude,cursor"). + +Then check if agent configs exist: +```bash +echo "CLAUDE_MD=$([ -f CLAUDE.md ] && echo exists || echo missing)" +echo "CURSOR_RULES=$([ -d .cursor/rules ] && ls .cursor/rules/*.mdc 2>/dev/null | wc -l | tr -d ' ' || echo 0)" +echo "AGENTS_MD=$([ -f AGENTS.md ] && echo exists || echo missing)" +echo "COPILOT=$([ -f .github/copilot-instructions.md ] && echo exists || echo missing)" +``` + +- If configs exist for the detected agents → Tell the user which configs are present. Move to Step 4. +- If configs are missing → Tell the user: "No agent configs found. I'll generate them now." + Use the detected or user-selected agent list: + ```bash + $CALIBER init --auto-approve --agent + ``` + For example: `$CALIBER init --auto-approve --agent claude,cursor` + This generates CLAUDE.md, Cursor rules, AGENTS.md, skills, and sync infrastructure for the specified agents. + +### Step 4: Check if configs are fresh + +```bash +$CALIBER score --json --quiet 2>/dev/null | head -1 +``` + +- If score is 80+ → Tell the user: "Your configs are in good shape (score: X/100)." +- If score is below 80 → Tell the user: "Your configs could be improved (score: X/100). Want me to run a refresh?" + If yes: + ```bash + $CALIBER refresh + ``` + +### Step 5: Ask about team setup + +Ask the user: "Are you setting up for yourself only, or for your team too?" + +- If **solo** → Continue with solo setup: + + Check if session learning is enabled: + ```bash + $CALIBER learn status 2>/dev/null | head -3 + ``` + - If learning is already enabled → note it in the summary. + - If not enabled → ask the user: "Caliber can learn from your coding sessions — when you correct a mistake or fix a pattern, it remembers for next time. Enable session learning?" + If yes: + ```bash + $CALIBER learn install + ``` + + Then tell the user: + "You're all set! Here's what happens next: + - Every time you commit, Caliber syncs your agent configs automatically + - Your CLAUDE.md, Cursor rules, and AGENTS.md stay current with your code + - Run `$CALIBER skills` anytime to discover community skills for your stack" + + Then show the summary (see below) and stop. + +- If **team** → Check if the GitHub Action already exists: + ```bash + [ -f .github/workflows/caliber-sync.yml ] && echo "ACTION_EXISTS" || echo "NO_ACTION" + ``` + - If ACTION_EXISTS → Tell the user: "GitHub Action is already configured." + - If NO_ACTION → Tell the user: "I'll create a GitHub Action that syncs configs nightly and on every PR." + Write this file to `.github/workflows/caliber-sync.yml`: + ```yaml + name: Caliber Sync + on: + schedule: + - cron: '0 3 * * 1-5' + pull_request: + types: [opened, synchronize] + workflow_dispatch: + jobs: + sync: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: caliber-ai-org/ai-setup@v1 + with: + mode: sync + auto-refresh: true + comment: true + github-token: ${{ secrets.GITHUB_TOKEN }} + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + ``` + Now determine which LLM provider the team uses. Check the local Caliber config: + ```bash + $CALIBER config --show 2>/dev/null || echo "NO_CONFIG" + ``` + + Based on the provider, the GitHub Action needs the corresponding secret: + - **anthropic** → `ANTHROPIC_API_KEY` + - **openai** → `OPENAI_API_KEY` + - **vertex** → `VERTEX_PROJECT_ID` and `GOOGLE_APPLICATION_CREDENTIALS` (service account JSON) + + Update the workflow env block to match the provider. For example, if using OpenAI: + ```yaml + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + ``` + + Then check if the `gh` CLI is available to set the secret: + ```bash + command -v gh >/dev/null 2>&1 && echo "GH_AVAILABLE" || echo "NO_GH" + ``` + + - If GH_AVAILABLE → Ask the user for their API key and set it: + ```bash + gh secret set ANTHROPIC_API_KEY + ``` + (This prompts for the value securely via stdin) + - If NO_GH → Tell the user exactly what to do: + "Go to your repo on GitHub → Settings → Secrets and variables → Actions → New repository secret. + Name: ANTHROPIC_API_KEY (or OPENAI_API_KEY depending on provider) + Value: your API key" + + Finally, offer to commit and push the workflow file: + ```bash + git add .github/workflows/caliber-sync.yml + git commit -m "feat: add Caliber sync GitHub Action" + git push + ``` + +### Summary + +After completing all steps, show the user what's configured: + +``` +Caliber Setup Complete: +✓ Caliber installed (vX.X.X) +✓ Pre-commit hook — configs sync on every commit +✓ Agent configs — CLAUDE.md, Cursor rules, AGENTS.md +✓ Config score: X/100 +✓ GitHub Action — nightly sync + PR checks (team only) + +From now on, every commit keeps all your agent configs in sync automatically. +``` + +## When to trigger + +- When a developer first opens this project +- When the user asks about keeping agent configs up to date +- When the user asks "how do I set up Caliber" or "what is Caliber" +- When the user sees a message saying "Run /setup-caliber to get set up" + +## When NOT to trigger + +- The user is in the middle of time-sensitive work diff --git a/.caliber/cache/fingerprint.json b/.caliber/cache/fingerprint.json new file mode 100644 index 00000000..3d130090 --- /dev/null +++ b/.caliber/cache/fingerprint.json @@ -0,0 +1,674 @@ +{ + "version": 1, + "gitHead": "7620771ede3b1e6e9a2f14cf20e845af1078635e", + "treeSignature": "6af41b6c83fa3661", + "codeAnalysis": { + "files": [ + { + "path": "theme/src/utils/index.ts", + "content": "export * from \"./createMultiplierAutogenerate\";\nexport * from \"./createUseRecipe\";\nexport * from \"./createUseSpacingUtility\";\nexport * from \"./createUseUtility\";\nexport * from \"./createUseDerivedVariable\";\nexport * from \"./createUseVariable\";\nexport * from \"./mergeElementOptions\";\nexport * from \"./registerElementThemes\";", + "size": 322, + "priority": 98 + }, + { + "path": "theme/src/utils/createUseRecipe.ts", + "content": "import type {\n Styleframe,\n Recipe,\n VariantDeclarationsBlock,\n} from \"@styleframe/core\";\nimport { defu } from \"defu\";\n\nexport type RecipeConfig<\n Variants extends Record<\n string,\n Record\n > = Record>,\n> = {\n base?: VariantDeclarationsBlock;\n variants?: Variants;\n defaultVariants?: {\n [K in keyof Variants]?: keyof Variants[K] & string;\n };\n compoundVariants?: Array<{\n match: {\n [K in keyof Variants]?: keyof Variants[K] & string;\n };\n css?: VariantDeclarationsBlock;\n className?: string;\n }>;\n};\n\nexport type DeepPartial = {\n [P in keyof T]?: T[P] extends object ? DeepPartial : T[P];\n};\n\nexport type FilterConfig<\n Variants extends Record>,\n> = {\n [K in keyof Variants]?: Array;\n};\n\nexport type ApplyFilter<\n V extends Record>,\n F,\n> = {\n [K in keyof V]: K extends keyof F\n ? F[K] extends (infer U)[]\n ? Pick>\n : V[K]\n : V[K];\n};\n\nfunction applyFilter(\n config: RecipeConfig,\n filter: Record,\n): RecipeConfig {\n const result = { ...config };\n\n if (result.variants) {\n const filteredVariants = { ...result.variants };\n for (const axis of Object.keys(filter)) {\n const allowedValues = filter[axis];\n if (!allowedValues || !(axis in filteredVariants)) continue;\n\n const allowedSet = new Set(allowedValues);\n const original = filteredVariants[axis];\n if (!original) continue;\n\n const filtered: Record = {};\n for (const [key, value] of Object.entries(original)) {\n if (allowedSet.has(key)) {\n filtered[key] = value;\n }\n }\n filteredVariants[axis] = filtered;\n }\n result.variants = filteredVariants;\n }\n\n if (result.compoundVariants) {\n result.compoundVariants = result.compoundVariants.filter((compound) => {\n for (const [axis, matchValue] of Object.entries(compound.match)) {\n const allowedValues = filter[axis];\n if (allowedValues) {\n const allowedSet = new Set(allowedValues);\n if (!allowedSet.has(matchValue as string)) {\n return false;\n }\n }\n }\n return true;\n });\n }\n\n if (result.defaultVariants) {\n const adjustedDefaults = { ...result.defaultVariants };\n for (const axis of Object.keys(filter)) {\n const allowedValues = filter[axis];\n if (!allowedValues || !(axis in adjustedDefaults)) continue;\n\n const allowedSet = new Set(allowedValues);\n const currentDefault = (adjustedDefaults as Record)[axis];\n if (currentDefault && !allowedSet.has(currentDefault)) {\n delete (adjustedDefaults as Record)[axis];\n }\n }\n result.defaultVariants = adjustedDefaults;\n }\n\n return result;\n}\n\nexport function createUseRecipe<\n Name extends string,\n const Config extends RecipeConfig,\n>(name: Name, defaults: Config, setup?: (s: Styleframe) => void) {\n type Variants = NonNullable;\n\n return function useRecipe = {}>(\n s: Styleframe,\n options?: DeepPartial> & { filter?: F },\n ): Recipe> {\n if (setup) {\n setup(s);\n }\n\n const { filter, ...configOverrides } = options ?? {};\n const merged = defu(configOverrides, defaults) as RecipeConfig;\n const filtered = filter ? applyFilter(merged, filter) : merged;\n const recipe = s.recipe({\n name,\n ...filtered,\n }) as Recipe>;\n\n return recipe;\n };\n}", + "size": 4213, + "priority": 69 + }, + { + "path": "theme/src/utils/createUseVariable.ts", + "content": "import {\n isKeyReferenceValue,\n type DeclarationsCallbackContext,\n type TokenValue,\n type Variable,\n} from \"@styleframe/core\";\nimport type { CamelCase } from \"scule\";\nimport { camelCase } from \"scule\";\nimport type { ExportKeys } from \"../types\";\n\nexport function createUseVariable<\n PropertyName extends string,\n PropertyType = TokenValue,\n Delimiter extends string = \".\",\n Defaults extends Record = Record,\n MergeDefaults extends boolean = false,\n>(\n propertyName: PropertyName,\n {\n defaults,\n mergeDefaults = false as MergeDefaults,\n transform = (value) => value as TokenValue,\n delimiter = \".\" as Delimiter,\n }: {\n defaults?: Defaults;\n mergeDefaults?: MergeDefaults;\n transform?: (value: PropertyType) => TokenValue;\n delimiter?: Delimiter;\n } = {},\n) {\n type WithDefaults = MergeDefaults extends true ? Defaults & T : T;\n\n return function useVariable<\n Context extends DeclarationsCallbackContext = DeclarationsCallbackContext,\n T extends Record = Defaults,\n >(\n s: Context,\n tokens?: T,\n { default: isDefault = true }: { default?: boolean } = {},\n ): ExportKeys, Delimiter> {\n const result: Record> = {};\n\n const resolvedTokens = mergeDefaults\n ? ({ ...defaults, ...tokens } as T)\n : ((tokens ?? defaults ?? {}) as T);\n const pairs = Object.entries(resolvedTokens);\n\n pairs.sort(([_aKey, aValue], [_bKey, bValue]) => {\n if (isKeyReferenceValue(aValue)) return 1;\n if (isKeyReferenceValue(bValue)) return -1;\n return 0;\n });\n\n const createVariableName = (key: string) =>\n `${propertyName}${key === \"default\" ? \"\" : `${delimiter}${key}`}` as const;\n\n for (const [key, value] of pairs) {\n const variableName = createVariableName(key);\n const exportName: CamelCase =\n camelCase(variableName);\n\n const variableValue = isKeyReferenceValue(value)\n ? (value as unknown as TokenValue)\n : transform(value);\n\n result[exportName] = s.variable(variableName, variableValue, {\n default: isDefault,\n });\n }\n\n return result as ExportKeys, Delimiter>;\n };\n}", + "size": 2509, + "priority": 54 + }, + { + "path": "theme/src/utils/createUseSpacingUtility.ts", + "content": "import type {\n ModifierFactory,\n Styleframe,\n TokenValue,\n UtilityCallbackFn,\n UtilityCreatorFn,\n Variable,\n} from \"@styleframe/core\";\nimport { createMultiplierAutogenerate } from \"./createMultiplierAutogenerate\";\nimport type { UseUtilityOptions } from \"./createUseUtility\";\n\nexport interface CreateUseSpacingUtilityOptions<\n Defaults extends Record,\n> {\n defaults?: Defaults;\n mergeDefaults?: boolean;\n baseVariable?: string | Variable;\n fallback?: string;\n namespace?: string;\n}\n\nexport function createUseSpacingUtility<\n UtilityName extends string,\n Defaults extends Record = Record,\n>(\n utilityName: UtilityName,\n factory: UtilityCallbackFn,\n options: CreateUseSpacingUtilityOptions = {},\n) {\n const {\n defaults,\n mergeDefaults = false,\n baseVariable = \"spacing\",\n fallback = \"1rem\",\n namespace,\n } = options;\n\n return function useSpacingUtility<\n T extends Record = Defaults,\n >(\n s: Styleframe,\n values?: T,\n modifiers?: (ModifierFactory | ModifierFactory[])[],\n utilityOptions?: UseUtilityOptions,\n ): UtilityCreatorFn {\n const resolvedName = utilityOptions?.name ?? utilityName;\n\n const autogenerate = createMultiplierAutogenerate({\n s,\n baseVariable,\n fallback: typeof baseVariable === \"string\" ? fallback : undefined,\n namespace,\n });\n\n const createUtility = s.utility(resolvedName, factory, {\n autogenerate,\n namespace,\n });\n\n const resolvedValues = mergeDefaults\n ? { ...defaults, ...values }\n : (values ?? defaults);\n\n if (resolvedValues && Object.keys(resolvedValues).length > 0) {\n createUtility(resolvedValues as Record, modifiers);\n }\n\n return createUtility;\n };\n}", + "size": 1992, + "priority": 36 + }, + { + "path": "theme/src/utils/createUseUtility.ts", + "content": "import type {\n ModifierFactory,\n Styleframe,\n TokenValue,\n UtilityCallbackFn,\n UtilityCreatorFn,\n} from \"@styleframe/core\";\n\nexport interface UseUtilityOptions {\n name?: string;\n}\n\nexport interface CreateUseUtilityOptions<\n Defaults extends Record,\n> {\n defaults?: Defaults;\n mergeDefaults?: boolean;\n namespace?: string | string[];\n}\n\nexport function createUseUtility<\n UtilityName extends string,\n Defaults extends Record = Record,\n>(\n utilityName: UtilityName,\n factory: UtilityCallbackFn,\n options: CreateUseUtilityOptions = {},\n) {\n const { defaults, mergeDefaults = false, namespace } = options;\n\n return function useUtility = Defaults>(\n s: Styleframe,\n values?: T,\n modifiers?: (ModifierFactory | ModifierFactory[])[],\n utilityOptions?: UseUtilityOptions,\n ): UtilityCreatorFn {\n const resolvedName = utilityOptions?.name ?? utilityName;\n const createUtility = s.utility(resolvedName, factory, { namespace });\n\n const resolvedValues = mergeDefaults\n ? { ...defaults, ...values }\n : (values ?? defaults);\n\n if (resolvedValues && Object.keys(resolvedValues).length > 0) {\n createUtility(resolvedValues as Record, modifiers);\n }\n\n return createUtility;\n };\n}", + "size": 1450, + "priority": 36 + }, + { + "path": "theme/src/utils/oklchGamut.ts", + "content": "import { rgb, oklch as toOklch } from \"culori\";\n\ntype OklchComponents = ReturnType;\n\nexport function parseOklch(value: string): OklchComponents | undefined {\n // ...\n}\n\nfunction isInSrgbGamut(l: number, c: number, h: number): boolean {\n // ...\n}\n\nconst BINARY_SEARCH_ITERATIONS = 20;\nconst MAX_CHROMA = 0.4;\n\nexport function findMaxChroma(l: number, h: number): number {\n // ...\n}\n\nconst ACHROMATIC_THRESHOLD = 0.001;\n\nexport function computeLightnessColor(\n baseL: number,\n baseC: number,\n baseH: number,\n baseAlpha: number,\n targetL: number,\n): string {\n let scaledC: number;\n\n if (baseC < ACHROMATIC_THRESHOLD) {\n scaledC = 0;\n } else {\n const maxChromaAtBase = findMaxChroma(baseL, baseH);\n const maxChromaAtTarget = findMaxChroma(targetL, baseH);\n\n if (maxChromaAtBase < ACHROMATIC_THRESHOLD) {\n scaledC = 0;\n } else {\n scaledC = baseC * (maxChromaAtTarget / maxChromaAtBase);\n scaledC = Math.min(scaledC, maxChromaAtTarget);\n }\n }\n\n return formatOklch(targetL, scaledC, baseH, baseAlpha);\n}\n\nexport function computeRelativeColor(\n baseL: number,\n baseC: number,\n baseH: number,\n baseAlpha: number,\n offset: number,\n): string {\n const targetL = Math.max(0, Math.min(1, baseL + offset));\n return computeLightnessColor(baseL, baseC, baseH, baseAlpha, targetL);\n}\n\nexport function formatOklch(\n l: number,\n c: number,\n h: number,\n alpha: number,\n): string {\n const lStr = round(l, 4);\n const cStr = round(c, 4);\n const hStr = round(h, 2);\n const aStr = round(alpha, 4);\n\n return `oklch(${lStr} ${cStr} ${hStr} / ${aStr})`;\n}\n\nfunction round(value: number, decimals: number): string {\n // ...\n}", + "size": 1768, + "priority": 33 + }, + { + "path": "theme/src/utils/createMultiplierAutogenerate.ts", + "content": "import type {\n Styleframe,\n TokenValue,\n UtilityAutogenerateFn,\n Variable,\n} from \"@styleframe/core\";\nimport { transformUtilityKey } from \"@styleframe/core\";\n\nexport function isNumericValue(value: unknown): value is string | number {\n // ...\n}\n\nexport interface CreateMultiplierAutogenerateOptions {\n s: Styleframe;\n baseVariable: Variable | string;\n fallback?: string;\n replacer?: (key: string) => string;\n namespace?: string | string[];\n}\n\nexport function createMultiplierAutogenerate(\n options: CreateMultiplierAutogenerateOptions,\n): UtilityAutogenerateFn {\n const { s, baseVariable, fallback, replacer = (v) => v, namespace } = options;\n const defaultAutogenerate = transformUtilityKey(\n namespace ? { replacer, namespace } : replacer,\n );\n\n return (value: TokenValue): Record => {\n if (typeof value === \"string\" && value[0] === \"@\") {\n const unprefixed = value.slice(1);\n if (isNumericValue(unprefixed)) {\n const multiplier = unprefixed.trim();\n const calcValue = s.css`calc(${s.ref(baseVariable, fallback)} * ${multiplier})`;\n return {\n [multiplier]: calcValue,\n };\n }\n }\n\n return defaultAutogenerate(value);\n };\n}", + "size": 1327, + "priority": 32 + }, + { + "path": "theme/src/utils/createMultiplierAutogenerate.test.ts", + "content": "import { styleframe } from \"@styleframe/core\";\nimport {\n createMultiplierAutogenerate,\n isNumericValue,\n} from \"./createMultiplierAutogenerate\";\n\ndescribe(\"isNumericValue\", () => {\n describe(\"should return true for\", () => {\n it(\"positive integers\", () => {\n expect(isNumericValue(\"1\")).toBe(true);\n expect(isNumericValue(\"10\")).toBe(true);\n expect(isNumericValue(\"100\")).toBe(true);\n expect(isNumericValue(1)).toBe(true);\n expect(isNumericValue(10)).toBe(true);\n });\n\n it(\"decimals\", () => {\n expect(isNumericValue(\"1.5\")).toBe(true);\n expect(isNumericValue(\"0.5\")).toBe(true);\n expect(isNumericValue(\".5\")).toBe(true);\n expect(isNumericValue(\"1.25\")).toBe(true);\n expect(isNumericValue(1.5)).toBe(true);\n expect(isNumericValue(0.5)).toBe(true);\n });\n\n it(\"negative numbers\", () => {\n expect(isNumericValue(\"-1\")).toBe(true);\n expect(isNumericValue(\"-1.5\")).toBe(true);\n expect(isNumericValue(\"-0.5\")).toBe(true);\n expect(isNumericValue(\"-.5\")).toBe(true);\n expect(isNumericValue(-1)).toBe(true);\n expect(isNumericValue(-1.5)).toBe(true);\n });\n\n it(\"zero\", () => {\n expect(isNumericValue(\"0\")).toBe(true);\n expect(isNumericValue(0)).toBe(true);\n });\n\n it(\"numbers with whitespace\", () => {\n expect(isNumericValue(\" 1 \")).toBe(true);\n expect(isNumericValue(\" 1.5 \")).toBe(true);\n });\n });\n\n describe(\"should return false for\", () => {\n it(\"named tokens\", () => {\n expect(isNumericValue(\"sm\")).toBe(false);\n expect(isNumericValue(\"md\")).toBe(false);\n expect(isNumericValue(\"primary\")).toBe(false);\n });\n\n it(\"CSS units\", () => {\n expect(isNumericValue(\"1rem\")).toBe(false);\n expect(isNumericValue(\"1px\")).toBe(false);\n expect(isNumericValue(\"1em\")).toBe(false);\n expect(isNumericValue(\"100%\")).toBe(false);\n });\n\n it(\"CSS keywords\", () => {\n expect(isNumericValue(\"auto\")).toBe(false);\n expect(isNumericValue(\"inherit\")).toBe(false);\n expect(isNumericValue(\"initial\")).toBe(false);\n });\n\n it(\"variable references\", () => {\n expect(isNumericValue(\"@spacing\")).toBe(false);\n expect(isNumericValue(\"@spacing.sm\")).toBe(false);\n });\n\n it(\"other types\", () => {\n expect(isNumericValue(null)).toBe(false);\n expect(isNumericValue(undefined)).toBe(false);\n expect(isNumericValue({})).toBe(false);\n expect(isNumericValue([])).toBe(false);\n expect(isNumericValue(true)).toBe(false);\n });\n\n it(\"empty values\", () => {\n expect(isNumericValue(\"\")).toBe(false);\n expect(isNumericValue(\" \")).toBe(false);\n });\n\n it(\"invalid number formats\", () => {\n expect(isNumericValue(\"1.2.3\")).toBe(false);\n expect(isNumericValue(\"1-2\")).toBe(false);\n expect(isNumericValue(\"1+2\")).toBe(false);\n });\n });\n});\n\ndescribe(\"createMultiplierAutogenerate\", () => {\n it(\"should generate calc expression for @-prefixed integer multipliers\", () => {\n const s = styleframe();\n const autogenerate = createMultiplierAutogenerate({\n s,\n baseVariable: \"spacing\",\n });\n\n const result = autogenerate(\"@2\");\n expect(result).toHaveProperty(\"2\");\n expect(result[\"2\"]).toEqual({\n type: \"css\",\n value: [\n \"calc(\",\n { type: \"reference\", name: \"spacing\", fallback: undefined },\n \" * \",\n \"2\",\n \")\",\n ],\n });\n });\n\n it(\"should generate calc expression for @-prefixed decimal multipliers\", () => {\n const s = styleframe();\n const autogenerate = createMultiplierAutogenerate({\n s,\n baseVariable: \"spacing\",\n });\n\n const result = autogenerate(\"@1.5\");\n expect(result).toHaveProperty(\"1.5\");\n expect(result[\"1.5\"]).toEqual({\n type: \"css\",\n value: [\n \"calc(\",\n { type: \"reference\", name: \"spacing\", fallback: undefined },\n \" * \",\n \"1.5\",\n \")\",\n ],\n });\n });\n\n it(\"should handle @-prefixed negative multipliers\", () => {\n const s = styleframe();\n const autogenerate = createMultiplierAutogenerate({\n s,\n baseVariable: \"spacing\",\n });\n\n const result = autogenerate(\"@-1\");\n expect(result).toHaveProperty(\"-1\");\n expect(result[\"-1\"]).toEqual({\n type: \"css\",\n value: [\n \"calc(\",\n { type: \"reference\", name: \"spacing\", fallback: undefined },\n \" * \",\n \"-1\",\n \")\",\n ],\n });\n });\n\n it(\"should treat plain numeric strings as arbitrary values\", () => {\n const s = styleframe();\n const autogenerate = createMultiplierAutogenerate({\n s,\n baseVariable: \"spacing\",\n });\n\n const result = autogenerate(\"1.5\");\n expect(result).toHaveProperty(\"[1.5]\");\n expect(result[\"[1.5]\"]).toBe(\"1.5\");\n });\n\n it(\"should treat number type values as arbitrary values\", () => {\n const s = styleframe();\n const autogenerate = createMultiplierAutogenerate({\n s,\n baseVariable: \"spacing\",\n });\n\n const result = autogenerate(1.5);\n expect(result).toHaveProperty(\"[1.5]\");\n });\n\n it(\"should trim whitespace from @-prefixed numeric values\", () => {\n const s = styleframe();\n const autogenerate = createMultiplierAutogenerate({\n s,\n baseVariable: \"spacing\",\n });\n\n const result = autogenerate(\"@ 1.5 \");\n expect(result).toHaveProperty(\"1.5\");\n expect(result).not.toHaveProperty(\" 1.5 \");\n });\n\n it(\"should treat leading whitespace before @ as arbitrary value\", () => {\n const s = styleframe();\n const autogenerate = createMultiplierAutogenerate({\n s,\n baseVariable: \"spacing\",\n });\n\n const result = autogenerate(\" @1.5\");\n expect(result).toHaveProperty(\"[@1.5]\");\n expect(result[\"[@1.5]\"]).toBe(\" @1.5\");\n });\n\n it(\"should fall back to default behavior for @ variable references\", () => {\n const s = styleframe();\n const autogenerate = createMultiplierAutogenerate({\n s,\n baseVariable: \"spacing\",\n });\n\n const result = autogenerate(\"@spacing.sm\");\n expect(result).toHaveProperty(\"spacing.sm\");\n expect(result[\"spacing.sm\"]).toEqual({\n type: \"reference\",\n name: \"spacing.sm\",\n });\n });\n\n it(\"should fall back to default behavior for Reference objects\", () => {\n const s = styleframe();\n const autogenerate = createMultiplierAutogenerate({\n s,\n baseVariable: \"spacing\",\n });\n\n const ref = { type: \"reference\" as const, name: \"spacing.md\" };\n const result = autogenerate(ref);\n expect(result).toHaveProperty(\"spacing.md\");\n expect(result[\"spacing.md\"]).toBe(ref);\n });\n\n it(\"should fall back to default behavior for arbitrary string values\", () => {\n const s = styleframe();\n const autogenerate = createMultiplierAutogenerate({\n s,\n baseVariable: \"spacing\",\n });\n\n const result = autogenerate(\"auto\");\n expect(result).toHaveProperty(\"[auto]\");\n expect(result[\"[auto]\"]).toBe(\"auto\");\n });\n\n it(\"should use Variable object as base\", () => {\n const s = styleframe();\n const spacingVar = s.variable(\"spacing\", \"1rem\");\n const autogenerate = createMultiplierAutogenerate({\n s,\n baseVariable: spacingVar,\n });\n\n const result = autogenerate(\"@2\");\n expect(result[\"2\"]).toEqual({\n type: \"css\",\n value: [\n \"calc(\",\n { type: \"reference\", name: \"spacing\", fallback: undefined },\n \" * \",\n \"2\",\n \")\",\n ],\n });\n });\n\n it(\"should use custom base variable name\", () => {\n const s = styleframe();\n const autogenerate = createMultiplierAutogenerate({\n s,\n baseVariable: \"custom-spacing\",\n });\n\n const result = autogenerate(\"@1.5\");\n expect(result[\"1.5\"]).toEqual({\n type: \"css\",\n value: [\n \"calc(\",\n { type: \"reference\", name: \"custom-spacing\", fallback: undefined },\n \" * \",\n \"1.5\",\n \")\",\n ],\n });\n });\n\n it(\"should use fallback value when provided\", () => {\n const s = styleframe();\n const autogenerate = createMultiplierAutogenerate({\n s,\n baseVariable: \"spacing\",\n fallback: \"1rem\",\n });\n\n const result = autogenerate(\"@2\");\n expect(result[\"2\"]).toEqual({\n type: \"css\",\n value: [\n \"calc(\",\n { type: \"reference\", name: \"spacing\", fallback: \"1rem\" },\n \" * \",\n \"2\",\n \")\",\n ],\n });\n });\n\n it(\"should apply namespace to non-numeric @ values\", () => {\n const s = styleframe();\n const autogenerate = createMultiplierAutogenerate({\n s,\n baseVariable: \"spacing\",\n namespace: \"spacing\",\n });\n\n const result = autogenerate(\"@sm\");\n expect(result).toEqual({\n sm: {\n type: \"reference\",\n name: \"spacing.sm\",\n },\n });\n });\n\n it(\"should not apply namespace to numeric multiplier values\", () => {\n const s = styleframe();\n const autogenerate = createMultiplierAutogenerate({\n s,\n baseVariable: \"spacing\",\n namespace: \"spacing\",\n });\n\n const result = autogenerate(\"@1.5\");\n expect(result).toHaveProperty(\"1.5\");\n expect(result[\"1.5\"]).toEqual({\n type: \"css\",\n value: [\n \"calc(\",\n { type: \"reference\", name: \"spacing\", fallback: undefined },\n \" * \",\n \"1.5\",\n \")\",\n ],\n });\n });\n\n it(\"should work with both namespace and replacer\", () => {\n const s = styleframe();\n const autogenerate = createMultiplierAutogenerate({\n s,\n baseVariable: \"spacing\",\n namespace: \"spacing\",\n replacer: (key) => key.toUpperCase(),\n });\n\n const result = autogenerate(\"@md\");\n expect(result).toEqual({\n MD: {\n type: \"reference\",\n name: \"spacing.md\",\n },\n });\n });\n\n it(\"should accept custom replacer function for non-multiplier values\", () => {\n const s = styleframe();\n const autogenerate = createMultiplierAutogenerate({\n s,\n baseVariable: \"spacing\",\n replacer: (key) => key.split(\".\").pop() ?? key,\n });\n\n const result = autogenerate(\"@spacing.md\");\n expect(result).toHaveProperty(\"md\");\n expect(result.md).toEqual({\n type: \"reference\",\n name: \"spacing.md\",\n });\n });\n});", + "size": 11786, + "priority": 29 + }, + { + "path": "theme/src/utils/createUseRecipe.test.ts", + "content": "import type { Styleframe } from \"@styleframe/core\";\nimport { styleframe } from \"@styleframe/core\";\nimport { createUseRecipe } from \"./createUseRecipe\";\n\nfunction registerUtilities(s: Styleframe) {\n // ...\n}\n\ndescribe(\"createUseRecipe\", () => {\n it(\"should create a recipe with name and base styles\", () => {\n const useRecipe = createUseRecipe(\"test\", {\n base: { display: \"flex\" },\n });\n const s = styleframe();\n registerUtilities(s);\n const recipe = useRecipe(s);\n\n expect(recipe.type).toBe(\"recipe\");\n expect(recipe.name).toBe(\"test\");\n expect(recipe.base).toEqual({ display: \"flex\" });\n });\n\n it(\"should create a recipe with variants\", () => {\n const useRecipe = createUseRecipe(\"test\", {\n base: { display: \"flex\" },\n variants: {\n size: {\n sm: { fontSize: \"12px\" },\n md: { fontSize: \"14px\" },\n lg: { fontSize: \"16px\" },\n },\n },\n });\n const s = styleframe();\n registerUtilities(s);\n const recipe = useRecipe(s);\n\n expect(recipe.variants).toEqual({\n size: {\n sm: { fontSize: \"12px\" },\n md: { fontSize: \"14px\" },\n lg: { fontSize: \"16px\" },\n },\n });\n });\n\n it(\"should create a recipe with defaultVariants\", () => {\n const useRecipe = createUseRecipe(\"test\", {\n base: {},\n variants: {\n size: {\n sm: { fontSize: \"12px\" },\n md: { fontSize: \"14px\" },\n },\n },\n defaultVariants: { size: \"sm\" },\n });\n const s = styleframe();\n registerUtilities(s);\n const recipe = useRecipe(s);\n\n expect(recipe.defaultVariants).toEqual({ size: \"sm\" });\n });\n\n it(\"should create a recipe with compoundVariants\", () => {\n const useRecipe = createUseRecipe(\"test\", {\n base: {},\n variants: {\n color: { primary: {}, error: {} },\n variant: { solid: {}, outline: {} },\n },\n compoundVariants: [\n {\n match: { color: \"primary\", variant: \"solid\" },\n css: { background: \"blue\" },\n },\n ],\n });\n const s = styleframe();\n registerUtilities(s);\n const recipe = useRecipe(s);\n\n expect(recipe.compoundVariants).toEqual([\n {\n match: { color: \"primary\", variant: \"solid\" },\n css: { background: \"blue\" },\n },\n ]);\n });\n\n it(\"should deep merge options with defaults\", () => {\n const useRecipe = createUseRecipe(\"test\", {\n base: { display: \"flex\", fontSize: \"14px\" },\n variants: {\n size: {\n sm: { fontSize: \"12px\" },\n },\n },\n });\n const s = styleframe();\n registerUtilities(s);\n const recipe = useRecipe(s, {\n base: { fontSize: \"16px\" },\n });\n\n expect(recipe.base).toEqual({ display: \"flex\", fontSize: \"16px\" });\n });\n\n it(\"should work with no options\", () => {\n const useRecipe = createUseRecipe(\"test\", {\n base: { display: \"flex\" },\n });\n const s = styleframe();\n registerUtilities(s);\n const recipe = useRecipe(s);\n\n expect(recipe.name).toBe(\"test\");\n expect(recipe.base).toEqual({ display: \"flex\" });\n });\n});\n\ndescribe(\"createUseRecipe filter\", () => {\n const useRecipe = createUseRecipe(\"test\", {\n base: { display: \"flex\" },\n variants: {\n color: {\n primary: {},\n secondary: {},\n success: {},\n error: {},\n },\n variant: {\n solid: {},\n outline: {},\n soft: {},\n },\n size: {\n sm: { fontSize: \"12px\" },\n md: { fontSize: \"14px\" },\n lg: { fontSize: \"16px\" },\n },\n },\n compoundVariants: [\n {\n match: { color: \"primary\", variant: \"solid\" },\n css: { background: \"blue\" },\n },\n {\n match: { color: \"primary\", variant: \"outline\" },\n css: { borderColor: \"blue\" },\n },\n {\n match: { color: \"error\", variant: \"solid\" },\n css: { background: \"red\" },\n },\n {\n match: { color: \"error\", variant: \"outline\" },\n css: { borderColor: \"red\" },\n },\n {\n match: { color: \"secondary\", variant: \"soft\" },\n css: { background: \"gray\" },\n },\n ],\n defaultVariants: {\n color: \"primary\",\n variant: \"solid\",\n size: \"md\",\n },\n });\n\n describe(\"variants filtering\", () => {\n it(\"should filter a single variant axis\", () => {\n const s = styleframe();\n registerUtilities(s);\n const recipe = useRecipe(s, {\n filter: { color: [\"primary\"] },\n });\n\n expect(Object.keys(recipe.variants!.color)).toEqual([\"primary\"]);\n expect(recipe.variants!.size).toEqual({\n sm: { fontSize: \"12px\" },\n md: { fontSize: \"14px\" },\n lg: { fontSize: \"16px\" },\n });\n expect(Object.keys(recipe.variants!.variant)).toEqual([\n \"solid\",\n \"outline\",\n \"soft\",\n ]);\n });\n\n it(\"should filter multiple variant axes\", () => {\n const s = styleframe();\n registerUtilities(s);\n const recipe = useRecipe(s, {\n filter: {\n color: [\"primary\", \"error\"],\n size: [\"sm\", \"md\"],\n },\n });\n\n expect(Object.keys(recipe.variants!.color)).toEqual([\"primary\", \"error\"]);\n expect(Object.keys(recipe.variants!.size)).toEqual([\"sm\", \"md\"]);\n expect(Object.keys(recipe.variants!.variant)).toEqual([\n \"solid\",\n \"outline\",\n \"soft\",\n ]);\n });\n\n it(\"should remove all values when filter is an empty array\", () => {\n const s = styleframe();\n registerUtilities(s);\n const recipe = useRecipe(s, {\n filter: { color: [] },\n });\n\n expect(Object.keys(recipe.variants!.color)).toEqual([]);\n });\n\n it(\"should ignore filter axes that do not exist in variants\", () => {\n const s = styleframe();\n registerUtilities(s);\n const recipe = useRecipe(s, {\n filter: { nonexistent: [] } as any,\n });\n\n expect(Object.keys(recipe.variants!.color)).toEqual([\n \"primary\",\n \"secondary\",\n \"success\",\n \"error\",\n ]);\n });\n\n it(\"should ignore filter values that do not exist in a variant axis\", () => {\n const s = styleframe();\n registerUtilities(s);\n const recipe = useRecipe(s, {\n filter: { color: [\"primary\", \"nonexistent\" as any] },\n });\n\n expect(Object.keys(recipe.variants!.color)).toEqual([\"primary\"]);\n });\n });\n\n describe(\"compoundVariants pruning\", () => {\n it(\"should prune compoundVariants referencing excluded values\", () => {\n const s = styleframe();\n registerUtilities(s);\n const recipe = useRecipe(s, {\n filter: { color: [\"primary\"] },\n });\n\n expect(recipe.compoundVariants).toEqual([\n {\n match: { color: \"primary\", variant: \"solid\" },\n css: { background: \"blue\" },\n },\n {\n match: { color: \"primary\", variant: \"outline\" },\n css: { borderColor: \"blue\" },\n },\n ]);\n });\n\n it(\"should prune compoundVariants when filtering multiple axes\", () => {\n const s = styleframe();\n registerUtilities(s);\n const recipe = useRecipe(s, {\n filter: {\n color: [\"primary\"],\n variant: [\"solid\"],\n },\n });\n\n expect(recipe.compoundVariants).toEqual([\n {\n match: { color: \"primary\", variant: \"solid\" },\n css: { background: \"blue\" },\n },\n ]);\n });\n\n it(\"should keep compoundVariants when their match values are all included\", () => {\n const s = styleframe();\n registerUtilities(s);\n const recipe = useRecipe(s, {\n filter: {\n color: [\"primary\", \"error\"],\n variant: [\"solid\", \"outline\"],\n },\n });\n\n expect(recipe.compoundVariants).toEqual([\n {\n match: { color: \"primary\", variant: \"solid\" },\n css: { background: \"blue\" },\n },\n {\n match: { color: \"primary\", variant: \"outline\" },\n css: { borderColor: \"blue\" },\n },\n {\n match: { color: \"error\", variant: \"solid\" },\n css: { background: \"red\" },\n },\n {\n match: { color: \"error\", variant: \"outline\" },\n css: { borderColor: \"red\" },\n },\n ]);\n });\n\n it(\"should remove all compoundVariants when filter is an empty array\", () => {\n const s = styleframe();\n registerUtilities(s);\n const recipe = useRecipe(s, {\n filter: { color: [] },\n });\n\n expect(recipe.compoundVariants).toEqual([]);\n });\n\n it(\"should keep compoundVariants for unfiltered axes\", () => {\n const s = styleframe();\n registerUtilities(s);\n const recipe = useRecipe(s, {\n filter: { size: [\"sm\"] },\n });\n\n expect(recipe.compoundVariants).toHaveLength(5);\n });\n });\n\n describe(\"defaultVariants adjustment\", () => {\n it(\"should keep defaultVariants when the default is included\", () => {\n const s = styleframe();\n registerUtilities(s);\n const recipe = useRecipe(s, {\n filter: { color: [\"primary\"] },\n });\n\n expect(recipe.defaultVariants?.color).toBe(\"primary\");\n });\n\n it(\"should remove defaultVariants when the default is excluded\", () => {\n const s = styleframe();\n registerUtilities(s);\n const recipe = useRecipe(s, {\n filter: { color: [\"error\"] },\n });\n\n expect(recipe.defaultVariants?.color).toBeUndefined();\n expect(recipe.defaultVariants?.variant).toBe(\"solid\");\n expect(recipe.defaultVariants?.size).toBe(\"md\");\n });\n\n it(\"should remove defaultVariants when filter is an empty array\", () => {\n const s = styleframe();\n registerUtilities(s);\n const recipe = useRecipe(s, {\n filter: { color: [] },\n });\n\n expect(recipe.defaultVariants?.color).toBeUndefined();\n });\n\n it(\"should not affect defaultVariants for unfiltered axes\", () => {\n const s = styleframe();\n registerUtilities(s);\n const recipe = useRecipe(s, {\n filter: { color: [\"primary\"] },\n });\n\n expect(recipe.defaultVariants?.variant).toBe(\"solid\");\n expect(recipe.defaultVariants?.size).toBe(\"md\");\n });\n });\n\n describe(\"no-op cases\", () => {\n it(\"should not filter when filter is not provided\", () => {\n const s = styleframe();\n registerUtilities(s);\n const recipe = useRecipe(s);\n\n expect(Object.keys(recipe.variants!.color)).toEqual([\n \"primary\",\n \"secondary\",\n \"success\",\n \"error\",\n ]);\n expect(recipe.compoundVariants).toHaveLength(5);\n });\n\n it(\"should not filter when filter is an empty object\", () => {\n const s = styleframe();\n registerUtilities(s);\n const recipe = useRecipe(s, { filter: {} });\n\n expect(Object.keys(recipe.variants!.color)).toEqual([\n \"primary\",\n \"secondary\",\n \"success\",\n \"error\",\n ]);\n expect(recipe.compoundVariants).toHaveLength(5);\n });\n });\n\n describe(\"filter combined with config overrides\", () => {\n it(\"should apply overrides before filtering\", () => {\n const s = styleframe();\n registerUtilities(s);\n const recipe = useRecipe(s, {\n base: { display: \"inline-flex\" },\n filter: { color: [\"primary\"] },\n });\n\n expect(recipe.base).toEqual({ display: \"inline-flex\" });\n expect(Object.keys(recipe.variants!.color)).toEqual([\"primary\"]);\n });\n });\n});", + "size": 13652, + "priority": 26 + }, + { + "path": "theme/src/utils/createUseSpacingUtility.test.ts", + "content": "import type { Utility } from \"@styleframe/core\";\nimport { isUtility, styleframe } from \"@styleframe/core\";\nimport { consumeCSS } from \"@styleframe/transpiler\";\nimport { createUseSpacingUtility } from \"./createUseSpacingUtility\";\n\ndescribe(\"createUseSpacingUtility\", () => {\n it(\"should return a function\", () => {\n const useMargin = createUseSpacingUtility(\"margin\", ({ value }) => ({\n margin: value,\n }));\n expect(typeof useMargin).toBe(\"function\");\n });\n\n it(\"should create a composable with multiplier support\", () => {\n const useMargin = createUseSpacingUtility(\"margin\", ({ value }) => ({\n margin: value,\n }));\n const s = styleframe();\n s.variable(\"spacing\", \"1rem\");\n\n const createMargin = useMargin(s);\n createMargin([\"@1.5\", \"@2\", \"@-1\"]);\n\n const utilities = s.root.children.filter(\n (u): u is Utility => isUtility(u) && u.name === \"margin\",\n );\n expect(utilities).toHaveLength(3);\n });\n\n it(\"should generate correct calc CSS output for decimal multipliers\", () => {\n const useMargin = createUseSpacingUtility(\"margin\", ({ value }) => ({\n margin: value,\n }));\n const s = styleframe();\n s.variable(\"spacing\", \"1rem\");\n\n const createMargin = useMargin(s);\n createMargin([\"@1.5\"]);\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toContain(\"._margin\\\\:1\\\\.5 {\");\n expect(css).toContain(\"margin: calc(var(--spacing, 1rem) * 1.5);\");\n });\n\n it(\"should generate correct calc CSS output for integer multipliers\", () => {\n const useMargin = createUseSpacingUtility(\"margin\", ({ value }) => ({\n margin: value,\n }));\n const s = styleframe();\n s.variable(\"spacing\", \"1rem\");\n\n const createMargin = useMargin(s);\n createMargin([\"@2\"]);\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toContain(\"._margin\\\\:2 {\");\n expect(css).toContain(\"margin: calc(var(--spacing, 1rem) * 2);\");\n });\n\n it(\"should generate correct calc CSS output for negative multipliers\", () => {\n const useMargin = createUseSpacingUtility(\"margin\", ({ value }) => ({\n margin: value,\n }));\n const s = styleframe();\n s.variable(\"spacing\", \"1rem\");\n\n const createMargin = useMargin(s);\n createMargin([\"@-1\"]);\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toContain(\"._margin\\\\:-1 {\");\n expect(css).toContain(\"margin: calc(var(--spacing, 1rem) * -1);\");\n });\n\n it(\"should work with named values alongside multipliers\", () => {\n const useMargin = createUseSpacingUtility(\"margin\", ({ value }) => ({\n margin: value,\n }));\n const s = styleframe();\n s.variable(\"spacing\", \"1rem\");\n\n useMargin(s, { sm: \"0.5rem\", md: \"1rem\" });\n const createMargin = s.root.utilities[0]?.create;\n createMargin?.([\"@1.5\", \"@2\"]);\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toContain(\"._margin\\\\:sm {\");\n expect(css).toContain(\"margin: 0.5rem;\");\n expect(css).toContain(\"._margin\\\\:1\\\\.5 {\");\n expect(css).toContain(\"margin: calc(var(--spacing, 1rem) * 1.5);\");\n });\n\n it(\"should allow custom base variable\", () => {\n const useGap = createUseSpacingUtility(\n \"gap\",\n ({ value }) => ({ gap: value }),\n { baseVariable: \"gap-base\" },\n );\n const s = styleframe();\n s.variable(\"gap-base\", \"0.5rem\");\n\n const createGap = useGap(s);\n createGap([\"@2\"]);\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toContain(\"gap: calc(var(--gap-base, 1rem) * 2);\");\n });\n\n it(\"should handle Variable object as base variable without fallback\", () => {\n const s = styleframe();\n const spacingVar = s.variable(\"my-spacing\", \"0.5rem\");\n\n const useMargin = createUseSpacingUtility(\n \"margin\",\n ({ value }) => ({ margin: value }),\n { baseVariable: spacingVar },\n );\n\n const createMargin = useMargin(s);\n createMargin([\"@1.5\"]);\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toContain(\"margin: calc(var(--my-spacing) * 1.5);\");\n expect(css).not.toContain(\"margin: calc(var(--my-spacing, \");\n });\n\n it(\"should use fallback value when base variable is not defined\", () => {\n const useMargin = createUseSpacingUtility(\n \"margin\",\n ({ value }) => ({ margin: value }),\n { fallback: \"0.5rem\" },\n );\n const s = styleframe();\n\n const createMargin = useMargin(s);\n createMargin([\"@2\"]);\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toContain(\"margin: calc(var(--spacing, 0.5rem) * 2);\");\n });\n\n it(\"should use default fallback of 1rem\", () => {\n const useMargin = createUseSpacingUtility(\"margin\", ({ value }) => ({\n margin: value,\n }));\n const s = styleframe();\n\n const createMargin = useMargin(s);\n createMargin([\"@2\"]);\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toContain(\"margin: calc(var(--spacing, 1rem) * 2);\");\n });\n\n it(\"should fall back to default behavior for @ references\", () => {\n const useMargin = createUseSpacingUtility(\"margin\", ({ value }) => ({\n margin: value,\n }));\n const s = styleframe();\n s.variable(\"spacing.sm\", \"0.5rem\");\n\n const createMargin = useMargin(s);\n createMargin([\"@spacing.sm\"]);\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toContain(\"._margin\\\\:spacing\\\\.sm {\");\n expect(css).toContain(\"margin: var(--spacing--sm);\");\n });\n\n it(\"should fall back to default behavior for arbitrary values\", () => {\n const useMargin = createUseSpacingUtility(\"margin\", ({ value }) => ({\n margin: value,\n }));\n const s = styleframe();\n\n const createMargin = useMargin(s);\n createMargin([\"auto\"]);\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toContain(\"._margin\\\\:\\\\[auto\\\\] {\");\n expect(css).toContain(\"margin: auto;\");\n });\n\n it(\"should support defaults option\", () => {\n const useMargin = createUseSpacingUtility(\n \"margin\",\n ({ value }) => ({ margin: value }),\n { defaults: { sm: \"0.5rem\", md: \"1rem\" } },\n );\n const s = styleframe();\n\n useMargin(s);\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toContain(\"._margin\\\\:sm {\");\n expect(css).toContain(\"._margin\\\\:md {\");\n });\n\n it(\"should support mergeDefaults option\", () => {\n const useMargin = createUseSpacingUtility(\n \"margin\",\n ({ value }) => ({ margin: value }),\n { defaults: { sm: \"0.5rem\" }, mergeDefaults: true },\n );\n const s = styleframe();\n\n useMargin(s, { lg: \"1.5rem\" });\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toContain(\"._margin\\\\:sm {\");\n expect(css).toContain(\"._margin\\\\:lg {\");\n });\n\n it(\"should work with modifiers\", () => {\n const useMargin = createUseSpacingUtility(\"margin\", ({ value }) => ({\n margin: value,\n }));\n const s = styleframe();\n s.variable(\"spacing\", \"1rem\");\n\n const hover = s.modifier(\"hover\", ({ declarations }) => ({\n \"&:hover\": declarations,\n }));\n\n const createMargin = useMargin(s, { sm: \"0.5rem\" }, [hover]);\n createMargin([\"@1.5\"], [hover]);\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toContain(\"._margin\\\\:sm {\");\n expect(css).toContain(\"._margin\\\\:1\\\\.5 {\");\n expect(css).toContain(\"._hover\\\\:margin\\\\:sm {\");\n expect(css).toContain(\"._hover\\\\:margin\\\\:1\\\\.5 {\");\n });\n\n it(\"should return the utility creator function\", () => {\n const useMargin = createUseSpacingUtility(\"margin\", ({ value }) => ({\n margin: value,\n }));\n const s = styleframe();\n\n const createMargin = useMargin(s);\n\n expect(typeof createMargin).toBe(\"function\");\n });\n\n it(\"should handle empty values object\", () => {\n const useMargin = createUseSpacingUtility(\"margin\", ({ value }) => ({\n margin: value,\n }));\n const s = styleframe();\n\n useMargin(s, {});\n\n expect(s.root.children).toHaveLength(0);\n });\n\n describe(\"utilityOptions.name override\", () => {\n it(\"should override the utility name\", () => {\n const useMargin = createUseSpacingUtility(\"margin\", ({ value }) => ({\n margin: value,\n }));\n const s = styleframe();\n s.variable(\"spacing\", \"1rem\");\n\n useMargin(s, { sm: \"0.5rem\" }, undefined, { name: \"m\" });\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toContain(\"._m\\\\:sm {\");\n expect(css).not.toContain(\"._margin\");\n });\n\n it(\"should override the utility name for multiplier values\", () => {\n const useMargin = createUseSpacingUtility(\"margin\", ({ value }) => ({\n margin: value,\n }));\n const s = styleframe();\n s.variable(\"spacing\", \"1rem\");\n\n const createMargin = useMargin(s, undefined, undefined, { name: \"m\" });\n createMargin([\"@1.5\"]);\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toContain(\"._m\\\\:1\\\\.5 {\");\n expect(css).not.toContain(\"._margin\");\n });\n\n it(\"should use default name when no override provided\", () => {\n const useMargin = createUseSpacingUtility(\"margin\", ({ value }) => ({\n margin: value,\n }));\n const s = styleframe();\n\n useMargin(s, { sm: \"0.5rem\" });\n\n expect(s.root.utilities[0]?.name).toBe(\"margin\");\n });\n });\n});", + "size": 10146, + "priority": 26 + }, + { + "path": "theme/src/utils/createUseDerivedVariable.ts", + "content": "import type {\n DeclarationsCallbackContext,\n TokenValue,\n Variable,\n} from \"@styleframe/core\";\nimport type { ExportKeys } from \"../types\";\nimport { createUseVariable } from \"./createUseVariable\";\n\nexport function createUseDerivedVariable<\n PropertyType = TokenValue,\n Delimiter extends string = \".\",\n Defaults extends Record = Record,\n>({\n defaults,\n transform,\n delimiter,\n}: {\n defaults?: Defaults;\n transform: (\n value: PropertyType,\n context: { s: DeclarationsCallbackContext; parent: Variable },\n ) => TokenValue;\n delimiter?: Delimiter;\n}) {\n return function useDerivedVariable<\n Context extends DeclarationsCallbackContext,\n Name extends string,\n T extends Record = Defaults,\n >(\n s: Context,\n parent: Variable,\n tokens: T,\n { default: isDefault = true }: { default?: boolean } = {},\n ): ExportKeys {\n return createUseVariable(parent.name, {\n defaults: defaults as Record | undefined,\n transform: (value) => transform(value as PropertyType, { s, parent }),\n delimiter,\n })(s, tokens as Record, {\n default: isDefault,\n }) as ExportKeys;\n };\n}", + "size": 1388, + "priority": 25 + }, + { + "path": "theme/src/utils/mergeElementOptions.ts", + "content": "import type { WithThemes } from \"../types\";\n\nexport function mergeElementOptions(\n defaults: WithThemes,\n overrides: WithThemes,\n): WithThemes {\n const { themes: defaultThemes, ...defaultConfig } = defaults;\n const { themes: userThemes, ...userConfig } = overrides;\n\n const mergedConfig = { ...defaultConfig, ...userConfig } as T;\n\n let mergedThemes: Record | undefined;\n if (defaultThemes || userThemes) {\n mergedThemes = {};\n const themeNames = new Set([\n ...Object.keys(defaultThemes ?? {}),\n ...Object.keys(userThemes ?? {}),\n ]);\n for (const name of themeNames) {\n mergedThemes[name] = {\n ...defaultThemes?.[name],\n ...userThemes?.[name],\n } as T;\n }\n }\n\n return { ...mergedConfig, themes: mergedThemes } as WithThemes;\n}", + "size": 903, + "priority": 25 + }, + { + "path": "theme/src/utils/registerElementThemes.ts", + "content": "import type { DeclarationsCallbackContext, Styleframe } from \"@styleframe/core\";\n\nexport function registerElementThemes(\n s: Styleframe,\n themes: Record | undefined,\n applyDesignTokens: (ctx: DeclarationsCallbackContext, config: T) => void,\n): void {\n if (!themes) return;\n for (const [themeName, overrides] of Object.entries(themes)) {\n s.theme(themeName, (ctx) => {\n applyDesignTokens(ctx, overrides);\n });\n }\n}", + "size": 470, + "priority": 25 + }, + { + "path": "theme/src/utils/oklchGamut.test.ts", + "content": "import { describe, expect, it } from \"vitest\";\nimport {\n computeLightnessColor,\n computeRelativeColor,\n findMaxChroma,\n formatOklch,\n parseOklch,\n} from \"./oklchGamut\";\n\ndescribe(\"oklchGamut\", () => {\n describe(\"parseOklch\", () => {\n it(\"should parse hex color strings\", () => {\n const result = parseOklch(\"#007bff\");\n expect(result).toBeDefined();\n expect(result!.l).toBeCloseTo(0.57, 1);\n expect(result!.c).toBeGreaterThan(0);\n expect(result!.h).toBeGreaterThan(0);\n expect(result!.alpha).toBe(1);\n });\n\n it(\"should parse oklch CSS strings\", () => {\n const result = parseOklch(\"oklch(0.5 0.19 258.8 / 1)\");\n expect(result).toBeDefined();\n expect(result!.l).toBeCloseTo(0.5, 2);\n expect(result!.c).toBeCloseTo(0.19, 2);\n expect(result!.h).toBeCloseTo(258.8, 1);\n expect(result!.alpha).toBe(1);\n });\n\n it(\"should parse oklch without alpha\", () => {\n const result = parseOklch(\"oklch(0.5 0.19 258.8)\");\n expect(result).toBeDefined();\n expect(result!.alpha).toBe(1);\n });\n\n it(\"should handle black (achromatic, no hue)\", () => {\n const result = parseOklch(\"#000000\");\n expect(result).toBeDefined();\n expect(result!.l).toBeCloseTo(0, 2);\n expect(result!.c).toBeCloseTo(0, 3);\n });\n\n it(\"should handle white (achromatic, no hue)\", () => {\n const result = parseOklch(\"#ffffff\");\n expect(result).toBeDefined();\n expect(result!.l).toBeCloseTo(1, 2);\n expect(result!.c).toBeCloseTo(0, 3);\n });\n\n it(\"should return undefined for invalid strings\", () => {\n expect(parseOklch(\"not-a-color\")).toBeUndefined();\n });\n });\n\n describe(\"findMaxChroma\", () => {\n it(\"should return near-zero for L=0 (black)\", () => {\n expect(findMaxChroma(0, 240)).toBeLessThan(0.05);\n });\n\n it(\"should return near-zero for L=1 (white)\", () => {\n expect(findMaxChroma(1, 240)).toBeLessThan(0.05);\n });\n\n it(\"should return positive chroma for mid-range lightness\", () => {\n const maxC = findMaxChroma(0.5, 240);\n expect(maxC).toBeGreaterThan(0.05);\n });\n\n it(\"should vary by hue\", () => {\n const maxBlue = findMaxChroma(0.5, 260);\n const maxYellow = findMaxChroma(0.5, 100);\n expect(maxBlue).not.toBeCloseTo(maxYellow, 2);\n });\n });\n\n describe(\"computeLightnessColor\", () => {\n it(\"should produce a valid oklch string\", () => {\n const result = computeLightnessColor(0.55, 0.15, 240, 1, 0.93);\n expect(result).toMatch(/^oklch\\(.+ .+ .+ \\/ .+\\)$/);\n });\n\n it(\"should keep achromatic colors achromatic\", () => {\n const result = computeLightnessColor(0.5, 0, 0, 1, 0.93);\n expect(result).toContain(\" 0 \");\n });\n\n it(\"should reduce chroma at extreme lightness levels\", () => {\n const baseL = 0.55;\n const baseC = 0.15;\n const baseH = 240;\n\n const lightResult = computeLightnessColor(baseL, baseC, baseH, 1, 0.97);\n const midResult = computeLightnessColor(baseL, baseC, baseH, 1, 0.55);\n\n const lightC = Number.parseFloat(lightResult.split(\" \")[1] ?? \"0\");\n const midC = Number.parseFloat(midResult.split(\" \")[1] ?? \"0\");\n\n expect(lightC).toBeLessThan(midC);\n });\n\n it(\"should preserve alpha\", () => {\n const result = computeLightnessColor(0.55, 0.15, 240, 0.5, 0.93);\n expect(result).toContain(\"/ 0.5\");\n });\n\n it(\"should work with known color #007bff at various lightness targets\", () => {\n const parsed = parseOklch(\"#007bff\")!;\n\n const light = computeLightnessColor(\n parsed.l,\n parsed.c,\n parsed.h,\n parsed.alpha,\n 0.93,\n );\n expect(light).toMatch(/^oklch\\(/);\n\n const dark = computeLightnessColor(\n parsed.l,\n parsed.c,\n parsed.h,\n parsed.alpha,\n 0.17,\n );\n expect(dark).toMatch(/^oklch\\(/);\n });\n });\n\n describe(\"computeRelativeColor\", () => {\n it(\"should produce a lighter color with positive offset\", () => {\n const base = parseOklch(\"#007bff\")!;\n const result = computeRelativeColor(\n base.l,\n base.c,\n base.h,\n base.alpha,\n 0.1,\n );\n const resultL = Number.parseFloat(\n result.match(/oklch\\(([0-9.]+)/)?.[1] ?? \"0\",\n );\n expect(resultL).toBeGreaterThan(base.l);\n });\n\n it(\"should produce a darker color with negative offset\", () => {\n const base = parseOklch(\"#007bff\")!;\n const result = computeRelativeColor(\n base.l,\n base.c,\n base.h,\n base.alpha,\n -0.1,\n );\n const resultL = Number.parseFloat(\n result.match(/oklch\\(([0-9.]+)/)?.[1] ?? \"0\",\n );\n expect(resultL).toBeLessThan(base.l);\n });\n\n it(\"should clamp lightness to 0\", () => {\n const result = computeRelativeColor(0.1, 0.15, 240, 1, -0.5);\n const resultL = Number.parseFloat(\n result.match(/oklch\\(([0-9.]+)/)?.[1] ?? \"-1\",\n );\n expect(resultL).toBe(0);\n });\n\n it(\"should clamp lightness to 1\", () => {\n const result = computeRelativeColor(0.9, 0.15, 240, 1, 0.5);\n const resultL = Number.parseFloat(\n result.match(/oklch\\(([0-9.]+)/)?.[1] ?? \"-1\",\n );\n expect(resultL).toBe(1);\n });\n });\n\n describe(\"formatOklch\", () => {\n it(\"should format with consistent precision\", () => {\n const result = formatOklch(0.93, 0.045, 240.93, 1);\n expect(result).toBe(\"oklch(0.93 0.045 240.93 / 1)\");\n });\n\n it(\"should trim trailing zeros\", () => {\n const result = formatOklch(0.5, 0.1, 240, 1);\n expect(result).toBe(\"oklch(0.5 0.1 240 / 1)\");\n });\n });\n});", + "size": 6504, + "priority": 23 + }, + { + "path": "theme/src/values/index.ts", + "content": "export * from \"./borderColor\";\nexport * from \"./borderRadius\";\nexport * from \"./borderStyle\";\nexport * from \"./borderWidth\";\nexport * from \"./boxShadow\";\nexport * from \"./breakpoint\";\nexport * from \"./color\";\nexport * from \"./colorLevel\";\nexport * from \"./colorShade\";\nexport * from \"./colorTint\";\nexport * from \"./duration\";\nexport * from \"./easing\";\nexport * from \"./fontFamily\";\nexport * from \"./fontSize\";\nexport * from \"./fontStyle\";\nexport * from \"./fontWeight\";\nexport * from \"./letterSpacing\";\nexport * from \"./lineHeight\";\nexport * from \"./scale\";\nexport * from \"./scalePowers\";\nexport * from \"./spacing\";\nexport * from \"./zIndex\";\n\nexport * from \"./accessibility\";\nexport * from \"./backgrounds\";\nexport * from \"./blendModes\";\nexport * from \"./borders\";\nexport * from \"./flexboxGrid\";\nexport * from \"./interactivity\";\nexport * from \"./layout\";\nexport * from \"./sizing\";\nexport * from \"./tables\";\nexport * from \"./transforms\";\nexport * from \"./transitions\";\nexport * from \"./typography\";", + "size": 995, + "priority": 98 + }, + { + "path": "theme/src/values/color.ts", + "content": "export const colorValues = {\n\n primary: \"#007A99\",\n secondary: \"#406AD4\",\n success: \"#058059\",\n warning: \"#E8BF2B\",\n error: \"#D22E3E\",\n info: \"#0E76B2\",\n gray: \"#5F7186\",\n white: \"#ffffff\",\n black: \"#000000\",\n\n background: \"#F8FAFC\",\n surface: \"#FFFFFF\",\n\n text: \"#0F172A\",\n \"text-inverted\": \"#F1F5F9\",\n \"text-weak\": \"#334155\",\n \"text-weaker\": \"#475569\",\n \"text-weakest\": \"#64748B\",\n} as const;\n\nexport const darkModeColorValues = {\n\n primary: \"#007A99\",\n secondary: \"#406AD4\",\n success: \"#058059\",\n warning: \"#E8BF2B\",\n error: \"#D22E3E\",\n info: \"#0E76B2\",\n gray: \"#5F7186\",\n\n background: \"#15181E\",\n surface: \"#1C2028\",\n\n text: \"#F1F5F9\",\n \"text-inverted\": \"#0F172A\",\n \"text-weak\": \"#CBD5E1\",\n \"text-weaker\": \"#94A3B8\",\n \"text-weakest\": \"#64748B\",\n};", + "size": 841, + "priority": 44 + }, + { + "path": "theme/src/values/borderColor.ts", + "content": "export const borderColorValues = {\n default: \"@color.gray-200\",\n primary: \"@color.primary\",\n secondary: \"@color.secondary\",\n success: \"@color.success\",\n warning: \"@color.warning\",\n error: \"@color.error\",\n info: \"@color.info\",\n} as const;\n\nexport const darkModeBorderColorValues = {\n default: \"@color.gray-700\",\n} as const;", + "size": 346, + "priority": 32 + }, + { + "path": "theme/src/values/fontSize.ts", + "content": "export const fontSizeValues = {\n default: \"@font-size.md\",\n \"3xs\": \"0.5rem\",\n \"2xs\": \"0.625rem\",\n xs: \"0.75rem\",\n sm: \"0.875rem\",\n md: \"1rem\",\n lg: \"1.125rem\",\n xl: \"1.25rem\",\n \"2xl\": \"1.5rem\",\n \"3xl\": \"1.875rem\",\n \"4xl\": \"2.25rem\",\n} as const;", + "size": 277, + "priority": 29 + }, + { + "path": "theme/src/values/borderRadius.ts", + "content": "export const borderRadiusValues = {\n default: \"@border-radius.md\",\n none: \"0\",\n sm: \"0.125rem\",\n md: \"0.25rem\",\n lg: \"0.5rem\",\n xl: \"0.75rem\",\n \"2xl\": \"1rem\",\n full: \"9999px\",\n} as const;", + "size": 211, + "priority": 26 + }, + { + "path": "theme/src/values/borderWidth.ts", + "content": "export const borderWidthValues = {\n default: \"@border-width.thin\",\n none: \"0\",\n thin: \"thin\",\n medium: \"medium\",\n thick: \"thick\",\n} as const;", + "size": 156, + "priority": 26 + }, + { + "path": "theme/src/values/boxShadow.ts", + "content": "export const boxShadowValues = {\n default: \"@box-shadow.md\",\n none: \"none\",\n xs: \"0 1px 1px oklch(var(--box-shadow-color, 0 0 0) / 0.12), 0 2px 2px -1px oklch(var(--box-shadow-color, 0 0 0) / 0.06)\",\n sm: \"0 1px 2px oklch(var(--box-shadow-color, 0 0 0) / 0.14), 0 3px 6px -1px oklch(var(--box-shadow-color, 0 0 0) / 0.10)\",\n md: \"0 2px 4px oklch(var(--box-shadow-color, 0 0 0) / 0.16), 0 8px 16px -4px oklch(var(--box-shadow-color, 0 0 0) / 0.10)\",\n lg: \"0 4px 8px oklch(var(--box-shadow-color, 0 0 0) / 0.18), 0 16px 24px -8px oklch(var(--box-shadow-color, 0 0 0) / 0.12)\",\n xl: \"0 8px 12px oklch(var(--box-shadow-color, 0 0 0) / 0.20), 0 24px 48px -12px oklch(var(--box-shadow-color, 0 0 0) / 0.14)\",\n \"2xl\":\n \"0 12px 16px oklch(var(--box-shadow-color, 0 0 0) / 0.22), 0 32px 64px -16px oklch(var(--box-shadow-color, 0 0 0) / 0.16)\",\n inner:\n \"inset 0 1px 0 oklch(var(--box-shadow-color, 0 0 0) / 0.08), inset 0 0 0 1px oklch(var(--box-shadow-color, 0 0 0) / 0.06)\",\n ring: \"0 0 0 1px oklch(var(--box-shadow-color, 0 0 0) / 0.12), 0 1px 2px oklch(var(--box-shadow-color, 0 0 0) / 0.08)\",\n} as const;", + "size": 1145, + "priority": 26 + }, + { + "path": "theme/src/values/breakpoint.ts", + "content": "export const breakpointValues = {\n xs: 0,\n sm: 576,\n md: 768,\n lg: 992,\n xl: 1200,\n \"2xl\": 1440,\n} as const;", + "size": 126, + "priority": 26 + }, + { + "path": "theme/src/values/fontFamily.ts", + "content": "export const fontFamilyValues = {\n default: \"@font-family.base\",\n base: \"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif\",\n print: \"'Georgia', 'Times New Roman', 'Times', serif\",\n mono: \"'SFMono-Regular', Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace\",\n} as const;", + "size": 367, + "priority": 26 + }, + { + "path": "theme/src/values/fontStyle.ts", + "content": "export const fontStyleValues = {\n default: \"@font-style.normal\",\n italic: \"italic\",\n oblique: \"oblique\",\n normal: \"normal\",\n inherit: \"inherit\",\n} as const;", + "size": 171, + "priority": 26 + }, + { + "path": "theme/src/values/fontWeight.ts", + "content": "export const fontWeightValues = {\n default: \"@font-weight.normal\",\n extralight: 200,\n light: 300,\n normal: \"normal\",\n medium: 500,\n semibold: 600,\n bold: \"bold\",\n black: 900,\n lighter: \"lighter\",\n bolder: \"bolder\",\n inherit: \"inherit\",\n} as const;", + "size": 280, + "priority": 26 + }, + { + "path": "theme/src/values/letterSpacing.ts", + "content": "export const letterSpacingValues = {\n default: \"@letter-spacing.normal\",\n tighter: \"-0.05em\",\n tight: \"-0.025em\",\n normal: \"normal\",\n wide: \"0.05em\",\n wider: \"0.1em\",\n} as const;", + "size": 196, + "priority": 26 + }, + { + "path": "theme/src/values/lineHeight.ts", + "content": "export const lineHeightValues = {\n default: \"@line-height.normal\",\n tight: 1.2,\n snug: 1.35,\n normal: 1.5,\n relaxed: 1.65,\n loose: 1.9,\n} as const;", + "size": 165, + "priority": 26 + }, + { + "path": "theme/src/values/spacing.ts", + "content": "export const spacingValues = {\n default: \"@spacing.md\",\n \"2xs\": \"0.25rem\",\n xs: \"0.5rem\",\n sm: \"0.75rem\",\n md: \"1rem\",\n lg: \"1.5rem\",\n xl: \"2rem\",\n \"2xl\": \"3rem\",\n \"3xl\": \"4rem\",\n} as const;", + "size": 217, + "priority": 26 + }, + { + "path": "theme/src/values/accessibility.ts", + "content": "export const forcedColorAdjustValues = {\n auto: \"auto\",\n none: \"none\",\n} as const;", + "size": 88, + "priority": 23 + }, + { + "path": "theme/src/values/backgrounds.ts", + "content": "export const backgroundAttachmentValues = {\n fixed: \"fixed\",\n local: \"local\",\n scroll: \"scroll\",\n} as const;\n\nexport const backgroundClipValues = {\n border: \"border-box\",\n padding: \"padding-box\",\n content: \"content-box\",\n text: \"text\",\n} as const;\n\nexport const backgroundOriginValues = {\n border: \"border-box\",\n padding: \"padding-box\",\n content: \"content-box\",\n} as const;\n\nexport const backgroundPositionValues = {\n bottom: \"bottom\",\n center: \"center\",\n left: \"left\",\n \"left-bottom\": \"left bottom\",\n \"left-top\": \"left top\",\n right: \"right\",\n \"right-bottom\": \"right bottom\",\n \"right-top\": \"right top\",\n top: \"top\",\n} as const;\n\nexport const backgroundRepeatValues = {\n repeat: \"repeat\",\n \"no-repeat\": \"no-repeat\",\n \"repeat-x\": \"repeat-x\",\n \"repeat-y\": \"repeat-y\",\n round: \"round\",\n space: \"space\",\n} as const;\n\nexport const backgroundSizeValues = {\n auto: \"auto\",\n cover: \"cover\",\n contain: \"contain\",\n} as const;\n\nexport const backgroundImageValues = {\n none: \"none\",\n \"gradient-to-t\": \"linear-gradient(to top, var(--tw-gradient-stops))\",\n \"gradient-to-tr\": \"linear-gradient(to top right, var(--tw-gradient-stops))\",\n \"gradient-to-r\": \"linear-gradient(to right, var(--tw-gradient-stops))\",\n \"gradient-to-br\":\n \"linear-gradient(to bottom right, var(--tw-gradient-stops))\",\n \"gradient-to-b\": \"linear-gradient(to bottom, var(--tw-gradient-stops))\",\n \"gradient-to-bl\": \"linear-gradient(to bottom left, var(--tw-gradient-stops))\",\n \"gradient-to-l\": \"linear-gradient(to left, var(--tw-gradient-stops))\",\n \"gradient-to-tl\": \"linear-gradient(to top left, var(--tw-gradient-stops))\",\n} as const;", + "size": 1706, + "priority": 23 + }, + { + "path": "theme/src/values/blendModes.ts", + "content": "export const backgroundBlendModeValues = {\n normal: \"normal\",\n multiply: \"multiply\",\n screen: \"screen\",\n overlay: \"overlay\",\n darken: \"darken\",\n lighten: \"lighten\",\n \"color-dodge\": \"color-dodge\",\n \"color-burn\": \"color-burn\",\n \"hard-light\": \"hard-light\",\n \"soft-light\": \"soft-light\",\n difference: \"difference\",\n exclusion: \"exclusion\",\n hue: \"hue\",\n saturation: \"saturation\",\n color: \"color\",\n luminosity: \"luminosity\",\n} as const;\n\nexport const mixBlendModeValues = {\n normal: \"normal\",\n multiply: \"multiply\",\n screen: \"screen\",\n overlay: \"overlay\",\n darken: \"darken\",\n lighten: \"lighten\",\n \"color-dodge\": \"color-dodge\",\n \"color-burn\": \"color-burn\",\n \"hard-light\": \"hard-light\",\n \"soft-light\": \"soft-light\",\n difference: \"difference\",\n exclusion: \"exclusion\",\n hue: \"hue\",\n saturation: \"saturation\",\n color: \"color\",\n luminosity: \"luminosity\",\n \"plus-darker\": \"plus-darker\",\n \"plus-lighter\": \"plus-lighter\",\n} as const;", + "size": 1021, + "priority": 23 + }, + { + "path": "theme/src/values/borderStyle.ts", + "content": "export const borderStyleValues = {\n default: \"@border-style.solid\",\n none: \"none\",\n solid: \"solid\",\n dashed: \"dashed\",\n dotted: \"dotted\",\n double: \"double\",\n groove: \"groove\",\n inset: \"inset\",\n outset: \"outset\",\n} as const;", + "size": 250, + "priority": 23 + }, + { + "path": "theme/src/values/borders.ts", + "content": "export const divideStyleValues = {\n solid: \"solid\",\n dashed: \"dashed\",\n dotted: \"dotted\",\n double: \"double\",\n none: \"none\",\n} as const;\n\nexport const outlineStyleValues = {\n none: \"none\",\n solid: \"solid\",\n dashed: \"dashed\",\n dotted: \"dotted\",\n double: \"double\",\n} as const;", + "size": 303, + "priority": 23 + }, + { + "path": "theme/src/values/colorLevel.ts", + "content": "export const colorLevelValues = {\n 50: 0.97,\n 100: 0.93,\n 150: 0.89,\n 200: 0.85,\n 250: 0.8,\n 300: 0.75,\n 350: 0.7,\n 400: 0.65,\n 450: 0.6,\n 500: 0.55,\n 550: 0.5,\n 600: 0.45,\n 650: 0.4,\n 700: 0.35,\n 750: 0.3,\n 800: 0.25,\n 850: 0.21,\n 900: 0.17,\n 950: 0.12,\n} as const;", + "size": 323, + "priority": 23 + }, + { + "path": "theme/src/values/colorShade.ts", + "content": "export const colorShadeValues = {\n \"shade-50\": 5,\n \"shade-100\": 10,\n \"shade-150\": 15,\n \"shade-200\": 20,\n} as const;", + "size": 127, + "priority": 23 + }, + { + "path": "theme/src/values/colorTint.ts", + "content": "export const colorTintValues = {\n \"tint-50\": 5,\n \"tint-100\": 10,\n \"tint-150\": 15,\n \"tint-200\": 20,\n} as const;", + "size": 122, + "priority": 23 + }, + { + "path": "theme/src/values/flexboxGrid.ts", + "content": "export const flexValues = {\n \"1\": \"1 1 0%\",\n auto: \"1 1 auto\",\n initial: \"0 1 auto\",\n none: \"none\",\n} as const;\n\nexport const flexDirectionValues = {\n row: \"row\",\n \"row-reverse\": \"row-reverse\",\n col: \"column\",\n \"col-reverse\": \"column-reverse\",\n} as const;\n\nexport const flexWrapValues = {\n wrap: \"wrap\",\n \"wrap-reverse\": \"wrap-reverse\",\n nowrap: \"nowrap\",\n} as const;\n\nexport const justifyContentValues = {\n normal: \"normal\",\n start: \"flex-start\",\n end: \"flex-end\",\n center: \"center\",\n between: \"space-between\",\n around: \"space-around\",\n evenly: \"space-evenly\",\n stretch: \"stretch\",\n} as const;\n\nexport const justifyItemsValues = {\n start: \"start\",\n end: \"end\",\n center: \"center\",\n stretch: \"stretch\",\n} as const;\n\nexport const justifySelfValues = {\n auto: \"auto\",\n start: \"start\",\n end: \"end\",\n center: \"center\",\n stretch: \"stretch\",\n} as const;\n\nexport const alignContentValues = {\n normal: \"normal\",\n center: \"center\",\n start: \"flex-start\",\n end: \"flex-end\",\n between: \"space-between\",\n around: \"space-around\",\n evenly: \"space-evenly\",\n baseline: \"baseline\",\n stretch: \"stretch\",\n} as const;\n\nexport const alignItemsValues = {\n start: \"flex-start\",\n end: \"flex-end\",\n center: \"center\",\n baseline: \"baseline\",\n stretch: \"stretch\",\n} as const;\n\nexport const alignSelfValues = {\n auto: \"auto\",\n start: \"flex-start\",\n end: \"flex-end\",\n center: \"center\",\n stretch: \"stretch\",\n baseline: \"baseline\",\n} as const;\n\nexport const placeContentValues = {\n center: \"center\",\n start: \"start\",\n end: \"end\",\n between: \"space-between\",\n around: \"space-around\",\n evenly: \"space-evenly\",\n baseline: \"baseline\",\n stretch: \"stretch\",\n} as const;\n\nexport const placeItemsValues = {\n start: \"start\",\n end: \"end\",\n center: \"center\",\n baseline: \"baseline\",\n stretch: \"stretch\",\n} as const;\n\nexport const placeSelfValues = {\n auto: \"auto\",\n start: \"start\",\n end: \"end\",\n center: \"center\",\n stretch: \"stretch\",\n} as const;\n\nexport const gridAutoFlowValues = {\n row: \"row\",\n col: \"column\",\n dense: \"dense\",\n \"row-dense\": \"row dense\",\n \"col-dense\": \"column dense\",\n} as const;", + "size": 2263, + "priority": 23 + }, + { + "path": "theme/src/values/interactivity.ts", + "content": "export const appearanceValues = {\n none: \"none\",\n auto: \"auto\",\n} as const;\n\nexport const colorSchemeValues = {\n normal: \"normal\",\n light: \"light\",\n dark: \"dark\",\n \"light-dark\": \"light dark\",\n} as const;\n\nexport const cursorValues = {\n auto: \"auto\",\n default: \"default\",\n pointer: \"pointer\",\n wait: \"wait\",\n text: \"text\",\n move: \"move\",\n help: \"help\",\n \"not-allowed\": \"not-allowed\",\n none: \"none\",\n \"context-menu\": \"context-menu\",\n progress: \"progress\",\n cell: \"cell\",\n crosshair: \"crosshair\",\n \"vertical-text\": \"vertical-text\",\n alias: \"alias\",\n copy: \"copy\",\n \"no-drop\": \"no-drop\",\n grab: \"grab\",\n grabbing: \"grabbing\",\n \"all-scroll\": \"all-scroll\",\n \"col-resize\": \"col-resize\",\n \"row-resize\": \"row-resize\",\n \"n-resize\": \"n-resize\",\n \"e-resize\": \"e-resize\",\n \"s-resize\": \"s-resize\",\n \"w-resize\": \"w-resize\",\n \"ne-resize\": \"ne-resize\",\n \"nw-resize\": \"nw-resize\",\n \"se-resize\": \"se-resize\",\n \"sw-resize\": \"sw-resize\",\n \"ew-resize\": \"ew-resize\",\n \"ns-resize\": \"ns-resize\",\n \"nesw-resize\": \"nesw-resize\",\n \"nwse-resize\": \"nwse-resize\",\n \"zoom-in\": \"zoom-in\",\n \"zoom-out\": \"zoom-out\",\n} as const;\n\nexport const pointerEventsValues = {\n none: \"none\",\n auto: \"auto\",\n} as const;\n\nexport const resizeValues = {\n none: \"none\",\n y: \"vertical\",\n x: \"horizontal\",\n both: \"both\",\n} as const;\n\nexport const scrollBehaviorValues = {\n auto: \"auto\",\n smooth: \"smooth\",\n} as const;\n\nexport const touchActionValues = {\n auto: \"auto\",\n none: \"none\",\n \"pan-x\": \"pan-x\",\n \"pan-left\": \"pan-left\",\n \"pan-right\": \"pan-right\",\n \"pan-y\": \"pan-y\",\n \"pan-up\": \"pan-up\",\n \"pan-down\": \"pan-down\",\n \"pinch-zoom\": \"pinch-zoom\",\n manipulation: \"manipulation\",\n} as const;\n\nexport const userSelectValues = {\n none: \"none\",\n text: \"text\",\n all: \"all\",\n auto: \"auto\",\n} as const;\n\nexport const willChangeValues = {\n auto: \"auto\",\n scroll: \"scroll-position\",\n contents: \"contents\",\n transform: \"transform\",\n} as const;\n\nexport const scrollSnapAlignValues = {\n start: \"start\",\n end: \"end\",\n center: \"center\",\n \"align-none\": \"none\",\n} as const;\n\nexport const scrollSnapStopValues = {\n normal: \"normal\",\n always: \"always\",\n} as const;\n\nexport const scrollSnapTypeValues = {\n none: \"none\",\n x: \"x var(--tw-scroll-snap-strictness)\",\n y: \"y var(--tw-scroll-snap-strictness)\",\n both: \"both var(--tw-scroll-snap-strictness)\",\n mandatory: \"mandatory\",\n proximity: \"proximity\",\n} as const;", + "size": 2582, + "priority": 23 + }, + { + "path": "theme/src/values/layout.ts", + "content": "export const displayValues = {\n block: \"block\",\n \"inline-block\": \"inline-block\",\n inline: \"inline\",\n flex: \"flex\",\n \"inline-flex\": \"inline-flex\",\n table: \"table\",\n \"inline-table\": \"inline-table\",\n \"table-caption\": \"table-caption\",\n \"table-cell\": \"table-cell\",\n \"table-column\": \"table-column\",\n \"table-column-group\": \"table-column-group\",\n \"table-footer-group\": \"table-footer-group\",\n \"table-header-group\": \"table-header-group\",\n \"table-row-group\": \"table-row-group\",\n \"table-row\": \"table-row\",\n \"flow-root\": \"flow-root\",\n grid: \"grid\",\n \"inline-grid\": \"inline-grid\",\n contents: \"contents\",\n \"list-item\": \"list-item\",\n hidden: \"none\",\n} as const;\n\nexport const positionValues = {\n static: \"static\",\n fixed: \"fixed\",\n absolute: \"absolute\",\n relative: \"relative\",\n sticky: \"sticky\",\n} as const;\n\nexport const visibilityValues = {\n visible: \"visible\",\n invisible: \"hidden\",\n collapse: \"collapse\",\n} as const;\n\nexport const overflowValues = {\n auto: \"auto\",\n hidden: \"hidden\",\n clip: \"clip\",\n visible: \"visible\",\n scroll: \"scroll\",\n} as const;\n\nexport const overscrollValues = {\n auto: \"auto\",\n contain: \"contain\",\n none: \"none\",\n} as const;\n\nexport const floatValues = {\n start: \"inline-start\",\n end: \"inline-end\",\n right: \"right\",\n left: \"left\",\n none: \"none\",\n} as const;\n\nexport const clearValues = {\n start: \"inline-start\",\n end: \"inline-end\",\n left: \"left\",\n right: \"right\",\n both: \"both\",\n none: \"none\",\n} as const;\n\nexport const isolationValues = {\n isolate: \"isolate\",\n auto: \"auto\",\n} as const;\n\nexport const objectFitValues = {\n contain: \"contain\",\n cover: \"cover\",\n fill: \"fill\",\n none: \"none\",\n \"scale-down\": \"scale-down\",\n} as const;\n\nexport const objectPositionValues = {\n bottom: \"bottom\",\n center: \"center\",\n left: \"left\",\n \"left-bottom\": \"left bottom\",\n \"left-top\": \"left top\",\n right: \"right\",\n \"right-bottom\": \"right bottom\",\n \"right-top\": \"right top\",\n top: \"top\",\n} as const;\n\nexport const aspectRatioValues = {\n auto: \"auto\",\n square: \"1 / 1\",\n video: \"16 / 9\",\n} as const;\n\nexport const boxSizingValues = {\n border: \"border-box\",\n content: \"content-box\",\n} as const;\n\nexport const boxDecorationBreakValues = {\n clone: \"clone\",\n slice: \"slice\",\n} as const;\n\nexport const breakAfterValues = {\n auto: \"auto\",\n avoid: \"avoid\",\n all: \"all\",\n \"avoid-page\": \"avoid-page\",\n page: \"page\",\n left: \"left\",\n right: \"right\",\n column: \"column\",\n} as const;\n\nexport const breakBeforeValues = {\n auto: \"auto\",\n avoid: \"avoid\",\n all: \"all\",\n \"avoid-page\": \"avoid-page\",\n page: \"page\",\n left: \"left\",\n right: \"right\",\n column: \"column\",\n} as const;\n\nexport const breakInsideValues = {\n auto: \"auto\",\n avoid: \"avoid\",\n \"avoid-page\": \"avoid-page\",\n \"avoid-column\": \"avoid-column\",\n} as const;", + "size": 2964, + "priority": 23 + }, + { + "path": "theme/src/values/scale.ts", + "content": "export const scaleValues = {\n default: \"@scale.minor-third\",\n \"minor-second\": 1.067,\n \"major-second\": 1.125,\n \"minor-third\": 1.2,\n \"major-third\": 1.25,\n \"perfect-fourth\": 1.333,\n \"augmented-fourth\": 1.414,\n \"perfect-fifth\": 1.5,\n golden: 1.618,\n} as const;", + "size": 283, + "priority": 23 + }, + { + "path": "theme/src/values/tables.ts", + "content": "export const borderCollapseValues = {\n collapse: \"collapse\",\n separate: \"separate\",\n} as const;\n\nexport const tableLayoutValues = {\n auto: \"auto\",\n fixed: \"fixed\",\n} as const;\n\nexport const captionSideValues = {\n top: \"top\",\n bottom: \"bottom\",\n} as const;", + "size": 273, + "priority": 23 + }, + { + "path": "theme/src/values/transforms.ts", + "content": "export const transformOriginValues = {\n center: \"center\",\n top: \"top\",\n \"top-right\": \"top right\",\n right: \"right\",\n \"bottom-right\": \"bottom right\",\n bottom: \"bottom\",\n \"bottom-left\": \"bottom left\",\n left: \"left\",\n \"top-left\": \"top left\",\n} as const;\n\nexport const transformStyleValues = {\n flat: \"flat\",\n \"3d\": \"preserve-3d\",\n} as const;\n\nexport const backfaceVisibilityValues = {\n visible: \"visible\",\n hidden: \"hidden\",\n} as const;\n\nexport const perspectiveOriginValues = {\n center: \"center\",\n top: \"top\",\n \"top-right\": \"top right\",\n right: \"right\",\n \"bottom-right\": \"bottom right\",\n bottom: \"bottom\",\n \"bottom-left\": \"bottom left\",\n left: \"left\",\n \"top-left\": \"top left\",\n} as const;", + "size": 751, + "priority": 23 + }, + { + "path": "theme/src/values/transitions.ts", + "content": "export const transitionPropertyValues = {\n none: \"none\",\n all: \"all\",\n default:\n \"color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter\",\n colors:\n \"color, background-color, border-color, text-decoration-color, fill, stroke\",\n opacity: \"opacity\",\n shadow: \"box-shadow\",\n transform: \"transform\",\n} as const;\n\nexport const transitionBehaviorValues = {\n normal: \"normal\",\n \"allow-discrete\": \"allow-discrete\",\n} as const;\n\nexport const animationValues = {\n none: \"none\",\n spin: \"spin 1s linear infinite\",\n ping: \"ping 1s cubic-bezier(0, 0, 0.2, 1) infinite\",\n pulse: \"pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite\",\n bounce: \"bounce 1s infinite\",\n} as const;", + "size": 789, + "priority": 23 + }, + { + "path": "theme/src/values/typography.ts", + "content": "export const textAlignValues = {\n left: \"left\",\n center: \"center\",\n right: \"right\",\n justify: \"justify\",\n start: \"start\",\n end: \"end\",\n} as const;\n\nexport const textOverflowValues = {\n truncate: \"ellipsis\",\n \"text-ellipsis\": \"ellipsis\",\n \"text-clip\": \"clip\",\n} as const;\n\nexport const textTransformValues = {\n uppercase: \"uppercase\",\n lowercase: \"lowercase\",\n capitalize: \"capitalize\",\n \"normal-case\": \"none\",\n} as const;\n\nexport const textWrapValues = {\n wrap: \"wrap\",\n nowrap: \"nowrap\",\n balance: \"balance\",\n pretty: \"pretty\",\n} as const;\n\nexport const whitespaceValues = {\n normal: \"normal\",\n nowrap: \"nowrap\",\n pre: \"pre\",\n \"pre-line\": \"pre-line\",\n \"pre-wrap\": \"pre-wrap\",\n \"break-spaces\": \"break-spaces\",\n} as const;\n\nexport const wordBreakValues = {\n normal: \"normal\",\n words: \"break-word\",\n all: \"break-all\",\n keep: \"keep-all\",\n} as const;\n\nexport const overflowWrapValues = {\n normal: \"normal\",\n \"break-word\": \"break-word\",\n anywhere: \"anywhere\",\n} as const;\n\nexport const hyphensValues = {\n none: \"none\",\n manual: \"manual\",\n auto: \"auto\",\n} as const;\n\nexport const verticalAlignValues = {\n baseline: \"baseline\",\n top: \"top\",\n middle: \"middle\",\n bottom: \"bottom\",\n \"text-top\": \"text-top\",\n \"text-bottom\": \"text-bottom\",\n sub: \"sub\",\n super: \"super\",\n} as const;\n\nexport const fontSmoothingValues = {\n antialiased: \"antialiased\",\n \"subpixel-antialiased\": \"auto\",\n} as const;\n\nexport const fontStretchValues = {\n \"ultra-condensed\": \"ultra-condensed\",\n \"extra-condensed\": \"extra-condensed\",\n condensed: \"condensed\",\n \"semi-condensed\": \"semi-condensed\",\n normal: \"normal\",\n \"semi-expanded\": \"semi-expanded\",\n expanded: \"expanded\",\n \"extra-expanded\": \"extra-expanded\",\n \"ultra-expanded\": \"ultra-expanded\",\n} as const;\n\nexport const fontVariantNumericValues = {\n \"normal-nums\": \"normal\",\n ordinal: \"ordinal\",\n \"slashed-zero\": \"slashed-zero\",\n \"lining-nums\": \"lining-nums\",\n \"oldstyle-nums\": \"oldstyle-nums\",\n \"proportional-nums\": \"proportional-nums\",\n \"tabular-nums\": \"tabular-nums\",\n \"diagonal-fractions\": \"diagonal-fractions\",\n \"stacked-fractions\": \"stacked-fractions\",\n} as const;\n\nexport const listStylePositionValues = {\n inside: \"inside\",\n outside: \"outside\",\n} as const;\n\nexport const listStyleTypeValues = {\n none: \"none\",\n disc: \"disc\",\n decimal: \"decimal\",\n} as const;\n\nexport const textDecorationValues = {\n none: \"none\",\n underline: \"underline\",\n overline: \"overline\",\n \"line-through\": \"line-through\",\n} as const;\n\nexport const textDecorationLineValues = {\n underline: \"underline\",\n overline: \"overline\",\n \"line-through\": \"line-through\",\n \"no-underline\": \"none\",\n} as const;\n\nexport const textDecorationStyleValues = {\n solid: \"solid\",\n double: \"double\",\n dotted: \"dotted\",\n dashed: \"dashed\",\n wavy: \"wavy\",\n} as const;", + "size": 2970, + "priority": 23 + }, + { + "path": "theme/src/values/zIndex.ts", + "content": "export const zIndexValues = {\n default: \"@z-index.base\",\n hide: \"-1\",\n base: \"0\",\n dropdown: \"100\",\n sticky: \"200\",\n overlay: \"300\",\n modal: \"400\",\n popover: \"500\",\n toast: \"600\",\n max: \"9999\",\n auto: \"auto\",\n} as const;", + "size": 253, + "priority": 23 + }, + { + "path": "theme/src/values/duration.ts", + "content": "export const durationValues = {\n instant: \"0ms\",\n fastest: \"50ms\",\n faster: \"100ms\",\n fast: \"150ms\",\n normal: \"250ms\",\n slow: \"300ms\",\n slower: \"500ms\",\n slowest: \"1000ms\",\n} as const;", + "size": 208, + "priority": 20 + }, + { + "path": "theme/src/values/easing.ts", + "content": "export const easingValues = {\n linear: \"linear\",\n ease: \"ease\",\n \"ease-in\": \"ease-in\",\n \"ease-out\": \"ease-out\",\n \"ease-in-out\": \"ease-in-out\",\n\n \"ease-in-sine\": \"cubic-bezier(0.47, 0, 0.745, 0.715)\",\n \"ease-out-sine\": \"cubic-bezier(0.39, 0.575, 0.565, 1)\",\n \"ease-in-out-sine\": \"cubic-bezier(0.445, 0.05, 0.55, 0.95)\",\n\n \"ease-in-quad\": \"cubic-bezier(0.55, 0.085, 0.68, 0.53)\",\n \"ease-out-quad\": \"cubic-bezier(0.25, 0.46, 0.45, 0.94)\",\n \"ease-in-out-quad\": \"cubic-bezier(0.455, 0.03, 0.515, 0.955)\",\n\n \"ease-in-cubic\": \"cubic-bezier(0.55, 0.055, 0.675, 0.19)\",\n \"ease-out-cubic\": \"cubic-bezier(0.215, 0.61, 0.355, 1)\",\n \"ease-in-out-cubic\": \"cubic-bezier(0.645, 0.045, 0.355, 1)\",\n\n \"ease-in-quart\": \"cubic-bezier(0.895, 0.03, 0.685, 0.22)\",\n \"ease-out-quart\": \"cubic-bezier(0.165, 0.84, 0.44, 1)\",\n \"ease-in-out-quart\": \"cubic-bezier(0.77, 0, 0.175, 1)\",\n\n \"ease-in-quint\": \"cubic-bezier(0.755, 0.05, 0.855, 0.06)\",\n \"ease-out-quint\": \"cubic-bezier(0.23, 1, 0.32, 1)\",\n \"ease-in-out-quint\": \"cubic-bezier(0.86, 0, 0.07, 1)\",\n\n \"ease-in-expo\": \"cubic-bezier(0.95, 0.05, 0.795, 0.035)\",\n \"ease-out-expo\": \"cubic-bezier(0.19, 1, 0.22, 1)\",\n \"ease-in-out-expo\": \"cubic-bezier(1, 0, 0, 1)\",\n\n \"ease-in-circ\": \"cubic-bezier(0.6, 0.04, 0.98, 0.335)\",\n \"ease-out-circ\": \"cubic-bezier(0.075, 0.82, 0.165, 1)\",\n \"ease-in-out-circ\": \"cubic-bezier(0.785, 0.135, 0.15, 0.86)\",\n\n \"ease-in-back\": \"cubic-bezier(0.6, -0.28, 0.735, 0.045)\",\n \"ease-out-back\": \"cubic-bezier(0.175, 0.885, 0.32, 1.275)\",\n \"ease-in-out-back\": \"cubic-bezier(0.68, -0.55, 0.265, 1.55)\",\n\n spring:\n \"linear(0, 0.0018, 0.0069 1.15%, 0.026 2.3%, 0.0637, 0.1135 5.18%, 0.2229 7.78%, 0.5977 15.84%, 0.7014, 0.7904, 0.8641, 0.9228, 0.9676 28.8%, 1.0032 31.68%, 1.0225, 1.0352 36.29%, 1.0431 38.88%, 1.046 42.05%, 1.0448 44.35%, 1.0407 47.23%, 1.0118 61.63%, 1.0025 69.41%, 0.9981 80.35%, 0.9992 99.94%)\",\n\n bounce:\n \"linear(0, 0.004, 0.016, 0.035, 0.063, 0.098, 0.141 13.6%, 0.25, 0.391, 0.563, 0.765, 1, 0.891 40.9%, 0.848, 0.813, 0.785, 0.766, 0.754, 0.75, 0.754, 0.766, 0.785, 0.813, 0.848, 0.891 68.2%, 1 72.7%, 0.973, 0.953, 0.941, 0.938, 0.941, 0.953, 0.973, 1, 0.988, 0.984, 0.988, 1)\",\n} as const;", + "size": 2268, + "priority": 20 + }, + { + "path": "theme/src/values/scalePowers.ts", + "content": "export const scalePowerValues: readonly number[] = [\n -2, -1, 0, 1, 2, 3, 4, 5,\n] as const;", + "size": 94, + "priority": 20 + }, + { + "path": "theme/src/values/sizing.ts", + "content": "export const widthValues = {\n full: \"100%\",\n screen: \"100vw\",\n auto: \"auto\",\n \"1/2\": \"50%\",\n \"1/3\": \"33.333333%\",\n \"1/4\": \"25%\",\n} as const;\n\nexport const heightValues = {\n full: \"100%\",\n screen: \"100vh\",\n auto: \"auto\",\n \"1/2\": \"50%\",\n \"1/3\": \"33.333333%\",\n \"1/4\": \"25%\",\n} as const;", + "size": 319, + "priority": 20 + }, + { + "path": "engine/core/src/types/index.ts", + "content": "export * from \"./declarations\";\nexport * from \"./options\";\nexport * from \"./tokens\";", + "size": 84, + "priority": 80 + }, + { + "path": "engine/core/src/types/tokens.ts", + "content": "import type {\n DeclarationsBlock,\n DeclarationsCallback,\n DeclarationsCallbackContext,\n} from \"./declarations\";\n\nexport type Variable = {\n type: \"variable\";\n id: string;\n parentId?: string;\n name: Name;\n value: TokenValue;\n};\n\nexport type Reference = {\n type: \"reference\";\n name: Name;\n fallback?: TokenValue;\n};\n\nexport type Selector = {\n type: \"selector\";\n id: string;\n parentId?: string;\n query: string;\n declarations: DeclarationsBlock;\n variables: Variable[];\n children: ContainerChild[];\n _exportName?: string;\n};\n\nexport type AtRule = {\n type: \"at-rule\";\n id: string;\n parentId?: string;\n identifier: string;\n rule: string;\n declarations: DeclarationsBlock;\n variables: Variable[];\n children: ContainerChild[];\n};\n\nexport type CSS = {\n type: \"css\";\n value: TokenValue[];\n};\n\nexport type UtilityAutogenerateFn = (\n value: TokenValue,\n) => Record;\n\nexport type UtilityFactory = {\n type: \"utility\";\n name: Name;\n factory: UtilityCallbackFn;\n values: Array<{ key: string; value: TokenValue; modifiers: string[] }>;\n autogenerate: UtilityAutogenerateFn;\n namespace?: string | string[];\n create: UtilityCreatorFn;\n};\n\nexport type Utility = {\n type: \"utility\";\n id: string;\n parentId?: string;\n name: Name;\n value: string;\n declarations: DeclarationsBlock;\n variables: Variable[];\n children: ContainerChild[];\n modifiers: string[];\n};\n\nexport type UtilityCallbackFn = DeclarationsCallback<\n DeclarationsCallbackContext & {\n value: TokenValue;\n }\n>;\n\nexport type UtilityCreatorFn = (\n values: Record | TokenValue[],\n modifiers?: (ModifierFactory | ModifierFactory[])[],\n) => void;\n\nexport type ModifierCallbackFn = DeclarationsCallback<\n DeclarationsCallbackContext &\n Pick\n>;\n\nexport type ModifierFactory = {\n type: \"modifier\";\n key: string[];\n factory: ModifierCallbackFn;\n};\n\nexport type ModifierDeclarationsBlock = Record;\n\nexport type VariantDeclarationsValue = TokenValue | ModifierDeclarationsBlock;\n\nexport type VariantDeclarationsBlock = Record;\n\nexport type VariantsBase = Record<\n string,\n Record\n>;\n\nexport type RuntimeModifierDeclarationsBlock = Record<\n string,\n PrimitiveTokenValue\n>;\n\nexport type RuntimeVariantDeclarationsValue =\n | PrimitiveTokenValue\n | RuntimeModifierDeclarationsBlock;\n\nexport type RuntimeVariantDeclarationsBlock = Record<\n string,\n RuntimeVariantDeclarationsValue\n>;\n\nexport type RecipeRuntime = {\n base?: RuntimeVariantDeclarationsBlock;\n variants?: {\n [K in keyof Variants]?: {\n [O in keyof Variants[K]]?: RuntimeVariantDeclarationsBlock;\n };\n };\n defaultVariants?: {\n [K in keyof Variants]?: keyof Variants[K] & string;\n };\n compoundVariants?: Array<{\n match: {\n [K in keyof Variants]?: keyof Variants[K] & string;\n };\n css?: RuntimeVariantDeclarationsBlock;\n className?: string;\n }>;\n};\n\nexport type Recipe<\n Name extends string = string,\n Variants extends VariantsBase = VariantsBase,\n> = {\n type: \"recipe\";\n name: Name;\n base?: VariantDeclarationsBlock;\n variants?: Variants;\n defaultVariants?: {\n [K in keyof Variants]?: keyof Variants[K] & string;\n };\n compoundVariants?: Array<{\n match: {\n [K in keyof Variants]?: keyof Variants[K] & string;\n };\n css?: VariantDeclarationsBlock;\n className?: string;\n }>;\n _runtime?: RecipeRuntime;\n _exportName?: string;\n};\n\nexport type PrimitiveTokenValue = number | string | boolean | null | undefined;\n\nexport type TokenValue =\n | PrimitiveTokenValue\n | Reference\n | CSS\n | Array;\n\nexport type TokenType =\n | Variable[\"type\"]\n | Reference[\"type\"]\n | Selector[\"type\"]\n | AtRule[\"type\"]\n | CSS[\"type\"]\n | Utility[\"type\"]\n | ModifierFactory[\"type\"]\n | Recipe[\"type\"]\n | Theme[\"type\"]\n | Root[\"type\"];\n\nexport type Container = {\n id: string;\n parentId?: string;\n children: ContainerChild[];\n variables: Variable[];\n declarations: DeclarationsBlock;\n};\n\nexport type ContainerInput = Pick<\n Container,\n \"declarations\" | \"variables\" | \"children\"\n>;\n\nexport type ContainerChild = Variable | Selector | AtRule | Utility;\n\nexport type Theme = {\n type: \"theme\";\n id: string;\n parentId?: string;\n name: string;\n declarations: DeclarationsBlock;\n variables: Variable[];\n children: ContainerChild[];\n};\n\nexport type Root = {\n type: \"root\";\n id: string;\n parentId?: string;\n declarations: DeclarationsBlock;\n utilities: UtilityFactory[];\n modifiers: ModifierFactory[];\n recipes: Recipe[];\n variables: Variable[];\n children: ContainerChild[];\n themes: Theme[];\n _registry: Map;\n};", + "size": 5269, + "priority": 50 + }, + { + "path": "engine/core/src/types/options.ts", + "content": "export type VariableNameFn = (options: { name: string }) => string;\n\nexport interface UtilitySelectorOptions {\n name: string;\n value: string;\n modifiers: string[];\n}\n\nexport type UtilitySelectorFn = (options: UtilitySelectorOptions) => string;\n\nexport type ThemeSelectorFn = (options: { name: string }) => string;\n\nexport type StyleframeOptions = {\n indent?: string;\n variables?: {\n name?: VariableNameFn;\n };\n utilities?: {\n selector?: UtilitySelectorFn;\n };\n themes?: {\n selector?: ThemeSelectorFn;\n };\n};", + "size": 558, + "priority": 35 + }, + { + "path": "engine/core/src/types/declarations.ts", + "content": "import type { Properties as CSSProperties } from \"csstype\";\nimport type {\n createAtRuleFunction,\n createCssFunction,\n createKeyframesFunction,\n createMediaFunction,\n createRefFunction,\n createSelectorFunction,\n createVariableFunction,\n} from \"../tokens\";\nimport type { ContainerInput, TokenValue } from \"./tokens\";\n\ntype CSSValueWithReference = T extends string | number | undefined\n ? T | TokenValue\n : T extends object\n ? CSSValueWithReference\n : T | TokenValue;\n\nexport type DeclarationsBlock = {\n [K in keyof CSSProperties]: CSSValueWithReference;\n} & {\n [key: string]:\n | CSSValueWithReference\n | DeclarationsBlock\n | ContainerInput;\n};\n\nexport type DeclarationsCallbackContext = {\n variable: ReturnType;\n selector: ReturnType;\n atRule: ReturnType;\n keyframes: ReturnType;\n media: ReturnType;\n ref: ReturnType;\n css: ReturnType;\n};\n\nexport type DeclarationsCallback<\n Context extends DeclarationsCallbackContext = DeclarationsCallbackContext,\n> = (context: Context) => DeclarationsBlock | void;", + "size": 1354, + "priority": 32 + }, + { + "path": "theme/src/types.ts", + "content": "import type { Variable } from \"@styleframe/core\";\nimport type { CamelCase } from \"scule\";\n\nexport type WithThemes = TConfig & {\n themes?: Record;\n};\n\ntype ExportKeyVariableName<\n Prefix extends string,\n K,\n Separator extends string = \".\",\n> = K extends \"default\"\n ? Prefix\n : `${Prefix}${Separator}${K & (string | number)}`;\n\nexport type ExportKeys<\n Prefix extends string,\n T extends Record,\n Separator extends string = \".\",\n> = {\n [K in keyof T as CamelCase<\n ExportKeyVariableName\n >]: Variable>;\n};", + "size": 651, + "priority": 80 + }, + { + "path": "theme/src/index.ts", + "content": "export * from \"./elements\";\nexport * from \"./modifiers\";\nexport * from \"./presets\";\nexport * from \"./recipes\";\nexport * from \"./sanitize\";\nexport * from \"./states\";\nexport * from \"./types\";\nexport * from \"./utilities\";\nexport * from \"./utils\";\nexport * from \"./values\";\nexport * from \"./variables\";", + "size": 298, + "priority": 70 + }, + { + "path": "engine/core/src/utils/index.ts", + "content": "export * from \"./capitalizeFirst\";\nexport * from \"./cssSelector\";\nexport * from \"./deepClone\";\nexport * from \"./generateRandomId\";\nexport * from \"./getters\";\nexport * from \"./hash\";\nexport * from \"./isTokenEqual\";\nexport * from \"./merge\";", + "size": 238, + "priority": 73 + }, + { + "path": "engine/core/src/utils/capitalizeFirst.ts", + "content": "export type CapitalizeFirst =\n T extends `${infer First}${infer Rest}` ? `${Uppercase}${Rest}` : T;\n\nexport function capitalizeFirst(str: string): string {\n // ...\n}", + "size": 194, + "priority": 35 + }, + { + "path": "engine/core/src/utils/merge.ts", + "content": "import {\n isInstanceLicenseRequired,\n markInstanceLicenseRequired,\n} from \"@styleframe/license\";\nimport type { Styleframe } from \"../styleframe\";\nimport { isRoot } from \"../typeGuards\";\nimport type {\n Container,\n ContainerChild,\n Root,\n Theme,\n Variable,\n} from \"../types\";\n\nexport function mergeVariablesArray(a: Variable[], b: Variable[]) {\n // ...\n}\n\nexport function mergeThemesArray(a: Theme[], b: Theme[]) {\n // ...\n}\n\nexport function mergeContainers(a: T, b: T) {\n // ...\n}\n\nfunction registerChildren(children: ContainerChild[], root: Root) {\n // ...\n}\n\nexport function rebuildRegistry(root: Root) {\n // ...\n}\n\nexport function merge(base: Styleframe, ...instances: Styleframe[]) {\n // ...\n}", + "size": 744, + "priority": 34 + }, + { + "path": "engine/core/src/utils/cssSelector.ts", + "content": "export function classNameToCssSelector(className: string): string {\n // ...\n}", + "size": 78, + "priority": 30 + }, + { + "path": "engine/core/src/utils/hash.ts", + "content": "export function hashValue(value: string): string {\n // ...\n}", + "size": 61, + "priority": 29 + }, + { + "path": "engine/core/src/utils/isTokenEqual.ts", + "content": "import { isCSS, isRef } from \"../typeGuards\";\nimport type { TokenValue } from \"../types\";\n\nexport function isTokenEqual(a: TokenValue, b: TokenValue): boolean {\n // ...\n}", + "size": 171, + "priority": 27 + }, + { + "path": "engine/core/src/utils/cssSelector.test.ts", + "content": "import { describe, expect, it } from \"vitest\";\nimport { classNameToCssSelector } from \"./cssSelector\";\n\ndescribe(\"classNameToCssSelector\", () => {\n it(\"should add . prefix to simple class name\", () => {\n expect(classNameToCssSelector(\"sf-margin-sm\")).toBe(\".sf-margin-sm\");\n });\n\n it(\"should escape colons\", () => {\n expect(classNameToCssSelector(\"_margin:sm\")).toBe(\"._margin\\\\:sm\");\n });\n\n it(\"should escape brackets\", () => {\n expect(classNameToCssSelector(\"_margin:[16px]\")).toBe(\n \"._margin\\\\:\\\\[16px\\\\]\",\n );\n });\n\n it(\"should escape hash character\", () => {\n expect(classNameToCssSelector(\"_color:[#1E3A8A]\")).toBe(\n \"._color\\\\:\\\\[\\\\#1E3A8A\\\\]\",\n );\n });\n\n it(\"should escape parentheses\", () => {\n expect(classNameToCssSelector(\"_bg:[rgb(255,0,0)]\")).toBe(\n \"._bg\\\\:\\\\[rgb\\\\(255\\\\,0\\\\,0\\\\)\\\\]\",\n );\n });\n\n it(\"should escape percentage\", () => {\n expect(classNameToCssSelector(\"_width:[50%]\")).toBe(\n \"._width\\\\:\\\\[50\\\\%\\\\]\",\n );\n });\n\n it(\"should escape dots\", () => {\n expect(classNameToCssSelector(\"_opacity:[0.5]\")).toBe(\n \"._opacity\\\\:\\\\[0\\\\.5\\\\]\",\n );\n });\n\n it(\"should escape commas\", () => {\n expect(classNameToCssSelector(\"_bg:[rgba(255,0,0,0.5)]\")).toBe(\n \"._bg\\\\:\\\\[rgba\\\\(255\\\\,0\\\\,0\\\\,0\\\\.5\\\\)\\\\]\",\n );\n });\n\n it(\"should not escape characters that are not special\", () => {\n expect(classNameToCssSelector(\"_margin-sm\")).toBe(\"._margin-sm\");\n });\n\n it(\"should handle multiple colons (modifiers)\", () => {\n expect(classNameToCssSelector(\"_hover:focus:margin:sm\")).toBe(\n \"._hover\\\\:focus\\\\:margin\\\\:sm\",\n );\n });\n\n it(\"should escape forward slashes\", () => {\n expect(classNameToCssSelector(\"_width:1/2\")).toBe(\"._width\\\\:1\\\\/2\");\n });\n});", + "size": 1924, + "priority": 26 + }, + { + "path": "engine/core/src/utils/getters.test.ts", + "content": "import { describe, expect, it } from \"vitest\";\nimport type { Container } from \"../types\";\nimport { createRoot } from \"../tokens/root\";\nimport { getVariable, getUtility, getModifier } from \"./getters\";\n\ndescribe(\"getVariable\", () => {\n describe(\"basic functionality\", () => {\n it(\"should return a variable when it exists\", () => {\n const container: Container = {\n id: \"test-id\",\n variables: [\n {\n type: \"variable\",\n id: \"test-id\",\n name: \"primary-color\",\n value: \"#007bff\",\n },\n ],\n declarations: {},\n children: [],\n };\n\n const result = getVariable(container, \"primary-color\");\n\n expect(result).toEqual({\n type: \"variable\",\n id: \"test-id\",\n name: \"primary-color\",\n value: \"#007bff\",\n });\n });\n\n it(\"should return the correct variable when multiple exist\", () => {\n const container: Container = {\n id: \"test-id\",\n variables: [\n {\n type: \"variable\",\n id: \"test-id\",\n name: \"primary-color\",\n value: \"#007bff\",\n },\n {\n type: \"variable\",\n id: \"test-id\",\n name: \"secondary-color\",\n value: \"#6c757d\",\n },\n {\n type: \"variable\",\n id: \"test-id\",\n name: \"spacing\",\n value: \"1rem\",\n },\n ],\n declarations: {},\n children: [],\n };\n\n const result = getVariable(container, \"secondary-color\");\n\n expect(result).toEqual({\n type: \"variable\",\n id: \"test-id\",\n name: \"secondary-color\",\n value: \"#6c757d\",\n });\n });\n\n it(\"should throw an error when variable does not exist\", () => {\n const container: Container = {\n id: \"test-id\",\n variables: [\n {\n type: \"variable\",\n id: \"test-id\",\n name: \"primary-color\",\n value: \"#007bff\",\n },\n ],\n declarations: {},\n children: [],\n };\n\n expect(() => getVariable(container, \"non-existent\")).toThrow(\n 'Variable \"non-existent\" not found',\n );\n });\n\n it(\"should throw an error when variables array is empty\", () => {\n const container: Container = {\n id: \"test-id\",\n variables: [],\n declarations: {},\n children: [],\n };\n\n expect(() => getVariable(container, \"any-variable\")).toThrow(\n 'Variable \"any-variable\" not found',\n );\n });\n });\n\n describe(\"edge cases\", () => {\n it(\"should handle variables with special characters in names\", () => {\n const container: Container = {\n id: \"test-id\",\n variables: [\n {\n type: \"variable\",\n id: \"test-id\",\n name: \"color-primary-500\",\n value: \"#007bff\",\n },\n ],\n declarations: {},\n children: [],\n };\n\n const result = getVariable(container, \"color-primary-500\");\n\n expect(result.name).toBe(\"color-primary-500\");\n });\n\n it(\"should be case-sensitive\", () => {\n const container: Container = {\n id: \"test-id\",\n variables: [\n {\n type: \"variable\",\n id: \"test-id\",\n name: \"primaryColor\",\n value: \"#007bff\",\n },\n ],\n declarations: {},\n children: [],\n };\n\n expect(() => getVariable(container, \"primarycolor\")).toThrow(\n 'Variable \"primarycolor\" not found',\n );\n });\n });\n});\n\ndescribe(\"getUtility\", () => {\n const mockAutogenerate = () => ({});\n const mockCreate = () => {};\n\n describe(\"basic functionality\", () => {\n it(\"should return a utility when it exists\", () => {\n const mockFactory = () => {};\n const root = createRoot();\n root.utilities = [\n {\n type: \"utility\",\n name: \"padding\",\n factory: mockFactory,\n values: [],\n autogenerate: mockAutogenerate,\n create: mockCreate,\n },\n ];\n\n const result = getUtility(root, \"padding\");\n\n expect(result).toEqual({\n type: \"utility\",\n name: \"padding\",\n factory: mockFactory,\n values: [],\n autogenerate: mockAutogenerate,\n create: mockCreate,\n });\n });\n\n it(\"should return the correct utility when multiple exist\", () => {\n const mockFactory1 = () => {};\n const mockFactory2 = () => {};\n const mockFactory3 = () => {};\n const root = createRoot();\n root.utilities = [\n {\n type: \"utility\",\n name: \"padding\",\n factory: mockFactory1,\n values: [],\n autogenerate: mockAutogenerate,\n create: mockCreate,\n },\n {\n type: \"utility\",\n name: \"margin\",\n factory: mockFactory2,\n values: [],\n autogenerate: mockAutogenerate,\n create: mockCreate,\n },\n {\n type: \"utility\",\n name: \"display\",\n factory: mockFactory3,\n values: [],\n autogenerate: mockAutogenerate,\n create: mockCreate,\n },\n ];\n\n const result = getUtility(root, \"margin\");\n\n expect(result).toEqual({\n type: \"utility\",\n name: \"margin\",\n factory: mockFactory2,\n values: [],\n autogenerate: mockAutogenerate,\n create: mockCreate,\n });\n });\n\n it(\"should throw an error when utility does not exist\", () => {\n const root = createRoot();\n root.utilities = [\n {\n type: \"utility\",\n name: \"padding\",\n factory: () => {},\n values: [],\n autogenerate: mockAutogenerate,\n create: mockCreate,\n },\n ];\n\n expect(() => getUtility(root, \"non-existent\")).toThrow(\n 'Utility \"non-existent\" not found',\n );\n });\n\n it(\"should throw an error when utilities array is empty\", () => {\n const root = createRoot();\n\n expect(() => getUtility(root, \"padding\")).toThrow(\n 'Utility \"padding\" not found',\n );\n });\n });\n\n describe(\"edge cases\", () => {\n it(\"should handle utilities with hyphenated names\", () => {\n const mockFactory = () => {};\n const root = createRoot();\n root.utilities = [\n {\n type: \"utility\",\n name: \"flex-direction\",\n factory: mockFactory,\n values: [],\n autogenerate: mockAutogenerate,\n create: mockCreate,\n },\n ];\n\n const result = getUtility(root, \"flex-direction\");\n\n expect(result.name).toBe(\"flex-direction\");\n });\n\n it(\"should be case-sensitive\", () => {\n const root = createRoot();\n root.utilities = [\n {\n type: \"utility\",\n name: \"padding\",\n factory: () => {},\n values: [],\n autogenerate: mockAutogenerate,\n create: mockCreate,\n },\n ];\n\n expect(() => getUtility(root, \"Padding\")).toThrow(\n 'Utility \"Padding\" not found',\n );\n });\n });\n});\n\ndescribe(\"getModifier\", () => {\n describe(\"basic functionality\", () => {\n it(\"should return a modifier when it exists\", () => {\n const mockFactory = () => {};\n const root = createRoot();\n root.modifiers = [\n {\n type: \"modifier\",\n key: [\"hover\"],\n factory: mockFactory,\n },\n ];\n\n const result = getModifier(root, \"hover\");\n\n expect(result).toEqual({\n type: \"modifier\",\n key: [\"hover\"],\n factory: mockFactory,\n });\n });\n\n it(\"should return the correct modifier when multiple exist\", () => {\n const mockFactory1 = () => {};\n const mockFactory2 = () => {};\n const mockFactory3 = () => {};\n const root = createRoot();\n root.modifiers = [\n {\n type: \"modifier\",\n key: [\"hover\"],\n factory: mockFactory1,\n },\n {\n type: \"modifier\",\n key: [\"focus\"],\n factory: mockFactory2,\n },\n {\n type: \"modifier\",\n key: [\"active\"],\n factory: mockFactory3,\n },\n ];\n\n const result = getModifier(root, \"focus\");\n\n expect(result).toEqual({\n type: \"modifier\",\n key: [\"focus\"],\n factory: mockFactory2,\n });\n });\n\n it(\"should find modifier when name is in key array\", () => {\n const mockFactory = () => {};\n const root = createRoot();\n root.modifiers = [\n {\n type: \"modifier\",\n key: [\"sm\", \"small\"],\n factory: mockFactory,\n },\n ];\n\n const result = getModifier(root, \"small\");\n\n expect(result).toEqual({\n type: \"modifier\",\n key: [\"sm\", \"small\"],\n factory: mockFactory,\n });\n });\n\n it(\"should throw an error when modifier does not exist\", () => {\n const root = createRoot();\n root.modifiers = [\n {\n type: \"modifier\",\n key: [\"hover\"],\n factory: () => {},\n },\n ];\n\n expect(() => getModifier(root, \"non-existent\")).toThrow(\n 'Modifier \"non-existent\" not found',\n );\n });\n\n it(\"should throw an error when modifiers array is empty\", () => {\n const root = createRoot();\n\n expect(() => getModifier(root, \"hover\")).toThrow(\n 'Modifier \"hover\" not found',\n );\n });\n });\n\n describe(\"edge cases\", () => {\n it(\"should return first matching modifier when multiple keys match\", () => {\n const mockFactory1 = () => {};\n const mockFactory2 = () => {};\n const root = createRoot();\n root.modifiers = [\n {\n type: \"modifier\",\n key: [\"hover\", \"h\"],\n factory: mockFactory1,\n },\n {\n type: \"modifier\",\n key: [\"h\", \"highlight\"],\n factory: mockFactory2,\n },\n ];\n\n const result = getModifier(root, \"h\");\n\n expect(result.factory).toBe(mockFactory1);\n });\n\n it(\"should handle modifiers with multiple keys\", () => {\n const mockFactory = () => {};\n const root = createRoot();\n root.modifiers = [\n {\n type: \"modifier\",\n key: [\"sm\", \"small\", \"s\"],\n factory: mockFactory,\n },\n ];\n\n const result1 = getModifier(root, \"sm\");\n const result2 = getModifier(root, \"small\");\n const result3 = getModifier(root, \"s\");\n\n expect(result1).toBe(result2);\n expect(result2).toBe(result3);\n });\n\n it(\"should be case-sensitive\", () => {\n const root = createRoot();\n root.modifiers = [\n {\n type: \"modifier\",\n key: [\"hover\"],\n factory: () => {},\n },\n ];\n\n expect(() => getModifier(root, \"Hover\")).toThrow(\n 'Modifier \"Hover\" not found',\n );\n });\n });\n});", + "size": 13776, + "priority": 26 + }, + { + "path": "engine/core/src/utils/generateRandomId.ts", + "content": "export function generateRandomId(prefix?: string, length: number = 8): string {\n // ...\n}", + "size": 90, + "priority": 25 + }, + { + "path": "engine/core/src/utils/getters.ts", + "content": "import type {\n Container,\n ModifierFactory,\n Root,\n UtilityFactory,\n Variable,\n} from \"../types\";\n\nexport function getVariable(root: Container, name: string): Variable {\n // ...\n}\n\nexport function getUtility(root: Root, name: string): UtilityFactory {\n // ...\n}\n\nexport function getModifier(root: Root, name: string): ModifierFactory {\n // ...\n}", + "size": 363, + "priority": 24 + }, + { + "path": "engine/core/src/utils/hash.test.ts", + "content": "import { describe, it, expect } from \"vitest\";\nimport { hashValue } from \"./hash\";\n\ndescribe(\"hashValue\", () => {\n it(\"should produce a 7-character hex string\", () => {\n const result = hashValue(\"all 0.3s ease\");\n expect(result).toMatch(/^[0-9a-f]{7}$/);\n });\n\n it(\"should be deterministic (same input = same output)\", () => {\n const a = hashValue(\"all 0.3s ease\");\n const b = hashValue(\"all 0.3s ease\");\n expect(a).toBe(b);\n });\n\n it(\"should produce different hashes for different inputs\", () => {\n const a = hashValue(\"all 0.3s ease\");\n const b = hashValue(\"all 0.5s linear\");\n expect(a).not.toBe(b);\n });\n\n it(\"should handle empty string\", () => {\n const result = hashValue(\"\");\n expect(result).toMatch(/^[0-9a-f]{7}$/);\n });\n\n it(\"should handle single space\", () => {\n const result = hashValue(\" \");\n expect(result).toMatch(/^[0-9a-f]{7}$/);\n });\n\n it(\"should handle strings with various whitespace types\", () => {\n const tab = hashValue(\"a\\tb\");\n const space = hashValue(\"a b\");\n const newline = hashValue(\"a\\nb\");\n expect(tab).toMatch(/^[0-9a-f]{7}$/);\n expect(space).toMatch(/^[0-9a-f]{7}$/);\n expect(newline).toMatch(/^[0-9a-f]{7}$/);\n expect(new Set([tab, space, newline]).size).toBe(3);\n });\n\n it(\"should handle long strings\", () => {\n const long = \"a\".repeat(10000);\n const result = hashValue(long);\n expect(result).toMatch(/^[0-9a-f]{7}$/);\n });\n\n it(\"should handle CSS transition values\", () => {\n const result = hashValue(\"all 0.3s ease\");\n expect(result).toMatch(/^[0-9a-f]{7}$/);\n expect(result).not.toContain(\" \");\n });\n\n it(\"should produce different hashes for similar CSS values\", () => {\n const values = [\n \"all 0.3s ease\",\n \"all 0.3s ease-in\",\n \"all 0.3s ease-out\",\n \"opacity 0.3s ease\",\n \"all 0.5s ease\",\n ];\n const hashes = values.map(hashValue);\n expect(new Set(hashes).size).toBe(values.length);\n });\n});", + "size": 2141, + "priority": 23 + }, + { + "path": "engine/core/src/utils/isTokenEqual.test.ts", + "content": "import { describe, expect, it } from \"vitest\";\nimport { isTokenEqual } from \"./isTokenEqual\";\nimport type { Reference, CSS, TokenValue } from \"../types\";\n\ndescribe(\"isTokenEqual\", () => {\n describe(\"primitive values\", () => {\n it(\"should return true for identical strings\", () => {\n expect(isTokenEqual(\"hello\", \"hello\")).toBe(true);\n });\n\n it(\"should return false for different strings\", () => {\n expect(isTokenEqual(\"hello\", \"world\")).toBe(false);\n });\n\n it(\"should return true for identical numbers\", () => {\n expect(isTokenEqual(42, 42)).toBe(true);\n });\n\n it(\"should return false for different numbers\", () => {\n expect(isTokenEqual(42, 100)).toBe(false);\n });\n\n it(\"should return true for identical booleans\", () => {\n expect(isTokenEqual(true, true)).toBe(true);\n expect(isTokenEqual(false, false)).toBe(true);\n });\n\n it(\"should return false for different booleans\", () => {\n expect(isTokenEqual(true, false)).toBe(false);\n });\n\n it(\"should return true for null === null\", () => {\n expect(isTokenEqual(null, null)).toBe(true);\n });\n\n it(\"should return true for undefined === undefined\", () => {\n expect(isTokenEqual(undefined, undefined)).toBe(true);\n });\n\n it(\"should return false for null vs undefined\", () => {\n expect(isTokenEqual(null, undefined)).toBe(false);\n });\n });\n\n describe(\"type mismatches\", () => {\n it(\"should return false for string vs number\", () => {\n expect(isTokenEqual(\"42\", 42)).toBe(false);\n });\n\n it(\"should return false for boolean vs number\", () => {\n expect(isTokenEqual(true, 1)).toBe(false);\n });\n\n it(\"should return false for string vs boolean\", () => {\n expect(isTokenEqual(\"true\", true)).toBe(false);\n });\n\n it(\"should return false for primitive vs object\", () => {\n const ref: Reference = { type: \"reference\", name: \"test\" };\n expect(isTokenEqual(\"test\", ref)).toBe(false);\n });\n\n it(\"should return false for null vs object\", () => {\n const ref: Reference = { type: \"reference\", name: \"test\" };\n expect(isTokenEqual(null, ref)).toBe(false);\n });\n\n it(\"should return false for primitive vs array\", () => {\n expect(isTokenEqual(\"test\", [\"test\"])).toBe(false);\n });\n });\n\n describe(\"array comparison\", () => {\n it(\"should return true for identical empty arrays\", () => {\n expect(isTokenEqual([], [])).toBe(true);\n });\n\n it(\"should return true for identical primitive arrays\", () => {\n expect(isTokenEqual([\"a\", \"b\", \"c\"], [\"a\", \"b\", \"c\"])).toBe(true);\n });\n\n it(\"should return false for arrays with different lengths\", () => {\n expect(isTokenEqual([\"a\", \"b\"], [\"a\", \"b\", \"c\"])).toBe(false);\n });\n\n it(\"should return false for arrays with different values\", () => {\n expect(isTokenEqual([\"a\", \"b\", \"c\"], [\"a\", \"x\", \"c\"])).toBe(false);\n });\n\n it(\"should return false for arrays with same values in different order\", () => {\n expect(isTokenEqual([\"a\", \"b\", \"c\"], [\"c\", \"b\", \"a\"])).toBe(false);\n });\n\n it(\"should return true for arrays with identical reference objects\", () => {\n const arr1: TokenValue = [\n { type: \"reference\", name: \"color\" },\n { type: \"reference\", name: \"spacing\" },\n ];\n const arr2: TokenValue = [\n { type: \"reference\", name: \"color\" },\n { type: \"reference\", name: \"spacing\" },\n ];\n expect(isTokenEqual(arr1, arr2)).toBe(true);\n });\n\n it(\"should return false for arrays with different reference objects\", () => {\n const arr1: TokenValue = [\n { type: \"reference\", name: \"color\" },\n { type: \"reference\", name: \"spacing\" },\n ];\n const arr2: TokenValue = [\n { type: \"reference\", name: \"color\" },\n { type: \"reference\", name: \"other\" },\n ];\n expect(isTokenEqual(arr1, arr2)).toBe(false);\n });\n\n it(\"should return true for mixed primitive and reference arrays\", () => {\n const arr1: TokenValue = [\"hello\", { type: \"reference\", name: \"test\" }];\n const arr2: TokenValue = [\"hello\", { type: \"reference\", name: \"test\" }];\n expect(isTokenEqual(arr1, arr2)).toBe(true);\n });\n\n it(\"should return false for array vs non-array\", () => {\n expect(isTokenEqual([\"test\"], { type: \"reference\", name: \"test\" })).toBe(\n false,\n );\n });\n });\n\n describe(\"Reference objects\", () => {\n it(\"should return true for identical references\", () => {\n const ref1: Reference = { type: \"reference\", name: \"primary-color\" };\n const ref2: Reference = { type: \"reference\", name: \"primary-color\" };\n expect(isTokenEqual(ref1, ref2)).toBe(true);\n });\n\n it(\"should return false for references with different names\", () => {\n const ref1: Reference = { type: \"reference\", name: \"primary-color\" };\n const ref2: Reference = { type: \"reference\", name: \"secondary-color\" };\n expect(isTokenEqual(ref1, ref2)).toBe(false);\n });\n\n it(\"should return true for references with identical fallbacks\", () => {\n const ref1: Reference = {\n type: \"reference\",\n name: \"color\",\n fallback: \"#007bff\",\n };\n const ref2: Reference = {\n type: \"reference\",\n name: \"color\",\n fallback: \"#007bff\",\n };\n expect(isTokenEqual(ref1, ref2)).toBe(true);\n });\n\n it(\"should return false for references with different fallbacks\", () => {\n const ref1: Reference = {\n type: \"reference\",\n name: \"color\",\n fallback: \"#007bff\",\n };\n const ref2: Reference = {\n type: \"reference\",\n name: \"color\",\n fallback: \"#ff0000\",\n };\n expect(isTokenEqual(ref1, ref2)).toBe(false);\n });\n\n it(\"should return true for references with undefined fallbacks\", () => {\n const ref1: Reference = {\n type: \"reference\",\n name: \"color\",\n fallback: undefined,\n };\n const ref2: Reference = {\n type: \"reference\",\n name: \"color\",\n fallback: undefined,\n };\n expect(isTokenEqual(ref1, ref2)).toBe(true);\n });\n\n it(\"should return true for references with nested reference fallbacks\", () => {\n const ref1: Reference = {\n type: \"reference\",\n name: \"color\",\n fallback: { type: \"reference\", name: \"fallback-color\" },\n };\n const ref2: Reference = {\n type: \"reference\",\n name: \"color\",\n fallback: { type: \"reference\", name: \"fallback-color\" },\n };\n expect(isTokenEqual(ref1, ref2)).toBe(true);\n });\n\n it(\"should return false for references with different nested fallbacks\", () => {\n const ref1: Reference = {\n type: \"reference\",\n name: \"color\",\n fallback: { type: \"reference\", name: \"fallback-color\" },\n };\n const ref2: Reference = {\n type: \"reference\",\n name: \"color\",\n fallback: { type: \"reference\", name: \"other-fallback\" },\n };\n expect(isTokenEqual(ref1, ref2)).toBe(false);\n });\n });\n\n describe(\"CSS objects\", () => {\n it(\"should return true for identical CSS objects\", () => {\n const css1: CSS = { type: \"css\", value: [\"10px\", \"20px\"] };\n const css2: CSS = { type: \"css\", value: [\"10px\", \"20px\"] };\n expect(isTokenEqual(css1, css2)).toBe(true);\n });\n\n it(\"should return false for CSS objects with different values\", () => {\n const css1: CSS = { type: \"css\", value: [\"10px\", \"20px\"] };\n const css2: CSS = { type: \"css\", value: [\"10px\", \"30px\"] };\n expect(isTokenEqual(css1, css2)).toBe(false);\n });\n\n it(\"should return true for CSS objects with empty values\", () => {\n const css1: CSS = { type: \"css\", value: [] };\n const css2: CSS = { type: \"css\", value: [] };\n expect(isTokenEqual(css1, css2)).toBe(true);\n });\n\n it(\"should return true for CSS objects containing references\", () => {\n const css1: CSS = {\n type: \"css\",\n value: [{ type: \"reference\", name: \"spacing\" }, \"auto\"],\n };\n const css2: CSS = {\n type: \"css\",\n value: [{ type: \"reference\", name: \"spacing\" }, \"auto\"],\n };\n expect(isTokenEqual(css1, css2)).toBe(true);\n });\n\n it(\"should return false for CSS objects with different references\", () => {\n const css1: CSS = {\n type: \"css\",\n value: [{ type: \"reference\", name: \"spacing\" }, \"auto\"],\n };\n const css2: CSS = {\n type: \"css\",\n value: [{ type: \"reference\", name: \"other\" }, \"auto\"],\n };\n expect(isTokenEqual(css1, css2)).toBe(false);\n });\n });\n\n describe(\"mixed type comparisons\", () => {\n it(\"should return false for Reference vs CSS with same type value\", () => {\n const ref: Reference = { type: \"reference\", name: \"test\" };\n const css: CSS = { type: \"css\", value: [\"test\"] };\n expect(isTokenEqual(ref, css)).toBe(false);\n });\n\n it(\"should return false for objects with different type properties\", () => {\n const ref: Reference = { type: \"reference\", name: \"test\" };\n const css: CSS = { type: \"css\", value: [\"test\"] };\n expect(isTokenEqual(ref, css)).toBe(false);\n });\n });\n\n describe(\"same object reference\", () => {\n it(\"should return true when comparing same object reference\", () => {\n const ref: Reference = { type: \"reference\", name: \"test\" };\n expect(isTokenEqual(ref, ref)).toBe(true);\n });\n\n it(\"should return true when comparing same array reference\", () => {\n const arr: TokenValue = [\"a\", \"b\", \"c\"];\n expect(isTokenEqual(arr, arr)).toBe(true);\n });\n });\n\n describe(\"edge cases\", () => {\n it(\"should handle empty strings\", () => {\n expect(isTokenEqual(\"\", \"\")).toBe(true);\n expect(isTokenEqual(\"\", \"a\")).toBe(false);\n });\n\n it(\"should handle zero values\", () => {\n expect(isTokenEqual(0, 0)).toBe(true);\n expect(isTokenEqual(0, 1)).toBe(false);\n });\n\n it(\"should handle negative numbers\", () => {\n expect(isTokenEqual(-1, -1)).toBe(true);\n expect(isTokenEqual(-1, 1)).toBe(false);\n });\n\n it(\"should handle decimal numbers\", () => {\n expect(isTokenEqual(0.5, 0.5)).toBe(true);\n expect(isTokenEqual(0.1 + 0.2, 0.3)).toBe(false); // floating point precision\n });\n\n it(\"should handle references with empty names\", () => {\n const ref1: Reference = { type: \"reference\", name: \"\" };\n const ref2: Reference = { type: \"reference\", name: \"\" };\n expect(isTokenEqual(ref1, ref2)).toBe(true);\n });\n\n it(\"should handle deeply nested fallbacks\", () => {\n const ref1: Reference = {\n type: \"reference\",\n name: \"a\",\n fallback: {\n type: \"reference\",\n name: \"b\",\n fallback: {\n type: \"reference\",\n name: \"c\",\n fallback: \"default\",\n },\n },\n };\n const ref2: Reference = {\n type: \"reference\",\n name: \"a\",\n fallback: {\n type: \"reference\",\n name: \"b\",\n fallback: {\n type: \"reference\",\n name: \"c\",\n fallback: \"default\",\n },\n },\n };\n expect(isTokenEqual(ref1, ref2)).toBe(true);\n });\n\n it(\"should return false for deeply nested fallbacks with one difference\", () => {\n const ref1: Reference = {\n type: \"reference\",\n name: \"a\",\n fallback: {\n type: \"reference\",\n name: \"b\",\n fallback: {\n type: \"reference\",\n name: \"c\",\n fallback: \"default1\",\n },\n },\n };\n const ref2: Reference = {\n type: \"reference\",\n name: \"a\",\n fallback: {\n type: \"reference\",\n name: \"b\",\n fallback: {\n type: \"reference\",\n name: \"c\",\n fallback: \"default2\",\n },\n },\n };\n expect(isTokenEqual(ref1, ref2)).toBe(false);\n });\n\n it(\"should return false for objects without type property\", () => {\n const obj1 = { name: \"test\" } as unknown as TokenValue;\n const obj2 = { name: \"test\" } as unknown as TokenValue;\n expect(isTokenEqual(obj1, obj2)).toBe(false);\n });\n\n it(\"should return false for objects with unknown type values\", () => {\n const obj1 = { type: \"unknown\", name: \"test\" } as unknown as TokenValue;\n const obj2 = { type: \"unknown\", name: \"test\" } as unknown as TokenValue;\n expect(isTokenEqual(obj1, obj2)).toBe(false);\n });\n });\n});", + "size": 14474, + "priority": 23 + }, + { + "path": "engine/core/src/utils/deepClone.ts", + "content": "\n\ntype CloneFunction = (obj: T) => T;\ntype ConstructorHandler = (obj: T, fn: CloneFunction) => T;\ntype ConstructorHandlerTuple = [\n new (...args: any[]) => T,\n ConstructorHandler,\n];\n\ninterface RfdcOptions {\n circular?: boolean;\n proto?: boolean;\n constructorHandlers?: ConstructorHandlerTuple[];\n}\n\nfunction copyBuffer(cur: ArrayBufferView): ArrayBufferView {\n // ...\n}\n\nexport function rfdc(opts?: RfdcOptions): CloneFunction {\n // ...\n}\n\nfunction rfdcCircular(opts: RfdcOptions): CloneFunction {\n // ...\n}\n\nexport const deepClone = rfdc();", + "size": 614, + "priority": 22 + }, + { + "path": "theme/src/variables/index.ts", + "content": "export * from \"./useBorderColorDesignTokens\";\nexport * from \"./useBorderRadiusDesignTokens\";\nexport * from \"./useBorderStyleDesignTokens\";\nexport * from \"./useBorderWidthDesignTokens\";\nexport * from \"./useBoxShadowDesignTokens\";\nexport * from \"./useBreakpointDesignTokens\";\nexport * from \"./useColorDesignTokens\";\nexport * from \"./useColorLevelDesignTokens\";\nexport * from \"./useColorShadeDesignTokens\";\nexport * from \"./useColorTintDesignTokens\";\nexport * from \"./useDurationDesignTokens\";\nexport * from \"./useEasingDesignTokens\";\nexport * from \"./useFontFamilyDesignTokens\";\nexport * from \"./useFontSizeDesignTokens\";\nexport * from \"./useFontStyleDesignTokens\";\nexport * from \"./useFontWeightDesignTokens\";\nexport * from \"./useLetterSpacingDesignTokens\";\nexport * from \"./useLineHeightDesignTokens\";\nexport * from \"./useMultiplierDesignTokens\";\nexport * from \"./useScaleDesignTokens\";\nexport * from \"./useScalePowersDesignTokens\";\nexport * from \"./useSpacingDesignTokens\";\nexport * from \"./useZIndexDesignTokens\";", + "size": 1015, + "priority": 72 + }, + { + "path": "theme/src/variables/useColorLevelDesignTokens.ts", + "content": "import { createUseDerivedVariable } from \"../utils\";\nimport { computeLightnessColor, parseOklch } from \"../utils/oklchGamut\";\nimport { colorLevelValues } from \"../values\";\nexport const useColorLevelDesignTokens = createUseDerivedVariable({\n defaults: colorLevelValues,\n delimiter: \"-\",\n transform: (value: number, { s, parent }) => {\n if (typeof value !== \"number\") {\n return 0;\n }\n\n const parsed = parseOklch(String(parent.value));\n if (!parsed) {\n return s.ref(parent);\n }\n\n return computeLightnessColor(\n parsed.l,\n parsed.c,\n parsed.h,\n parsed.alpha,\n value,\n );\n },\n});", + "size": 712, + "priority": 24 + }, + { + "path": "theme/src/variables/useColorShadeDesignTokens.ts", + "content": "import { createUseDerivedVariable } from \"../utils\";\nimport { computeRelativeColor, parseOklch } from \"../utils/oklchGamut\";\nimport { colorShadeValues } from \"../values\";\nexport const useColorShadeDesignTokens = createUseDerivedVariable({\n defaults: colorShadeValues,\n delimiter: \"-\",\n transform: (value: number, { s, parent }) => {\n if (typeof value !== \"number\") {\n return 0;\n }\n\n const parsed = parseOklch(String(parent.value));\n if (!parsed) {\n return s.ref(parent);\n }\n\n return computeRelativeColor(\n parsed.l,\n parsed.c,\n parsed.h,\n parsed.alpha,\n -(value / 100),\n );\n },\n});", + "size": 719, + "priority": 24 + }, + { + "path": "theme/src/variables/useColorTintDesignTokens.ts", + "content": "import { createUseDerivedVariable } from \"../utils\";\nimport { computeRelativeColor, parseOklch } from \"../utils/oklchGamut\";\nimport { colorTintValues } from \"../values\";\nexport const useColorTintDesignTokens = createUseDerivedVariable({\n defaults: colorTintValues,\n delimiter: \"-\",\n transform: (value: number, { s, parent }) => {\n if (typeof value !== \"number\") {\n return 0;\n }\n\n const parsed = parseOklch(String(parent.value));\n if (!parsed) {\n return s.ref(parent);\n }\n\n return computeRelativeColor(\n parsed.l,\n parsed.c,\n parsed.h,\n parsed.alpha,\n value / 100,\n );\n },\n});", + "size": 713, + "priority": 24 + }, + { + "path": "theme/src/variables/useScaleDesignTokens.ts", + "content": "import { createUseVariable } from \"../utils\";\nimport { scaleValues } from \"../values\";\n\nexport const useScaleDesignTokens = createUseVariable(\"scale\", {\n defaults: scaleValues,\n});", + "size": 183, + "priority": 24 + }, + { + "path": "theme/src/variables/useBorderColorDesignTokens.ts", + "content": "import { createUseVariable } from \"../utils\";\n\nexport const useBorderColorDesignTokens = createUseVariable(\"border-color\");", + "size": 123, + "priority": 22 + }, + { + "path": "theme/src/variables/useBorderRadiusDesignTokens.ts", + "content": "import { createUseVariable } from \"../utils\";\nimport { borderRadiusValues } from \"../values\";\nexport const useBorderRadiusDesignTokens = createUseVariable(\"border-radius\", {\n defaults: borderRadiusValues,\n});", + "size": 211, + "priority": 22 + }, + { + "path": "theme/src/variables/useBoxShadowDesignTokens.ts", + "content": "import { createUseVariable } from \"../utils\";\nimport { boxShadowValues } from \"../values\";\nexport const useBoxShadowDesignTokens = createUseVariable(\"box-shadow\", {\n defaults: boxShadowValues,\n});", + "size": 199, + "priority": 22 + }, + { + "path": "theme/src/variables/useBreakpointDesignTokens.ts", + "content": "import { createUseVariable } from \"../utils\";\nimport { breakpointValues } from \"../values\";\nexport const useBreakpointDesignTokens = createUseVariable(\"breakpoint\", {\n defaults: breakpointValues,\n});", + "size": 202, + "priority": 22 + }, + { + "path": "theme/src/variables/useColorDesignTokens.ts", + "content": "import { createUseVariable } from \"../utils\";\nimport { parseOklch } from \"../utils/oklchGamut\";\n\nexport const useColorDesignTokens = createUseVariable(\"color\", {\n transform: (value) => {\n let transformedValue = value;\n\n if (typeof value === \"string\") {\n const parsed = parseOklch(value);\n if (parsed) {\n const { l, c, h, alpha } = parsed;\n transformedValue = `oklch(${l} ${c} ${h} / ${alpha})`;\n }\n }\n\n return transformedValue;\n },\n});", + "size": 532, + "priority": 22 + }, + { + "path": "theme/src/variables/useDurationDesignTokens.ts", + "content": "import { createUseVariable } from \"../utils\";\nimport { durationValues } from \"../values\";\nexport const useDurationDesignTokens = createUseVariable(\"duration\", {\n defaults: durationValues,\n});", + "size": 194, + "priority": 22 + }, + { + "path": "theme/src/variables/useEasingDesignTokens.ts", + "content": "import { createUseVariable } from \"../utils\";\nimport { easingValues } from \"../values\";\nexport const useEasingDesignTokens = createUseVariable(\"easing\", {\n defaults: easingValues,\n});", + "size": 186, + "priority": 22 + }, + { + "path": "theme/src/variables/useFontFamilyDesignTokens.ts", + "content": "import { createUseVariable } from \"../utils\";\nimport { fontFamilyValues } from \"../values\";\nexport const useFontFamilyDesignTokens = createUseVariable(\"font-family\", {\n defaults: fontFamilyValues,\n});", + "size": 203, + "priority": 22 + }, + { + "path": "theme/src/variables/useFontSizeDesignTokens.ts", + "content": "import { createUseVariable } from \"../utils\";\nimport { fontSizeValues } from \"../values\";\nexport const useFontSizeDesignTokens = createUseVariable(\"font-size\", {\n defaults: fontSizeValues,\n});", + "size": 195, + "priority": 22 + }, + { + "path": "theme/src/variables/useLetterSpacingDesignTokens.ts", + "content": "import { createUseVariable } from \"../utils\";\nimport { letterSpacingValues } from \"../values\";\nexport const useLetterSpacingDesignTokens = createUseVariable(\n \"letter-spacing\",\n {\n defaults: letterSpacingValues,\n },\n);", + "size": 234, + "priority": 22 + }, + { + "path": "theme/src/variables/useLineHeightDesignTokens.ts", + "content": "import { createUseVariable } from \"../utils\";\nimport { lineHeightValues } from \"../values\";\nexport const useLineHeightDesignTokens = createUseVariable(\"line-height\", {\n defaults: lineHeightValues,\n});", + "size": 203, + "priority": 22 + }, + { + "path": "theme/src/variables/useScalePowersDesignTokens.ts", + "content": "import type {\n CSS,\n DeclarationsCallbackContext,\n Reference,\n TokenValue,\n Variable,\n} from \"@styleframe/core\";\nimport { isRef } from \"@styleframe/core\";\nimport { scalePowerValues } from \"../values\";\nexport function useScalePowersDesignTokens<\n Context extends DeclarationsCallbackContext,\n T extends readonly number[],\n>(\n s: Context,\n scale: Variable | Reference,\n powers: T = scalePowerValues as T,\n): Record {\n const results: Record = {};\n\n for (const power of powers) {\n const absPower = Math.abs(power);\n const operator = power > 0 ? \" * \" : \" / \";\n const value: TokenValue[] = [];\n\n if (power <= 0) {\n value.push(\"1\");\n }\n\n for (let i = 0; i < absPower; i++) {\n if (i > 0 || power < 0) {\n value.push(operator);\n }\n value.push(isRef(scale) ? scale : s.ref(scale));\n }\n\n results[power] = {\n type: \"css\",\n value,\n };\n }\n\n return results as Record;\n}", + "size": 1106, + "priority": 22 + }, + { + "path": "theme/src/variables/useSpacingDesignTokens.ts", + "content": "import { createUseVariable } from \"../utils\";\nimport { spacingValues } from \"../values\";\nexport const useSpacingDesignTokens = createUseVariable(\"spacing\", {\n defaults: spacingValues,\n});", + "size": 190, + "priority": 22 + }, + { + "path": "theme/src/variables/useZIndexDesignTokens.ts", + "content": "import { createUseVariable } from \"../utils\";\nimport { zIndexValues } from \"../values\";\nexport const useZIndexDesignTokens = createUseVariable(\"z-index\", {\n defaults: zIndexValues,\n});", + "size": 187, + "priority": 22 + }, + { + "path": "theme/src/variables/useBorderStyleDesignTokens.ts", + "content": "import { createUseVariable } from \"../utils\";\nimport { borderStyleValues } from \"../values\";\nexport const useBorderStyleDesignTokens = createUseVariable(\"border-style\", {\n defaults: borderStyleValues,\n});", + "size": 207, + "priority": 20 + }, + { + "path": "theme/src/variables/useBorderWidthDesignTokens.ts", + "content": "import { createUseVariable } from \"../utils\";\nimport { borderWidthValues } from \"../values\";\nexport const useBorderWidthDesignTokens = createUseVariable(\"border-width\", {\n defaults: borderWidthValues,\n});", + "size": 207, + "priority": 20 + }, + { + "path": "theme/src/variables/useFontStyleDesignTokens.ts", + "content": "import { createUseVariable } from \"../utils\";\nimport { fontStyleValues } from \"../values\";\nexport const useFontStyleDesignTokens = createUseVariable(\"font-style\", {\n defaults: fontStyleValues,\n});", + "size": 199, + "priority": 20 + }, + { + "path": "theme/src/variables/useFontWeightDesignTokens.ts", + "content": "import { createUseVariable } from \"../utils\";\nimport { fontWeightValues } from \"../values\";\nexport const useFontWeightDesignTokens = createUseVariable(\"font-weight\", {\n defaults: fontWeightValues,\n});", + "size": 203, + "priority": 20 + }, + { + "path": "theme/src/variables/useMultiplierDesignTokens.ts", + "content": "import { createUseDerivedVariable } from \"../utils\";\n\nexport const useMultiplierDesignTokens = createUseDerivedVariable({\n transform: (value, { s, parent }) => {\n return s.css`calc(${s.ref(parent)} * ${value})`;\n },\n});", + "size": 232, + "priority": 20 + }, + { + "path": "theme/src/variables/useBorderColorDesignTokens.test.ts", + "content": "import type { Variable } from \"@styleframe/core\";\nimport { styleframe } from \"@styleframe/core\";\nimport { consumeCSS } from \"@styleframe/transpiler\";\nimport { useBorderColorDesignTokens } from \"./useBorderColorDesignTokens\";\n\ndescribe(\"useBorderColorDesignTokens\", () => {\n it(\"should create all border color variables with correct names and values\", () => {\n const s = styleframe();\n const { borderColor, borderColorPrimary } = useBorderColorDesignTokens(s, {\n default: \"#e5e5e5\",\n primary: \"#3b82f6\",\n });\n\n expect(borderColorPrimary).toEqual(\n expect.objectContaining({\n type: \"variable\",\n name: \"border-color.primary\",\n value: \"#3b82f6\",\n }),\n );\n\n expect(borderColor).toEqual(\n expect.objectContaining({\n type: \"variable\",\n name: \"border-color\",\n value: \"#e5e5e5\",\n }),\n );\n });\n\n it(\"should add all border color variables to root\", () => {\n const s = styleframe();\n useBorderColorDesignTokens(s, {\n default: \"#e5e5e5\",\n primary: \"#3b82f6\",\n });\n\n expect(s.root.variables).toHaveLength(2);\n const names = s.root.variables.map((v) => v.name);\n expect(names).toContain(\"border-color\");\n expect(names).toContain(\"border-color.primary\");\n });\n\n it(\"should return all border color variables in an object\", () => {\n const s = styleframe();\n const borderColors = useBorderColorDesignTokens(s, {\n default: \"#e5e5e5\",\n primary: \"#3b82f6\",\n success: \"#10b981\",\n });\n\n expect(Object.keys(borderColors)).toEqual(\n expect.arrayContaining([\n \"borderColor\",\n \"borderColorPrimary\",\n \"borderColorSuccess\",\n ]),\n );\n });\n\n it(\"should compile to correct CSS output using consumeCSS\", () => {\n const s = styleframe();\n useBorderColorDesignTokens(s, {\n default: \"#e5e5e5\",\n primary: \"#3b82f6\",\n });\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toContain(\"--border-color--primary: #3b82f6;\");\n expect(css).toContain(\"--border-color: #e5e5e5;\");\n });\n\n it(\"should compile individual border color variable to correct CSS\", () => {\n const s = styleframe();\n const { borderColorPrimary } = useBorderColorDesignTokens(s, {\n primary: \"#3b82f6\",\n });\n\n const css = consumeCSS(borderColorPrimary, s.options);\n\n expect(css).toBe(\"--border-color--primary: #3b82f6;\");\n });\n\n it(\"should not create duplicate variables when called multiple times\", () => {\n const s = styleframe();\n const borderColors1 = useBorderColorDesignTokens(s, {\n default: \"#e5e5e5\",\n primary: \"#3b82f6\",\n });\n const borderColors2 = useBorderColorDesignTokens(s, {\n default: \"#e5e5e5\",\n primary: \"#3b82f6\",\n });\n\n expect(borderColors1.borderColorPrimary).toBe(\n borderColors2.borderColorPrimary,\n );\n expect(borderColors1.borderColor).toBe(borderColors2.borderColor);\n expect(s.root.variables).toHaveLength(2);\n });\n\n it(\"should allow border color variables to be used as references\", () => {\n const s = styleframe();\n const { borderColorPrimary } = useBorderColorDesignTokens(s, {\n primary: \"#3b82f6\",\n });\n\n const customBorderColor = s.variable(\n \"custom-border-color\",\n s.ref(borderColorPrimary),\n );\n\n expect(customBorderColor.value).toEqual({\n type: \"reference\",\n name: \"border-color.primary\",\n });\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toContain(\n \"--custom-border-color: var(--border-color--primary);\",\n );\n });\n\n it(\"should work with selector overrides\", () => {\n const s = styleframe();\n const { borderColorPrimary } = useBorderColorDesignTokens(s, {\n primary: \"#3b82f6\",\n });\n\n s.selector(\".custom-border-color\", ({ variable }) => {\n variable(borderColorPrimary, \"#ff0000\");\n });\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toContain(\"--border-color--primary: #3b82f6;\");\n expect(css).toContain(\".custom-border-color {\");\n expect(css).toContain(\"--border-color--primary: #ff0000;\");\n });\n\n describe(\"type safety\", () => {\n it(\"should preserve exact border color variable names in return type\", () => {\n const s = styleframe();\n const borderColors = useBorderColorDesignTokens(s, {\n default: \"#e5e5e5\",\n primary: \"#3b82f6\",\n });\n\n const primary: Variable<\"border-color.primary\"> =\n borderColors.borderColorPrimary;\n const borderColor: Variable<\"border-color\"> = borderColors.borderColor;\n\n expect(primary.name).toBe(\"border-color.primary\");\n expect(borderColor.name).toBe(\"border-color\");\n });\n\n it(\"should have correct value types\", () => {\n const s = styleframe();\n const borderColors = useBorderColorDesignTokens(s, {\n default: \"#e5e5e5\",\n primary: \"#3b82f6\",\n });\n\n expect(typeof borderColors.borderColorPrimary.value).toBe(\"string\");\n expect(typeof borderColors.borderColor.value).toBe(\"string\");\n });\n });\n\n describe(\"default border color\", () => {\n it(\"should create a default border color variable with the provided value\", () => {\n const s = styleframe();\n const { borderColor } = useBorderColorDesignTokens(s, {\n default: \"#e5e5e5\",\n });\n\n expect(borderColor).toEqual(\n expect.objectContaining({\n type: \"variable\",\n name: \"border-color\",\n value: \"#e5e5e5\",\n }),\n );\n });\n\n it(\"should allow customizing the default border color with a reference\", () => {\n const s = styleframe();\n const { borderColor } = useBorderColorDesignTokens(s, {\n primary: \"#3b82f6\",\n default: \"@border-color.primary\",\n });\n\n expect(borderColor.value).toEqual({\n type: \"reference\",\n name: \"border-color.primary\",\n });\n });\n\n it(\"should compile default border color reference to CSS correctly\", () => {\n const s = styleframe();\n useBorderColorDesignTokens(s, {\n primary: \"#3b82f6\",\n error: \"#ef4444\",\n default: \"@border-color.primary\",\n });\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toContain(\"--border-color--primary: #3b82f6;\");\n expect(css).toContain(\"--border-color--error: #ef4444;\");\n expect(css).toContain(\"--border-color: var(--border-color--primary);\");\n });\n });\n\n describe(\"with color variable references\", () => {\n it(\"should work with cross-domain var() references\", () => {\n const s = styleframe();\n\n useBorderColorDesignTokens(s, {\n default: \"var(--color--surface-shade-50)\",\n primary: \"var(--color--primary-shade-50)\",\n });\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toContain(\n \"--border-color--primary: var(--color--primary-shade-50);\",\n );\n expect(css).toContain(\"--border-color: var(--color--surface-shade-50);\");\n });\n });\n\n describe(\"practical usage\", () => {\n it(\"should work for creating borders with different colors\", () => {\n const s = styleframe();\n const { borderColor, borderColorPrimary } = useBorderColorDesignTokens(\n s,\n {\n default: \"#e5e5e5\",\n primary: \"#3b82f6\",\n },\n );\n\n s.selector(\".card\", ({ variable }) => {\n variable(\"border-color\", s.ref(borderColor));\n });\n\n s.selector(\".card-primary\", ({ variable }) => {\n variable(\"border-color\", s.ref(borderColorPrimary));\n });\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toContain(\"--border-color: var(--border-color);\");\n expect(css).toContain(\"--border-color: var(--border-color--primary);\");\n });\n\n it(\"should work for theme-specific border color overrides\", () => {\n const s = styleframe();\n const { borderColorPrimary } = useBorderColorDesignTokens(s, {\n primary: \"#3b82f6\",\n });\n\n s.selector(\".dark-theme\", ({ variable }) => {\n variable(borderColorPrimary, \"#60a5fa\");\n });\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toContain(\"--border-color--primary: #3b82f6;\");\n expect(css).toContain(\"--border-color--primary: #60a5fa;\");\n });\n\n it(\"should work for state-specific border colors\", () => {\n const s = styleframe();\n const { borderColor, borderColorPrimary } = useBorderColorDesignTokens(\n s,\n {\n default: \"#e5e5e5\",\n primary: \"#3b82f6\",\n error: \"#ef4444\",\n },\n );\n\n s.selector(\".input\", ({ variable }) => {\n variable(\"border-color\", s.ref(borderColor));\n });\n\n s.selector(\".input:focus\", ({ variable }) => {\n variable(\"border-color\", s.ref(borderColorPrimary));\n });\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toContain(\"--border-color--primary: #3b82f6;\");\n expect(css).toContain(\"--border-color--error: #ef4444;\");\n expect(css).toContain(\"--border-color: #e5e5e5;\");\n expect(css).toContain(\"--border-color: var(--border-color);\");\n expect(css).toContain(\"--border-color: var(--border-color--primary);\");\n });\n });\n});", + "size": 10468, + "priority": 11 + }, + { + "path": "theme/src/variables/useColorDesignTokens.test.ts", + "content": "import type { Variable } from \"@styleframe/core\";\nimport { styleframe } from \"@styleframe/core\";\nimport { consumeCSS } from \"@styleframe/transpiler\";\nimport { oklch } from \"culori\";\nimport { useColorDesignTokens } from \"./useColorDesignTokens\";\nimport { useColorLevelDesignTokens } from \"./useColorLevelDesignTokens\";\nimport { useColorShadeDesignTokens } from \"./useColorShadeDesignTokens\";\nimport { useColorTintDesignTokens } from \"./useColorTintDesignTokens\";\n\nfunction toOklch(color: string): string {\n // ...\n}\n\ndescribe(\"useColorDesignTokens\", () => {\n it(\"should create a single color variable with correct naming\", () => {\n const s = styleframe();\n const { colorPrimary } = useColorDesignTokens(s, {\n primary: \"#007bff\",\n });\n\n expect(colorPrimary).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color.primary\",\n value: toOklch(\"#007bff\"),\n });\n\n const css = consumeCSS(colorPrimary, s.options);\n expect(css).toBe(`--color--primary: ${toOklch(\"#007bff\")};`);\n });\n\n it(\"should create multiple color variables\", () => {\n const s = styleframe();\n const { colorPrimary, colorSecondary, colorTertiary } =\n useColorDesignTokens(s, {\n primary: \"#007bff\",\n secondary: \"#6c757d\",\n tertiary: \"#28a745\",\n });\n\n expect(colorPrimary).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color.primary\",\n value: toOklch(\"#007bff\"),\n });\n\n expect(colorSecondary).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color.secondary\",\n value: toOklch(\"#6c757d\"),\n });\n\n expect(colorTertiary).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color.tertiary\",\n value: toOklch(\"#28a745\"),\n });\n });\n\n it(\"should add variables to root\", () => {\n const s = styleframe();\n useColorDesignTokens(s, {\n primary: \"#007bff\",\n secondary: \"#6c757d\",\n });\n\n expect(s.root.variables).toHaveLength(2);\n expect(s.root.variables[0]?.name).toBe(\"color.primary\");\n expect(s.root.variables[1]?.name).toBe(\"color.secondary\");\n });\n\n it(\"should handle kebab-case color names\", () => {\n const s = styleframe();\n const { colorPrimaryDark } = useColorDesignTokens(s, {\n \"primary-dark\": \"#0056b3\",\n });\n\n expect(colorPrimaryDark).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color.primary-dark\",\n value: toOklch(\"#0056b3\"),\n });\n });\n\n it(\"should handle snake_case color names\", () => {\n const s = styleframe();\n const { colorPrimaryLight } = useColorDesignTokens(s, {\n primary_light: \"#80bdff\",\n });\n\n expect(colorPrimaryLight).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color.primary_light\",\n value: toOklch(\"#80bdff\"),\n });\n });\n\n it(\"should handle numeric color names\", () => {\n const s = styleframe();\n const { color500 } = useColorDesignTokens(s, {\n \"500\": \"#007bff\",\n });\n\n expect(color500).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color.500\",\n value: toOklch(\"#007bff\"),\n });\n });\n\n it(\"should handle rgb color values\", () => {\n const s = styleframe();\n const { colorPrimary } = useColorDesignTokens(s, {\n primary: \"rgb(0, 123, 255)\",\n });\n\n expect(colorPrimary).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color.primary\",\n value: toOklch(\"rgb(0, 123, 255)\"),\n });\n });\n\n it(\"should handle rgba color values\", () => {\n const s = styleframe();\n const { colorPrimary } = useColorDesignTokens(s, {\n primary: \"rgba(0, 123, 255, 0.5)\",\n });\n\n expect(colorPrimary).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color.primary\",\n value: toOklch(\"rgba(0, 123, 255, 0.5)\"),\n });\n });\n\n it(\"should handle hsl color values\", () => {\n const s = styleframe();\n const { colorPrimary } = useColorDesignTokens(s, {\n primary: \"hsl(211, 100%, 50%)\",\n });\n\n expect(colorPrimary).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color.primary\",\n value: toOklch(\"hsl(211, 100%, 50%)\"),\n });\n });\n\n it(\"should handle empty color object\", () => {\n const s = styleframe();\n const result = useColorDesignTokens(s, {});\n\n expect(result).toEqual({});\n expect(s.root.variables).toHaveLength(0);\n });\n\n it(\"should handle color references\", () => {\n const s = styleframe();\n const baseColor = s.variable(\"base-color\", \"#007bff\");\n const { colorPrimary } = useColorDesignTokens(s, {\n primary: s.ref(baseColor),\n });\n\n expect(colorPrimary.value).toEqual({\n type: \"reference\",\n name: \"base-color\",\n });\n });\n\n it(\"should compile to correct CSS output using consumeCSS\", () => {\n const s = styleframe();\n useColorDesignTokens(s, {\n primary: \"#007bff\",\n secondary: \"#6c757d\",\n tertiary: \"#28a745\",\n });\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toBe(`:root {\n --color--primary: ${toOklch(\"#007bff\")};\n --color--secondary: ${toOklch(\"#6c757d\")};\n --color--tertiary: ${toOklch(\"#28a745\")};\n}`);\n });\n\n describe(\"type safety\", () => {\n it(\"should preserve exact color names in return type\", () => {\n const s = styleframe();\n const colors = useColorDesignTokens(s, {\n primary: \"#007bff\",\n secondary: \"#6c757d\",\n });\n\n const primary: Variable<\"color.primary\"> = colors.colorPrimary;\n const secondary: Variable<\"color.secondary\"> = colors.colorSecondary;\n\n expect(primary.name).toBe(\"color.primary\");\n expect(secondary.name).toBe(\"color.secondary\");\n });\n\n it(\"should maintain type information for kebab-case names\", () => {\n const s = styleframe();\n const { colorPrimaryDark } = useColorDesignTokens(s, {\n \"primary-dark\": \"#0056b3\",\n });\n\n const typed: Variable<\"color.primary-dark\"> = colorPrimaryDark;\n expect(typed.name).toBe(\"color.primary-dark\");\n });\n\n it(\"should work with const assertion\", () => {\n const s = styleframe();\n const colorConfig = {\n primary: \"#007bff\",\n secondary: \"#6c757d\",\n } as const;\n\n const colors = useColorDesignTokens(s, colorConfig);\n\n expect(colors.colorPrimary.name).toBe(\"color.primary\");\n expect(colors.colorSecondary.name).toBe(\"color.secondary\");\n });\n });\n});\n\ndescribe(\"integration\", () => {\n it(\"should work together to create a complete color system\", () => {\n const s = styleframe();\n\n const { colorPrimary, colorSecondary } = useColorDesignTokens(s, {\n primary: \"#007bff\",\n secondary: \"#6c757d\",\n });\n\n const primaryLevels = useColorLevelDesignTokens(s, colorPrimary, {\n 100: 10,\n 500: 50,\n 900: 90,\n } as const);\n\n const secondaryShades = useColorShadeDesignTokens(s, colorSecondary, {\n \"shade-50\": 5,\n \"shade-100\": 10,\n } as const);\n\n const secondaryTints = useColorTintDesignTokens(s, colorSecondary, {\n \"tint-50\": 5,\n \"tint-100\": 10,\n } as const);\n\n expect(colorPrimary.name).toBe(\"color.primary\");\n expect(colorSecondary.name).toBe(\"color.secondary\");\n expect(primaryLevels.colorPrimary100.name).toBe(\"color.primary-100\");\n expect(primaryLevels.colorPrimary500.name).toBe(\"color.primary-500\");\n expect(primaryLevels.colorPrimary900.name).toBe(\"color.primary-900\");\n expect(secondaryShades.colorSecondaryShade50.name).toBe(\n \"color.secondary-shade-50\",\n );\n expect(secondaryShades.colorSecondaryShade100.name).toBe(\n \"color.secondary-shade-100\",\n );\n expect(secondaryTints.colorSecondaryTint50.name).toBe(\n \"color.secondary-tint-50\",\n );\n expect(secondaryTints.colorSecondaryTint100.name).toBe(\n \"color.secondary-tint-100\",\n );\n\n expect(s.root.variables.length).toBe(9);\n });\n\n it(\"should handle complex naming scenarios\", () => {\n const s = styleframe();\n\n const { colorBrandPrimary } = useColorDesignTokens(s, {\n \"brand-primary\": \"#007bff\",\n });\n\n const levels = useColorLevelDesignTokens(s, colorBrandPrimary, {\n 400: 0.65,\n } as const);\n\n const shades = useColorShadeDesignTokens(s, colorBrandPrimary, {\n \"shade-50\": 5,\n } as const);\n\n const tints = useColorTintDesignTokens(s, colorBrandPrimary, {\n \"tint-50\": 5,\n } as const);\n\n expect(colorBrandPrimary).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color.brand-primary\",\n value: toOklch(\"#007bff\"),\n });\n expect(levels.colorBrandPrimary400).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color.brand-primary-400\",\n value: expect.stringMatching(/^oklch\\(/),\n });\n expect(shades.colorBrandPrimaryShade50).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color.brand-primary-shade-50\",\n value: expect.stringMatching(/^oklch\\(/),\n });\n expect(tints.colorBrandPrimaryTint50).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color.brand-primary-tint-50\",\n value: expect.stringMatching(/^oklch\\(/),\n });\n });\n\n it(\"should compile complete color system to correct CSS output using consumeCSS\", () => {\n const s = styleframe();\n\n const { colorPrimary } = useColorDesignTokens(s, {\n primary: \"#007bff\",\n });\n\n const levels = useColorLevelDesignTokens(s, colorPrimary, {\n 100: 0.93,\n 200: 0.85,\n });\n\n const shades = useColorShadeDesignTokens(s, colorPrimary, {\n \"shade-50\": 5,\n });\n\n const tints = useColorTintDesignTokens(s, colorPrimary, {\n \"tint-50\": 5,\n });\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toMatch(/--color--primary-100: oklch\\(.+\\);/);\n expect(css).toMatch(/--color--primary-200: oklch\\(.+\\);/);\n expect(css).toMatch(/--color--primary-shade-50: oklch\\(.+\\);/);\n expect(css).toMatch(/--color--primary-tint-50: oklch\\(.+\\);/);\n\n expect(levels.colorPrimary100).toBeDefined();\n expect(shades.colorPrimaryShade50).toBeDefined();\n expect(tints.colorPrimaryTint50).toBeDefined();\n });\n});", + "size": 12124, + "priority": 11 + }, + { + "path": "theme/src/variables/useBorderRadiusDesignTokens.test.ts", + "content": "import type { Variable } from \"@styleframe/core\";\nimport { styleframe } from \"@styleframe/core\";\nimport { consumeCSS } from \"@styleframe/transpiler\";\nimport { useBorderRadiusDesignTokens } from \"./useBorderRadiusDesignTokens\";\n\ndescribe(\"useBorderRadiusDesignTokens\", () => {\n it(\"should create a single border-radius variable with 'default' key\", () => {\n const s = styleframe();\n const { borderRadius } = useBorderRadiusDesignTokens(s, {\n default: \"0.25rem\",\n });\n\n expect(borderRadius).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"border-radius\",\n value: \"0.25rem\",\n });\n\n const css = consumeCSS(borderRadius, s.options);\n expect(css).toBe(`--border-radius: 0.25rem;`);\n });\n\n it(\"should create border-radius variable with modifier for non-default keys\", () => {\n const s = styleframe();\n const { borderRadiusSm } = useBorderRadiusDesignTokens(s, {\n sm: \"0.125rem\",\n });\n\n expect(borderRadiusSm).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"border-radius.sm\",\n value: \"0.125rem\",\n });\n\n const css = consumeCSS(borderRadiusSm, s.options);\n expect(css).toBe(`--border-radius--sm: 0.125rem;`);\n });\n\n it(\"should create multiple border-radius variables\", () => {\n const s = styleframe();\n const { borderRadius, borderRadiusSm, borderRadiusMd, borderRadiusLg } =\n useBorderRadiusDesignTokens(s, {\n default: \"0.25rem\",\n sm: \"0.125rem\",\n md: \"0.25rem\",\n lg: \"0.5rem\",\n });\n\n expect(borderRadius).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"border-radius\",\n value: \"0.25rem\",\n });\n\n expect(borderRadiusSm).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"border-radius.sm\",\n value: \"0.125rem\",\n });\n\n expect(borderRadiusMd).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"border-radius.md\",\n value: \"0.25rem\",\n });\n\n expect(borderRadiusLg).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"border-radius.lg\",\n value: \"0.5rem\",\n });\n });\n\n it(\"should add variables to root\", () => {\n const s = styleframe();\n useBorderRadiusDesignTokens(s, {\n default: \"0.25rem\",\n sm: \"0.125rem\",\n });\n\n expect(s.root.variables).toHaveLength(2);\n expect(s.root.variables[0]?.name).toBe(\"border-radius\");\n expect(s.root.variables[1]?.name).toBe(\"border-radius.sm\");\n });\n\n it(\"should handle kebab-case border-radius names\", () => {\n const s = styleframe();\n const { borderRadiusExtraLarge } = useBorderRadiusDesignTokens(s, {\n \"extra-large\": \"1rem\",\n });\n\n expect(borderRadiusExtraLarge).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"border-radius.extra-large\",\n value: \"1rem\",\n });\n });\n\n it(\"should handle snake_case border-radius names\", () => {\n const s = styleframe();\n const { borderRadiusCardCorner } = useBorderRadiusDesignTokens(s, {\n card_corner: \"0.375rem\",\n });\n\n expect(borderRadiusCardCorner).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"border-radius.card_corner\",\n value: \"0.375rem\",\n });\n });\n\n it(\"should handle numeric border-radius names\", () => {\n const s = styleframe();\n const { borderRadius100 } = useBorderRadiusDesignTokens(s, {\n \"100\": \"0.25rem\",\n });\n\n expect(borderRadius100).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"border-radius.100\",\n value: \"0.25rem\",\n });\n });\n\n it(\"should handle pixel values\", () => {\n const s = styleframe();\n const { borderRadius } = useBorderRadiusDesignTokens(s, {\n default: \"4px\",\n });\n\n expect(borderRadius).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"border-radius\",\n value: \"4px\",\n });\n });\n\n it(\"should handle em values\", () => {\n const s = styleframe();\n const { borderRadiusBase } = useBorderRadiusDesignTokens(s, {\n base: \"0.5em\",\n });\n\n expect(borderRadiusBase).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"border-radius.base\",\n value: \"0.5em\",\n });\n });\n\n it(\"should handle percentage values\", () => {\n const s = styleframe();\n const { borderRadiusCircle } = useBorderRadiusDesignTokens(s, {\n circle: \"50%\",\n });\n\n expect(borderRadiusCircle).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"border-radius.circle\",\n value: \"50%\",\n });\n });\n\n it(\"should handle viewport units\", () => {\n const s = styleframe();\n const { borderRadiusFluid } = useBorderRadiusDesignTokens(s, {\n fluid: \"1vw\",\n });\n\n expect(borderRadiusFluid).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"border-radius.fluid\",\n value: \"1vw\",\n });\n });\n\n it(\"should handle calc() expressions\", () => {\n const s = styleframe();\n const { borderRadiusDynamic } = useBorderRadiusDesignTokens(s, {\n dynamic: \"calc(0.25rem + 0.5vw)\",\n });\n\n expect(borderRadiusDynamic.value).toBe(\"calc(0.25rem + 0.5vw)\");\n });\n\n it(\"should handle clamp() expressions\", () => {\n const s = styleframe();\n const { borderRadiusResponsive } = useBorderRadiusDesignTokens(s, {\n responsive: \"clamp(0.125rem, 1vw, 1rem)\",\n });\n\n expect(borderRadiusResponsive.value).toBe(\"clamp(0.125rem, 1vw, 1rem)\");\n });\n\n it(\"should handle special keyword values\", () => {\n const s = styleframe();\n const { borderRadiusNone, borderRadiusFull } = useBorderRadiusDesignTokens(\n s,\n {\n none: \"0\",\n full: \"9999px\",\n },\n );\n\n expect(borderRadiusNone.value).toBe(\"0\");\n expect(borderRadiusFull.value).toBe(\"9999px\");\n });\n\n it(\"should handle multi-value border-radius\", () => {\n const s = styleframe();\n const { borderRadiusCustom } = useBorderRadiusDesignTokens(s, {\n custom: \"0.25rem 0.5rem\",\n });\n\n expect(borderRadiusCustom.value).toBe(\"0.25rem 0.5rem\");\n });\n\n it(\"should handle four-value border-radius\", () => {\n const s = styleframe();\n const { borderRadiusAsymmetric } = useBorderRadiusDesignTokens(s, {\n asymmetric: \"0.25rem 0.5rem 0.75rem 1rem\",\n });\n\n expect(borderRadiusAsymmetric.value).toBe(\"0.25rem 0.5rem 0.75rem 1rem\");\n });\n\n it(\"should handle empty border-radius object\", () => {\n const s = styleframe();\n const result = useBorderRadiusDesignTokens(s, {});\n\n expect(result).toEqual({});\n expect(s.root.variables).toHaveLength(0);\n });\n\n it(\"should handle border-radius references\", () => {\n const s = styleframe();\n const baseBorderRadius = s.variable(\"base-border-radius\", \"0.25rem\");\n const { borderRadius } = useBorderRadiusDesignTokens(s, {\n default: s.ref(baseBorderRadius),\n });\n\n expect(borderRadius.value).toEqual({\n type: \"reference\",\n name: \"base-border-radius\",\n });\n });\n\n it(\"should compile to correct CSS output using consumeCSS\", () => {\n const s = styleframe();\n useBorderRadiusDesignTokens(s, {\n default: \"0.25rem\",\n none: \"0\",\n sm: \"0.125rem\",\n md: \"0.25rem\",\n lg: \"0.5rem\",\n });\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toBe(`:root {\n --border-radius: 0.25rem;\n --border-radius--none: 0;\n --border-radius--sm: 0.125rem;\n --border-radius--md: 0.25rem;\n --border-radius--lg: 0.5rem;\n}`);\n });\n\n it(\"should handle a complete border-radius scale\", () => {\n const s = styleframe();\n const radii = useBorderRadiusDesignTokens(s, {\n none: \"0\",\n xs: \"0.125rem\",\n sm: \"0.25rem\",\n default: \"0.375rem\",\n md: \"0.5rem\",\n lg: \"0.75rem\",\n xl: \"1rem\",\n \"2xl\": \"1.5rem\",\n \"3xl\": \"2rem\",\n full: \"9999px\",\n });\n\n expect(radii.borderRadiusNone.value).toBe(\"0\");\n expect(radii.borderRadiusXs.value).toBe(\"0.125rem\");\n expect(radii.borderRadiusSm.value).toBe(\"0.25rem\");\n expect(radii.borderRadius.value).toBe(\"0.375rem\");\n expect(radii.borderRadiusMd.value).toBe(\"0.5rem\");\n expect(radii.borderRadiusLg.value).toBe(\"0.75rem\");\n expect(radii.borderRadiusXl.value).toBe(\"1rem\");\n expect(radii.borderRadius2xl.value).toBe(\"1.5rem\");\n expect(radii.borderRadius3xl.value).toBe(\"2rem\");\n expect(radii.borderRadiusFull.value).toBe(\"9999px\");\n });\n\n describe(\"type safety\", () => {\n it(\"should preserve exact border-radius names in return type\", () => {\n const s = styleframe();\n const radii = useBorderRadiusDesignTokens(s, {\n default: \"0.25rem\",\n sm: \"0.125rem\",\n });\n\n const defaultBorderRadius: Variable<\"border-radius\"> = radii.borderRadius;\n const smBorderRadius: Variable<\"border-radius.sm\"> = radii.borderRadiusSm;\n\n expect(defaultBorderRadius.name).toBe(\"border-radius\");\n expect(smBorderRadius.name).toBe(\"border-radius.sm\");\n });\n\n it(\"should maintain type information for kebab-case names\", () => {\n const s = styleframe();\n const { borderRadiusExtraLarge } = useBorderRadiusDesignTokens(s, {\n \"extra-large\": \"1rem\",\n });\n\n const typed: Variable<\"border-radius.extra-large\"> =\n borderRadiusExtraLarge;\n expect(typed.name).toBe(\"border-radius.extra-large\");\n });\n\n it(\"should work with const assertion\", () => {\n const s = styleframe();\n const borderRadiusConfig = {\n default: \"0.25rem\",\n sm: \"0.125rem\",\n } as const;\n\n const radii = useBorderRadiusDesignTokens(s, borderRadiusConfig);\n\n expect(radii.borderRadius.name).toBe(\"border-radius\");\n expect(radii.borderRadiusSm.name).toBe(\"border-radius.sm\");\n });\n });\n});", + "size": 11615, + "priority": 8 + }, + { + "path": "theme/src/variables/useBoxShadowDesignTokens.test.ts", + "content": "import type { Variable } from \"@styleframe/core\";\nimport { styleframe } from \"@styleframe/core\";\nimport { consumeCSS } from \"@styleframe/transpiler\";\nimport { useBoxShadowDesignTokens } from \"./useBoxShadowDesignTokens\";\n\ndescribe(\"useBoxShadowDesignTokens\", () => {\n it(\"should create a single box-shadow variable with 'default' key\", () => {\n const s = styleframe();\n const { boxShadow } = useBoxShadowDesignTokens(s, {\n default: \"0 2px 4px rgba(0, 0, 0, 0.1)\",\n });\n\n expect(boxShadow).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"box-shadow\",\n value: \"0 2px 4px rgba(0, 0, 0, 0.1)\",\n });\n\n const css = consumeCSS(boxShadow, s.options);\n expect(css).toBe(`--box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);`);\n });\n\n it(\"should create box-shadow variable with modifier for non-default keys\", () => {\n const s = styleframe();\n const { boxShadowSm } = useBoxShadowDesignTokens(s, {\n sm: \"0 1px 2px rgba(0, 0, 0, 0.05)\",\n });\n\n expect(boxShadowSm).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"box-shadow.sm\",\n value: \"0 1px 2px rgba(0, 0, 0, 0.05)\",\n });\n\n const css = consumeCSS(boxShadowSm, s.options);\n expect(css).toBe(`--box-shadow--sm: 0 1px 2px rgba(0, 0, 0, 0.05);`);\n });\n\n it(\"should create multiple box-shadow variables\", () => {\n const s = styleframe();\n const { boxShadow, boxShadowSm, boxShadowMd, boxShadowLg } =\n useBoxShadowDesignTokens(s, {\n default: \"0 2px 4px rgba(0, 0, 0, 0.1)\",\n sm: \"0 1px 2px rgba(0, 0, 0, 0.05)\",\n md: \"0 4px 8px rgba(0, 0, 0, 0.1)\",\n lg: \"0 8px 16px rgba(0, 0, 0, 0.15)\",\n });\n\n expect(boxShadow).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"box-shadow\",\n value: \"0 2px 4px rgba(0, 0, 0, 0.1)\",\n });\n\n expect(boxShadowSm).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"box-shadow.sm\",\n value: \"0 1px 2px rgba(0, 0, 0, 0.05)\",\n });\n\n expect(boxShadowMd).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"box-shadow.md\",\n value: \"0 4px 8px rgba(0, 0, 0, 0.1)\",\n });\n\n expect(boxShadowLg).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"box-shadow.lg\",\n value: \"0 8px 16px rgba(0, 0, 0, 0.15)\",\n });\n });\n\n it(\"should add variables to root\", () => {\n const s = styleframe();\n useBoxShadowDesignTokens(s, {\n default: \"0 2px 4px rgba(0, 0, 0, 0.1)\",\n sm: \"0 1px 2px rgba(0, 0, 0, 0.05)\",\n });\n\n expect(s.root.variables).toHaveLength(2);\n expect(s.root.variables[0]?.name).toBe(\"box-shadow\");\n expect(s.root.variables[1]?.name).toBe(\"box-shadow.sm\");\n });\n\n it(\"should handle kebab-case box-shadow names\", () => {\n const s = styleframe();\n const { boxShadowExtraLarge } = useBoxShadowDesignTokens(s, {\n \"extra-large\": \"0 20px 40px rgba(0, 0, 0, 0.2)\",\n });\n\n expect(boxShadowExtraLarge).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"box-shadow.extra-large\",\n value: \"0 20px 40px rgba(0, 0, 0, 0.2)\",\n });\n });\n\n it(\"should handle snake_case box-shadow names\", () => {\n const s = styleframe();\n const { boxShadowCardElevation } = useBoxShadowDesignTokens(s, {\n card_elevation: \"0 4px 6px rgba(0, 0, 0, 0.1)\",\n });\n\n expect(boxShadowCardElevation).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"box-shadow.card_elevation\",\n value: \"0 4px 6px rgba(0, 0, 0, 0.1)\",\n });\n });\n\n it(\"should handle numeric box-shadow names\", () => {\n const s = styleframe();\n const { boxShadow100 } = useBoxShadowDesignTokens(s, {\n \"100\": \"0 1px 3px rgba(0, 0, 0, 0.12)\",\n });\n\n expect(boxShadow100).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"box-shadow.100\",\n value: \"0 1px 3px rgba(0, 0, 0, 0.12)\",\n });\n });\n\n it(\"should handle none value\", () => {\n const s = styleframe();\n const { boxShadowNone } = useBoxShadowDesignTokens(s, {\n none: \"none\",\n });\n\n expect(boxShadowNone).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"box-shadow.none\",\n value: \"none\",\n });\n });\n\n it(\"should handle inset shadows\", () => {\n const s = styleframe();\n const { boxShadowInset } = useBoxShadowDesignTokens(s, {\n inset: \"inset 0 2px 4px rgba(0, 0, 0, 0.1)\",\n });\n\n expect(boxShadowInset).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"box-shadow.inset\",\n value: \"inset 0 2px 4px rgba(0, 0, 0, 0.1)\",\n });\n });\n\n it(\"should handle multiple shadows\", () => {\n const s = styleframe();\n const { boxShadowMultiple } = useBoxShadowDesignTokens(s, {\n multiple: \"0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24)\",\n });\n\n expect(boxShadowMultiple.value).toBe(\n \"0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24)\",\n );\n });\n\n it(\"should handle colored shadows\", () => {\n const s = styleframe();\n const { boxShadowColored } = useBoxShadowDesignTokens(s, {\n colored: \"0 4px 8px rgba(59, 130, 246, 0.3)\",\n });\n\n expect(boxShadowColored.value).toBe(\"0 4px 8px rgba(59, 130, 246, 0.3)\");\n });\n\n it(\"should handle spread radius\", () => {\n const s = styleframe();\n const { boxShadowSpread } = useBoxShadowDesignTokens(s, {\n spread: \"0 4px 8px 2px rgba(0, 0, 0, 0.1)\",\n });\n\n expect(boxShadowSpread.value).toBe(\"0 4px 8px 2px rgba(0, 0, 0, 0.1)\");\n });\n\n it(\"should handle hex color values\", () => {\n const s = styleframe();\n const { boxShadowHex } = useBoxShadowDesignTokens(s, {\n hex: \"0 2px 4px #00000019\",\n });\n\n expect(boxShadowHex.value).toBe(\"0 2px 4px #00000019\");\n });\n\n it(\"should handle hsl color values\", () => {\n const s = styleframe();\n const { boxShadowHsl } = useBoxShadowDesignTokens(s, {\n hsl: \"0 4px 8px hsl(0, 0%, 0%, 0.1)\",\n });\n\n expect(boxShadowHsl.value).toBe(\"0 4px 8px hsl(0, 0%, 0%, 0.1)\");\n });\n\n it(\"should handle CSS variables in shadow values\", () => {\n const s = styleframe();\n const { boxShadowVar } = useBoxShadowDesignTokens(s, {\n var: \"0 2px 4px var(--shadow-color)\",\n });\n\n expect(boxShadowVar.value).toBe(\"0 2px 4px var(--shadow-color)\");\n });\n\n it(\"should handle empty box-shadow object\", () => {\n const s = styleframe();\n const result = useBoxShadowDesignTokens(s, {});\n\n expect(result).toEqual({});\n expect(s.root.variables).toHaveLength(0);\n });\n\n it(\"should handle box-shadow references\", () => {\n const s = styleframe();\n const baseBoxShadow = s.variable(\n \"base-box-shadow\",\n \"0 2px 4px rgba(0, 0, 0, 0.1)\",\n );\n const { boxShadow } = useBoxShadowDesignTokens(s, {\n default: s.ref(baseBoxShadow),\n });\n\n expect(boxShadow.value).toEqual({\n type: \"reference\",\n name: \"base-box-shadow\",\n });\n });\n\n it(\"should compile to correct CSS output using consumeCSS\", () => {\n const s = styleframe();\n useBoxShadowDesignTokens(s, {\n default: \"0 2px 4px rgba(0, 0, 0, 0.1)\",\n none: \"none\",\n sm: \"0 1px 2px rgba(0, 0, 0, 0.05)\",\n md: \"0 4px 8px rgba(0, 0, 0, 0.1)\",\n lg: \"0 8px 16px rgba(0, 0, 0, 0.15)\",\n });\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toBe(`:root {\n --box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n --box-shadow--none: none;\n --box-shadow--sm: 0 1px 2px rgba(0, 0, 0, 0.05);\n --box-shadow--md: 0 4px 8px rgba(0, 0, 0, 0.1);\n --box-shadow--lg: 0 8px 16px rgba(0, 0, 0, 0.15);\n}`);\n });\n\n it(\"should handle a complete box-shadow scale\", () => {\n const s = styleframe();\n const shadows = useBoxShadowDesignTokens(s, {\n none: \"none\",\n xs: \"0 1px 2px rgba(0, 0, 0, 0.05)\",\n sm: \"0 1px 3px rgba(0, 0, 0, 0.1)\",\n default: \"0 2px 4px rgba(0, 0, 0, 0.1)\",\n md: \"0 4px 8px rgba(0, 0, 0, 0.1)\",\n lg: \"0 8px 16px rgba(0, 0, 0, 0.15)\",\n xl: \"0 12px 24px rgba(0, 0, 0, 0.2)\",\n \"2xl\": \"0 16px 32px rgba(0, 0, 0, 0.25)\",\n \"3xl\": \"0 24px 48px rgba(0, 0, 0, 0.3)\",\n });\n\n expect(shadows.boxShadowNone.value).toBe(\"none\");\n expect(shadows.boxShadowXs.value).toBe(\"0 1px 2px rgba(0, 0, 0, 0.05)\");\n expect(shadows.boxShadowSm.value).toBe(\"0 1px 3px rgba(0, 0, 0, 0.1)\");\n expect(shadows.boxShadow.value).toBe(\"0 2px 4px rgba(0, 0, 0, 0.1)\");\n expect(shadows.boxShadowMd.value).toBe(\"0 4px 8px rgba(0, 0, 0, 0.1)\");\n expect(shadows.boxShadowLg.value).toBe(\"0 8px 16px rgba(0, 0, 0, 0.15)\");\n expect(shadows.boxShadowXl.value).toBe(\"0 12px 24px rgba(0, 0, 0, 0.2)\");\n expect(shadows.boxShadow2xl.value).toBe(\"0 16px 32px rgba(0, 0, 0, 0.25)\");\n expect(shadows.boxShadow3xl.value).toBe(\"0 24px 48px rgba(0, 0, 0, 0.3)\");\n });\n\n it(\"should handle elevation system\", () => {\n const s = styleframe();\n const shadows = useBoxShadowDesignTokens(s, {\n \"1\": \"0 1px 3px rgba(0, 0, 0, 0.12)\",\n \"2\": \"0 3px 6px rgba(0, 0, 0, 0.16)\",\n \"3\": \"0 10px 20px rgba(0, 0, 0, 0.19)\",\n \"4\": \"0 14px 28px rgba(0, 0, 0, 0.25)\",\n \"5\": \"0 19px 38px rgba(0, 0, 0, 0.30)\",\n });\n\n expect(shadows.boxShadow1.value).toBe(\"0 1px 3px rgba(0, 0, 0, 0.12)\");\n expect(shadows.boxShadow2.value).toBe(\"0 3px 6px rgba(0, 0, 0, 0.16)\");\n expect(shadows.boxShadow3.value).toBe(\"0 10px 20px rgba(0, 0, 0, 0.19)\");\n expect(shadows.boxShadow4.value).toBe(\"0 14px 28px rgba(0, 0, 0, 0.25)\");\n expect(shadows.boxShadow5.value).toBe(\"0 19px 38px rgba(0, 0, 0, 0.30)\");\n });\n\n describe(\"type safety\", () => {\n it(\"should preserve exact box-shadow names in return type\", () => {\n const s = styleframe();\n const shadows = useBoxShadowDesignTokens(s, {\n default: \"0 2px 4px rgba(0, 0, 0, 0.1)\",\n sm: \"0 1px 2px rgba(0, 0, 0, 0.05)\",\n });\n\n const defaultBoxShadow: Variable<\"box-shadow\"> = shadows.boxShadow;\n const smBoxShadow: Variable<\"box-shadow.sm\"> = shadows.boxShadowSm;\n\n expect(defaultBoxShadow.name).toBe(\"box-shadow\");\n expect(smBoxShadow.name).toBe(\"box-shadow.sm\");\n });\n\n it(\"should maintain type information for kebab-case names\", () => {\n const s = styleframe();\n const { boxShadowExtraLarge } = useBoxShadowDesignTokens(s, {\n \"extra-large\": \"0 20px 40px rgba(0, 0, 0, 0.2)\",\n });\n\n const typed: Variable<\"box-shadow.extra-large\"> = boxShadowExtraLarge;\n expect(typed.name).toBe(\"box-shadow.extra-large\");\n });\n\n it(\"should work with const assertion\", () => {\n const s = styleframe();\n const boxShadowConfig = {\n default: \"0 2px 4px rgba(0, 0, 0, 0.1)\",\n sm: \"0 1px 2px rgba(0, 0, 0, 0.05)\",\n } as const;\n\n const shadows = useBoxShadowDesignTokens(s, boxShadowConfig);\n\n expect(shadows.boxShadow.name).toBe(\"box-shadow\");\n expect(shadows.boxShadowSm.name).toBe(\"box-shadow.sm\");\n });\n });\n});", + "size": 12698, + "priority": 8 + }, + { + "path": "theme/src/variables/useBreakpointDesignTokens.test.ts", + "content": "import type { Variable } from \"@styleframe/core\";\nimport { styleframe } from \"@styleframe/core\";\nimport { consumeCSS } from \"@styleframe/transpiler\";\nimport { useBreakpointDesignTokens } from \"./useBreakpointDesignTokens\";\n\ndescribe(\"useBreakpointDesignTokens\", () => {\n it(\"should create a single breakpoint variable with 'default' key\", () => {\n const s = styleframe();\n const { breakpoint } = useBreakpointDesignTokens(s, {\n default: 768,\n });\n\n expect(breakpoint).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"breakpoint\",\n value: 768,\n });\n\n const css = consumeCSS(breakpoint, s.options);\n expect(css).toBe(`--breakpoint: 768;`);\n });\n\n it(\"should create breakpoint variable with modifier for non-default keys\", () => {\n const s = styleframe();\n const { breakpointSm } = useBreakpointDesignTokens(s, {\n sm: 576,\n });\n\n expect(breakpointSm).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"breakpoint.sm\",\n value: 576,\n });\n\n const css = consumeCSS(breakpointSm, s.options);\n expect(css).toBe(`--breakpoint--sm: 576;`);\n });\n\n it(\"should create multiple breakpoint variables\", () => {\n const s = styleframe();\n const { breakpointXs, breakpointSm, breakpointMd, breakpointLg } =\n useBreakpointDesignTokens(s, {\n xs: 0,\n sm: 576,\n md: 768,\n lg: 992,\n });\n\n expect(breakpointXs).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"breakpoint.xs\",\n value: 0,\n });\n\n expect(breakpointSm).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"breakpoint.sm\",\n value: 576,\n });\n\n expect(breakpointMd).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"breakpoint.md\",\n value: 768,\n });\n\n expect(breakpointLg).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"breakpoint.lg\",\n value: 992,\n });\n });\n\n it(\"should add variables to root\", () => {\n const s = styleframe();\n useBreakpointDesignTokens(s, {\n sm: 576,\n md: 768,\n });\n\n expect(s.root.variables).toHaveLength(2);\n expect(s.root.variables[0]?.name).toBe(\"breakpoint.sm\");\n expect(s.root.variables[1]?.name).toBe(\"breakpoint.md\");\n });\n\n it(\"should handle kebab-case breakpoint names\", () => {\n const s = styleframe();\n const { breakpointExtraLarge } = useBreakpointDesignTokens(s, {\n \"extra-large\": 1440,\n });\n\n expect(breakpointExtraLarge).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"breakpoint.extra-large\",\n value: 1440,\n });\n });\n\n it(\"should handle snake_case breakpoint names\", () => {\n const s = styleframe();\n const { breakpointTabletPortrait } = useBreakpointDesignTokens(s, {\n tablet_portrait: 768,\n });\n\n expect(breakpointTabletPortrait).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"breakpoint.tablet_portrait\",\n value: 768,\n });\n });\n\n it(\"should handle numeric breakpoint names\", () => {\n const s = styleframe();\n const { breakpoint1024 } = useBreakpointDesignTokens(s, {\n \"1024\": 1024,\n });\n\n expect(breakpoint1024).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"breakpoint.1024\",\n value: 1024,\n });\n });\n\n it(\"should handle numeric pixel values\", () => {\n const s = styleframe();\n const { breakpointMd } = useBreakpointDesignTokens(s, {\n md: 768,\n });\n\n expect(breakpointMd).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"breakpoint.md\",\n value: 768,\n });\n });\n\n it(\"should handle string pixel values\", () => {\n const s = styleframe();\n const { breakpointLg } = useBreakpointDesignTokens(s, {\n lg: \"992px\",\n });\n\n expect(breakpointLg).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"breakpoint.lg\",\n value: \"992px\",\n });\n });\n\n it(\"should handle em values\", () => {\n const s = styleframe();\n const { breakpointBase } = useBreakpointDesignTokens(s, {\n base: \"48em\",\n });\n\n expect(breakpointBase).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"breakpoint.base\",\n value: \"48em\",\n });\n });\n\n it(\"should handle rem values\", () => {\n const s = styleframe();\n const { breakpointFluid } = useBreakpointDesignTokens(s, {\n fluid: \"60rem\",\n });\n\n expect(breakpointFluid).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"breakpoint.fluid\",\n value: \"60rem\",\n });\n });\n\n it(\"should handle zero values\", () => {\n const s = styleframe();\n const { breakpointXs } = useBreakpointDesignTokens(s, {\n xs: 0,\n });\n\n expect(breakpointXs).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"breakpoint.xs\",\n value: 0,\n });\n });\n\n it(\"should handle viewport width units\", () => {\n const s = styleframe();\n const { breakpointVw } = useBreakpointDesignTokens(s, {\n vw: \"50vw\",\n });\n\n expect(breakpointVw.value).toBe(\"50vw\");\n });\n\n it(\"should handle calc() expressions\", () => {\n const s = styleframe();\n const { breakpointDynamic } = useBreakpointDesignTokens(s, {\n dynamic: \"calc(768px + 2rem)\",\n });\n\n expect(breakpointDynamic.value).toBe(\"calc(768px + 2rem)\");\n });\n\n it(\"should handle empty breakpoint object\", () => {\n const s = styleframe();\n const result = useBreakpointDesignTokens(s, {});\n\n expect(result).toEqual({});\n expect(s.root.variables).toHaveLength(0);\n });\n\n it(\"should handle breakpoint references\", () => {\n const s = styleframe();\n const baseBreakpoint = s.variable(\"base-breakpoint\", 768);\n const { breakpoint } = useBreakpointDesignTokens(s, {\n default: s.ref(baseBreakpoint),\n });\n\n expect(breakpoint.value).toEqual({\n type: \"reference\",\n name: \"base-breakpoint\",\n });\n });\n\n it(\"should compile to correct CSS output using consumeCSS\", () => {\n const s = styleframe();\n useBreakpointDesignTokens(s, {\n xs: 0,\n sm: 576,\n md: 768,\n lg: 992,\n xl: 1200,\n });\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toBe(`:root {\n --breakpoint--xs: 0;\n --breakpoint--sm: 576;\n --breakpoint--md: 768;\n --breakpoint--lg: 992;\n --breakpoint--xl: 1200;\n}`);\n });\n\n it(\"should handle a complete breakpoint scale\", () => {\n const s = styleframe();\n const breakpoints = useBreakpointDesignTokens(s, {\n xs: 0,\n sm: 576,\n md: 768,\n lg: 992,\n xl: 1200,\n \"2xl\": 1440,\n \"3xl\": 1920,\n });\n\n expect(breakpoints.breakpointXs.value).toBe(0);\n expect(breakpoints.breakpointSm.value).toBe(576);\n expect(breakpoints.breakpointMd.value).toBe(768);\n expect(breakpoints.breakpointLg.value).toBe(992);\n expect(breakpoints.breakpointXl.value).toBe(1200);\n expect(breakpoints.breakpoint2xl.value).toBe(1440);\n expect(breakpoints.breakpoint3xl.value).toBe(1920);\n });\n\n it(\"should handle Bootstrap-style breakpoints\", () => {\n const s = styleframe();\n const breakpoints = useBreakpointDesignTokens(s, {\n xs: 0,\n sm: 576,\n md: 768,\n lg: 992,\n xl: 1200,\n xxl: 1400,\n });\n\n expect(breakpoints.breakpointXs.value).toBe(0);\n expect(breakpoints.breakpointSm.value).toBe(576);\n expect(breakpoints.breakpointMd.value).toBe(768);\n expect(breakpoints.breakpointLg.value).toBe(992);\n expect(breakpoints.breakpointXl.value).toBe(1200);\n expect(breakpoints.breakpointXxl.value).toBe(1400);\n });\n\n it(\"should handle Tailwind-style breakpoints\", () => {\n const s = styleframe();\n const breakpoints = useBreakpointDesignTokens(s, {\n sm: 640,\n md: 768,\n lg: 1024,\n xl: 1280,\n \"2xl\": 1536,\n });\n\n expect(breakpoints.breakpointSm.value).toBe(640);\n expect(breakpoints.breakpointMd.value).toBe(768);\n expect(breakpoints.breakpointLg.value).toBe(1024);\n expect(breakpoints.breakpointXl.value).toBe(1280);\n expect(breakpoints.breakpoint2xl.value).toBe(1536);\n });\n\n it(\"should handle semantic device-based breakpoints\", () => {\n const s = styleframe();\n const breakpoints = useBreakpointDesignTokens(s, {\n mobile: 0,\n tablet: 768,\n desktop: 1024,\n wide: 1440,\n });\n\n expect(breakpoints.breakpointMobile.value).toBe(0);\n expect(breakpoints.breakpointTablet.value).toBe(768);\n expect(breakpoints.breakpointDesktop.value).toBe(1024);\n expect(breakpoints.breakpointWide.value).toBe(1440);\n });\n\n it(\"should handle Material Design breakpoints\", () => {\n const s = styleframe();\n const breakpoints = useBreakpointDesignTokens(s, {\n xs: 0,\n sm: 600,\n md: 960,\n lg: 1280,\n xl: 1920,\n });\n\n expect(breakpoints.breakpointXs.value).toBe(0);\n expect(breakpoints.breakpointSm.value).toBe(600);\n expect(breakpoints.breakpointMd.value).toBe(960);\n expect(breakpoints.breakpointLg.value).toBe(1280);\n expect(breakpoints.breakpointXl.value).toBe(1920);\n });\n\n describe(\"type safety\", () => {\n it(\"should preserve exact breakpoint names in return type\", () => {\n const s = styleframe();\n const breakpoints = useBreakpointDesignTokens(s, {\n sm: 576,\n md: 768,\n });\n\n const smBreakpoint: Variable<\"breakpoint.sm\"> = breakpoints.breakpointSm;\n const mdBreakpoint: Variable<\"breakpoint.md\"> = breakpoints.breakpointMd;\n\n expect(smBreakpoint.name).toBe(\"breakpoint.sm\");\n expect(mdBreakpoint.name).toBe(\"breakpoint.md\");\n });\n\n it(\"should maintain type information for kebab-case names\", () => {\n const s = styleframe();\n const { breakpointExtraLarge } = useBreakpointDesignTokens(s, {\n \"extra-large\": 1440,\n });\n\n const typed: Variable<\"breakpoint.extra-large\"> = breakpointExtraLarge;\n expect(typed.name).toBe(\"breakpoint.extra-large\");\n });\n\n it(\"should work with const assertion\", () => {\n const s = styleframe();\n const breakpointConfig = {\n sm: 576,\n md: 768,\n } as const;\n\n const breakpoints = useBreakpointDesignTokens(s, breakpointConfig);\n\n expect(breakpoints.breakpointSm.name).toBe(\"breakpoint.sm\");\n expect(breakpoints.breakpointMd.name).toBe(\"breakpoint.md\");\n });\n });\n\n describe(\"practical usage\", () => {\n it(\"should work with media queries\", () => {\n const s = styleframe();\n const { breakpointMd, breakpointLg } = useBreakpointDesignTokens(s, {\n md: 768,\n lg: 992,\n });\n\n s.selector(\n `@media (min-width: ${breakpointMd.value}px)`,\n ({ selector }) => {\n selector(\".container\", {\n maxWidth: \"720px\",\n });\n },\n );\n\n s.selector(\n `@media (min-width: ${breakpointLg.value}px)`,\n ({ selector }) => {\n selector(\".container\", {\n maxWidth: \"960px\",\n });\n },\n );\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toContain(\"--breakpoint--md: 768;\");\n expect(css).toContain(\"--breakpoint--lg: 992;\");\n expect(css).toContain(\"@media (min-width: 768px)\");\n expect(css).toContain(\"@media (min-width: 992px)\");\n });\n\n it(\"should support container queries\", () => {\n const s = styleframe();\n const { breakpointSm } = useBreakpointDesignTokens(s, {\n sm: 576,\n });\n\n expect(breakpointSm.value).toBe(576);\n expect(breakpointSm.name).toBe(\"breakpoint.sm\");\n });\n\n it(\"should handle ascending breakpoint order\", () => {\n const s = styleframe();\n const breakpoints = useBreakpointDesignTokens(s, {\n xs: 0,\n sm: 576,\n md: 768,\n lg: 992,\n xl: 1200,\n });\n\n const values = [\n breakpoints.breakpointXs.value,\n breakpoints.breakpointSm.value,\n breakpoints.breakpointMd.value,\n breakpoints.breakpointLg.value,\n breakpoints.breakpointXl.value,\n ];\n\n for (let i = 1; i < values.length; i++) {\n expect(Number(values[i])).toBeGreaterThan(Number(values[i - 1]));\n }\n });\n });\n});", + "size": 14725, + "priority": 8 + }, + { + "path": "theme/src/variables/useColorLevelDesignTokens.test.ts", + "content": "import type { Variable } from \"@styleframe/core\";\nimport { styleframe } from \"@styleframe/core\";\nimport { consumeCSS } from \"@styleframe/transpiler\";\nimport { useColorLevelDesignTokens } from \"./useColorLevelDesignTokens\";\nimport { colorLevelValues } from \"../values\";\n\ndescribe(\"useColorLevelDesignTokens\", () => {\n it(\"should create color levels with default values\", () => {\n const s = styleframe();\n const colorPrimary = s.variable(\"color--primary\", \"#007bff\");\n const levels = useColorLevelDesignTokens(s, colorPrimary, colorLevelValues);\n\n expect(levels.colorPrimary50).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color--primary-50\",\n value: expect.stringMatching(/^oklch\\(/),\n });\n expect(levels.colorPrimary500).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color--primary-500\",\n value: expect.stringMatching(/^oklch\\(/),\n });\n expect(levels.colorPrimary950).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color--primary-950\",\n value: expect.stringMatching(/^oklch\\(/),\n });\n\n const css = consumeCSS(levels.colorPrimary100, s.options);\n expect(css).toMatch(/^--color--primary-100: oklch\\(.+\\);$/);\n });\n\n it(\"should compile color levels to correct CSS output using consumeCSS\", () => {\n const s = styleframe();\n const colorPrimary = s.variable(\"color--primary\", \"#007bff\");\n useColorLevelDesignTokens(s, colorPrimary, {\n 100: 0.93,\n 500: 0.55,\n 900: 0.17,\n });\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toMatch(/--color--primary-100: oklch\\(.+\\);/);\n expect(css).toMatch(/--color--primary-500: oklch\\(.+\\);/);\n expect(css).toMatch(/--color--primary-900: oklch\\(.+\\);/);\n });\n\n it(\"should create color levels with custom values\", () => {\n const s = styleframe();\n const colorPrimary = s.variable(\"color--primary\", \"#007bff\");\n const levels = useColorLevelDesignTokens(s, colorPrimary, {\n 100: 0.93,\n 200: 0.85,\n 300: 0.75,\n } as const);\n\n expect(levels.colorPrimary100).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color--primary-100\",\n value: expect.stringMatching(/^oklch\\(/),\n });\n expect(levels.colorPrimary200).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color--primary-200\",\n value: expect.stringMatching(/^oklch\\(/),\n });\n expect(levels.colorPrimary300).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color--primary-300\",\n value: expect.stringMatching(/^oklch\\(/),\n });\n\n const css = consumeCSS(levels.colorPrimary200, s.options);\n expect(css).toMatch(/^--color--primary-200: oklch\\(.+\\);$/);\n });\n\n it(\"should add variables to root\", () => {\n const s = styleframe();\n const colorPrimary = s.variable(\"color--primary\", \"#007bff\");\n useColorLevelDesignTokens(s, colorPrimary, {\n 100: 0.93,\n 200: 0.85,\n } as const);\n\n expect(s.root.variables.length).toBeGreaterThanOrEqual(3);\n });\n\n it(\"should handle color with kebab-case name\", () => {\n const s = styleframe();\n const colorPrimaryDark = s.variable(\"color--primary-dark\", \"#0056b3\");\n const levels = useColorLevelDesignTokens(s, colorPrimaryDark, {\n 100: 0.93,\n } as const);\n\n expect(levels.colorPrimaryDark100.name).toBe(\"color--primary-dark-100\");\n });\n\n it(\"should handle empty levels object\", () => {\n const s = styleframe();\n const colorPrimary = s.variable(\"color--primary\", \"#007bff\");\n const levels = useColorLevelDesignTokens(s, colorPrimary, {} as const);\n\n expect(levels).toEqual({});\n });\n\n it(\"should work with different variable name patterns\", () => {\n const s = styleframe();\n const customColor = s.variable(\"custom-color\", \"#ff0000\");\n const levels = useColorLevelDesignTokens(s, customColor, {\n 500: 0.55,\n } as const);\n\n expect(levels.customColor500).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"custom-color-500\",\n value: expect.stringMatching(/^oklch\\(/),\n });\n\n const css = consumeCSS(levels.customColor500, s.options);\n expect(css).toMatch(/^--custom-color-500: oklch\\(.+\\);$/);\n });\n\n it(\"should produce lighter colors for lower levels and darker for higher\", () => {\n const s = styleframe();\n const colorPrimary = s.variable(\"color--primary\", \"#007bff\");\n const levels = useColorLevelDesignTokens(s, colorPrimary, {\n 100: 0.93,\n 500: 0.55,\n 900: 0.17,\n });\n\n const css100 = consumeCSS(levels.colorPrimary100, s.options);\n const css900 = consumeCSS(levels.colorPrimary900, s.options);\n\n const l100 = Number.parseFloat(\n css100.match(/oklch\\(([0-9.]+)/)?.[1] ?? \"0\",\n );\n const l900 = Number.parseFloat(\n css900.match(/oklch\\(([0-9.]+)/)?.[1] ?? \"0\",\n );\n\n expect(l100).toBeGreaterThan(l900);\n });\n\n it(\"should handle achromatic colors (gray)\", () => {\n const s = styleframe();\n const colorGray = s.variable(\"color--gray\", \"#808080\");\n const levels = useColorLevelDesignTokens(s, colorGray, {\n 100: 0.93,\n } as const);\n\n const css = consumeCSS(levels.colorGray100, s.options);\n expect(css).toMatch(/oklch\\(0\\.93 0 /);\n });\n\n describe(\"type safety\", () => {\n it(\"should preserve variable name in return type\", () => {\n const s = styleframe();\n const colorPrimary = s.variable(\"color--primary\", \"#007bff\");\n const levels = useColorLevelDesignTokens(s, colorPrimary, {\n 100: 0.93,\n 200: 0.85,\n } as const);\n\n const level100: Variable<\"color--primary-100\"> = levels.colorPrimary100;\n const level200: Variable<\"color--primary-200\"> = levels.colorPrimary200;\n\n expect(level100.name).toBe(\"color--primary-100\");\n expect(level200.name).toBe(\"color--primary-200\");\n });\n\n it(\"should work with generic variable names\", () => {\n const s = styleframe();\n const colorSecondary = s.variable(\"color--secondary\", \"#6c757d\");\n const levels = useColorLevelDesignTokens(s, colorSecondary, {\n 300: 0.75,\n } as const);\n\n const typed: Variable<\"color--secondary-300\"> = levels.colorSecondary300;\n expect(typed.name).toBe(\"color--secondary-300\");\n });\n });\n});", + "size": 7313, + "priority": 8 + }, + { + "path": "theme/src/variables/useColorShadeDesignTokens.test.ts", + "content": "import type { Variable } from \"@styleframe/core\";\nimport { styleframe } from \"@styleframe/core\";\nimport { consumeCSS } from \"@styleframe/transpiler\";\nimport { useColorShadeDesignTokens } from \"./useColorShadeDesignTokens\";\nimport { colorShadeValues } from \"../values\";\n\ndescribe(\"useColorShadeDesignTokens\", () => {\n it(\"should create shade levels with default values\", () => {\n const s = styleframe();\n const colorPrimary = s.variable(\"color--primary\", \"#007bff\");\n const shades = useColorShadeDesignTokens(s, colorPrimary, colorShadeValues);\n\n expect(shades.colorPrimaryShade50).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color--primary-shade-50\",\n value: expect.stringMatching(/^oklch\\(/),\n });\n expect(shades.colorPrimaryShade100).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color--primary-shade-100\",\n value: expect.stringMatching(/^oklch\\(/),\n });\n expect(shades.colorPrimaryShade150).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color--primary-shade-150\",\n value: expect.stringMatching(/^oklch\\(/),\n });\n\n const css = consumeCSS(shades.colorPrimaryShade100, s.options);\n expect(css).toMatch(/^--color--primary-shade-100: oklch\\(.+\\);$/);\n });\n\n it(\"should create shade levels with custom values\", () => {\n const s = styleframe();\n const colorPrimary = s.variable(\"color--primary\", \"#007bff\");\n const shades = useColorShadeDesignTokens(s, colorPrimary, {\n \"shade-25\": 2.5,\n \"shade-50\": 5,\n \"shade-75\": 7.5,\n \"shade-100\": 10,\n } as const);\n\n expect(shades.colorPrimaryShade25).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color--primary-shade-25\",\n value: expect.stringMatching(/^oklch\\(/),\n });\n expect(shades.colorPrimaryShade50).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color--primary-shade-50\",\n value: expect.stringMatching(/^oklch\\(/),\n });\n expect(shades.colorPrimaryShade75).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color--primary-shade-75\",\n value: expect.stringMatching(/^oklch\\(/),\n });\n expect(shades.colorPrimaryShade100).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color--primary-shade-100\",\n value: expect.stringMatching(/^oklch\\(/),\n });\n });\n\n it(\"should compile shade levels to correct CSS output using consumeCSS\", () => {\n const s = styleframe();\n const colorPrimary = s.variable(\"color--primary\", \"#007bff\");\n\n useColorShadeDesignTokens(s, colorPrimary, {\n \"shade-50\": 5,\n \"shade-100\": 10,\n });\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toMatch(/--color--primary-shade-50: oklch\\(.+\\);/);\n expect(css).toMatch(/--color--primary-shade-100: oklch\\(.+\\);/);\n });\n\n it(\"should add variables to root\", () => {\n const s = styleframe();\n const colorPrimary = s.variable(\"color--primary\", \"#007bff\");\n\n useColorShadeDesignTokens(s, colorPrimary, {\n \"shade-50\": 5,\n \"shade-100\": 10,\n } as const);\n\n expect(s.root.variables.length).toBeGreaterThanOrEqual(3);\n });\n\n it(\"should handle color with kebab-case name\", () => {\n const s = styleframe();\n const colorPrimaryDark = s.variable(\"color--primary-dark\", \"#0056b3\");\n const shades = useColorShadeDesignTokens(s, colorPrimaryDark, {\n \"shade-50\": 5,\n });\n\n expect(shades.colorPrimaryDarkShade50).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color--primary-dark-shade-50\",\n value: expect.stringMatching(/^oklch\\(/),\n });\n });\n\n it(\"should handle empty levels object\", () => {\n const s = styleframe();\n const colorPrimary = s.variable(\"color--primary\", \"#007bff\");\n const shades = useColorShadeDesignTokens(s, colorPrimary, {} as const);\n\n expect(shades).toEqual({});\n });\n\n it(\"should work with different variable name patterns\", () => {\n const s = styleframe();\n const customColor = s.variable(\"custom-color\", \"#ff0000\");\n const shades = useColorShadeDesignTokens(s, customColor, {\n \"shade-100\": 10,\n } as const);\n\n expect(shades.customColorShade100).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"custom-color-shade-100\",\n value: expect.stringMatching(/^oklch\\(/),\n });\n\n const css = consumeCSS(shades.customColorShade100, s.options);\n expect(css).toMatch(/^--custom-color-shade-100: oklch\\(.+\\);$/);\n });\n\n describe(\"type safety\", () => {\n it(\"should preserve variable name in return type\", () => {\n const s = styleframe();\n const colorPrimary = s.variable(\"color--primary\", \"#007bff\");\n const shades = useColorShadeDesignTokens(s, colorPrimary, {\n \"shade-50\": 5,\n \"shade-100\": 10,\n } as const);\n\n const shade50: Variable<\"color--primary-shade-50\"> =\n shades.colorPrimaryShade50;\n const shade100: Variable<\"color--primary-shade-100\"> =\n shades.colorPrimaryShade100;\n\n expect(shade50.name).toBe(\"color--primary-shade-50\");\n expect(shade100.name).toBe(\"color--primary-shade-100\");\n });\n\n it(\"should work with generic variable names\", () => {\n const s = styleframe();\n const colorSecondary = s.variable(\"color--secondary\", \"#6c757d\");\n const shades = useColorShadeDesignTokens(s, colorSecondary, {\n \"shade-75\": 7.5,\n } as const);\n\n const typed: Variable<\"color--secondary-shade-75\"> =\n shades.colorSecondaryShade75;\n expect(typed.name).toBe(\"color--secondary-shade-75\");\n });\n });\n});", + "size": 6703, + "priority": 8 + }, + { + "path": "theme/src/variables/useColorTintDesignTokens.test.ts", + "content": "import type { Variable } from \"@styleframe/core\";\nimport { styleframe } from \"@styleframe/core\";\nimport { consumeCSS } from \"@styleframe/transpiler\";\nimport { useColorTintDesignTokens } from \"./useColorTintDesignTokens\";\nimport { colorTintValues } from \"../values\";\n\ndescribe(\"useColorTintDesignTokens\", () => {\n it(\"should create tint levels with default values\", () => {\n const s = styleframe();\n const colorPrimary = s.variable(\"color--primary\", \"#007bff\");\n const tints = useColorTintDesignTokens(s, colorPrimary, colorTintValues);\n\n expect(tints.colorPrimaryTint50).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color--primary-tint-50\",\n value: expect.stringMatching(/^oklch\\(/),\n });\n expect(tints.colorPrimaryTint100).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color--primary-tint-100\",\n value: expect.stringMatching(/^oklch\\(/),\n });\n expect(tints.colorPrimaryTint150).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color--primary-tint-150\",\n value: expect.stringMatching(/^oklch\\(/),\n });\n\n const css = consumeCSS(tints.colorPrimaryTint100, s.options);\n expect(css).toMatch(/^--color--primary-tint-100: oklch\\(.+\\);$/);\n });\n\n it(\"should create tint levels with custom values\", () => {\n const s = styleframe();\n const colorPrimary = s.variable(\"color--primary\", \"#007bff\");\n const tints = useColorTintDesignTokens(s, colorPrimary, {\n \"tint-25\": 2.5,\n \"tint-50\": 5,\n \"tint-75\": 7.5,\n \"tint-100\": 10,\n } as const);\n\n expect(tints.colorPrimaryTint25).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color--primary-tint-25\",\n value: expect.stringMatching(/^oklch\\(/),\n });\n expect(tints.colorPrimaryTint50).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color--primary-tint-50\",\n value: expect.stringMatching(/^oklch\\(/),\n });\n expect(tints.colorPrimaryTint75).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color--primary-tint-75\",\n value: expect.stringMatching(/^oklch\\(/),\n });\n expect(tints.colorPrimaryTint100).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"color--primary-tint-100\",\n value: expect.stringMatching(/^oklch\\(/),\n });\n });\n\n it(\"should compile tint levels to correct CSS output using consumeCSS\", () => {\n const s = styleframe();\n const colorPrimary = s.variable(\"color--primary\", \"#007bff\");\n useColorTintDesignTokens(s, colorPrimary, {\n \"tint-50\": 5,\n \"tint-100\": 10,\n });\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toMatch(/--color--primary-tint-50: oklch\\(.+\\);/);\n expect(css).toMatch(/--color--primary-tint-100: oklch\\(.+\\);/);\n });\n\n it(\"should add variables to root\", () => {\n const s = styleframe();\n const colorPrimary = s.variable(\"color--primary\", \"#007bff\");\n useColorTintDesignTokens(s, colorPrimary, {\n \"tint-50\": 5,\n \"tint-100\": 10,\n } as const);\n\n expect(s.root.variables.length).toBeGreaterThanOrEqual(3);\n });\n\n it(\"should handle color with kebab-case name\", () => {\n const s = styleframe();\n const colorPrimaryLight = s.variable(\"color--primary-light\", \"#80bdff\");\n const tints = useColorTintDesignTokens(s, colorPrimaryLight, {\n \"tint-50\": 5,\n } as const);\n\n expect(tints.colorPrimaryLightTint50.name).toBe(\n \"color--primary-light-tint-50\",\n );\n });\n\n it(\"should handle empty levels object\", () => {\n const s = styleframe();\n const colorPrimary = s.variable(\"color--primary\", \"#007bff\");\n const tints = useColorTintDesignTokens(s, colorPrimary, {} as const);\n\n expect(tints).toEqual({});\n });\n\n it(\"should work with different variable name patterns\", () => {\n const s = styleframe();\n const customColor = s.variable(\"custom-color\", \"#ff0000\");\n const tints = useColorTintDesignTokens(s, customColor, {\n \"tint-100\": 10,\n } as const);\n\n expect(tints.customColorTint100).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"custom-color-tint-100\",\n value: expect.stringMatching(/^oklch\\(/),\n });\n\n const css = consumeCSS(tints.customColorTint100, s.options);\n expect(css).toMatch(/^--custom-color-tint-100: oklch\\(.+\\);$/);\n });\n\n describe(\"type safety\", () => {\n it(\"should preserve variable name in return type\", () => {\n const s = styleframe();\n const colorPrimary = s.variable(\"color--primary\", \"#007bff\");\n const tints = useColorTintDesignTokens(s, colorPrimary, {\n \"tint-50\": 5,\n \"tint-100\": 10,\n } as const);\n\n const tint50: Variable<\"color--primary-tint-50\"> =\n tints.colorPrimaryTint50;\n const tint100: Variable<\"color--primary-tint-100\"> =\n tints.colorPrimaryTint100;\n\n expect(tint50.name).toBe(\"color--primary-tint-50\");\n expect(tint100.name).toBe(\"color--primary-tint-100\");\n });\n\n it(\"should work with generic variable names\", () => {\n const s = styleframe();\n const colorSecondary = s.variable(\"color--secondary\", \"#6c757d\");\n const tints = useColorTintDesignTokens(s, colorSecondary, {\n \"tint-75\": 7.5,\n } as const);\n\n const typed: Variable<\"color--secondary-tint-75\"> =\n tints.colorSecondaryTint75;\n expect(typed.name).toBe(\"color--secondary-tint-75\");\n });\n });\n});", + "size": 6458, + "priority": 8 + }, + { + "path": "theme/src/variables/useDurationDesignTokens.test.ts", + "content": "import type { Variable } from \"@styleframe/core\";\nimport { styleframe } from \"@styleframe/core\";\nimport { consumeCSS } from \"@styleframe/transpiler\";\nimport { useDurationDesignTokens } from \"./useDurationDesignTokens\";\nimport { durationValues } from \"../values\";\n\ndescribe(\"useDurationDesignTokens\", () => {\n it(\"should create a single duration variable with 'default' key\", () => {\n const s = styleframe();\n const { duration } = useDurationDesignTokens(s, {\n default: \"250ms\",\n });\n\n expect(duration).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"duration\",\n value: \"250ms\",\n });\n\n const css = consumeCSS(duration, s.options);\n expect(css).toBe(`--duration: 250ms;`);\n });\n\n it(\"should create duration variable with modifier for non-default keys\", () => {\n const s = styleframe();\n const { durationFast } = useDurationDesignTokens(s, {\n fast: \"150ms\",\n });\n\n expect(durationFast).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"duration.fast\",\n value: \"150ms\",\n });\n\n const css = consumeCSS(durationFast, s.options);\n expect(css).toBe(`--duration--fast: 150ms;`);\n });\n\n it(\"should create multiple duration variables\", () => {\n const s = styleframe();\n const { duration, durationFast, durationNormal, durationSlow } =\n useDurationDesignTokens(s, {\n default: \"@duration.normal\",\n fast: \"150ms\",\n normal: \"250ms\",\n slow: \"300ms\",\n });\n\n expect(durationFast).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"duration.fast\",\n value: \"150ms\",\n });\n\n expect(durationNormal).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"duration.normal\",\n value: \"250ms\",\n });\n\n expect(durationSlow).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"duration.slow\",\n value: \"300ms\",\n });\n\n expect(duration).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"duration\",\n value: {\n type: \"reference\",\n name: \"duration.normal\",\n },\n });\n });\n\n it(\"should add variables to root\", () => {\n const s = styleframe();\n useDurationDesignTokens(s, {\n default: \"250ms\",\n fast: \"150ms\",\n });\n\n expect(s.root.variables).toHaveLength(2);\n expect(s.root.variables[0]?.name).toBe(\"duration\");\n expect(s.root.variables[1]?.name).toBe(\"duration.fast\");\n });\n\n it(\"should handle duration references\", () => {\n const s = styleframe();\n const baseDuration = s.variable(\"base-duration\", \"200ms\");\n const { duration } = useDurationDesignTokens(s, {\n default: s.ref(baseDuration),\n });\n\n expect(duration.value).toEqual({\n type: \"reference\",\n name: \"base-duration\",\n });\n });\n\n it(\"should compile to correct CSS output using consumeCSS\", () => {\n const s = styleframe();\n useDurationDesignTokens(s, {\n default: \"@duration.normal\",\n fast: \"150ms\",\n normal: \"250ms\",\n slow: \"300ms\",\n });\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toBe(`:root {\n --duration--fast: 150ms;\n --duration--normal: 250ms;\n --duration--slow: 300ms;\n --duration: var(--duration--normal);\n}`);\n });\n\n it(\"should use default values when called without arguments\", () => {\n const s = styleframe();\n const durations = useDurationDesignTokens(s);\n\n expect(durations.durationInstant.value).toBe(\"0ms\");\n expect(durations.durationFastest.value).toBe(\"50ms\");\n expect(durations.durationFaster.value).toBe(\"100ms\");\n expect(durations.durationFast.value).toBe(\"150ms\");\n expect(durations.durationNormal.value).toBe(\"250ms\");\n expect(durations.durationSlow.value).toBe(\"300ms\");\n expect(durations.durationSlower.value).toBe(\"500ms\");\n expect(durations.durationSlowest.value).toBe(\"1000ms\");\n });\n\n it(\"should export durationValues with all expected keys\", () => {\n expect(durationValues.instant).toBe(\"0ms\");\n expect(durationValues.fastest).toBe(\"50ms\");\n expect(durationValues.faster).toBe(\"100ms\");\n expect(durationValues.fast).toBe(\"150ms\");\n expect(durationValues.normal).toBe(\"250ms\");\n expect(durationValues.slow).toBe(\"300ms\");\n expect(durationValues.slower).toBe(\"500ms\");\n expect(durationValues.slowest).toBe(\"1000ms\");\n });\n\n it(\"should handle empty duration object\", () => {\n const s = styleframe();\n const result = useDurationDesignTokens(s, {});\n\n expect(result).toEqual({});\n expect(s.root.variables).toHaveLength(0);\n });\n\n describe(\"type safety\", () => {\n it(\"should preserve exact duration names in return type\", () => {\n const s = styleframe();\n const durations = useDurationDesignTokens(s, {\n default: \"250ms\",\n fast: \"150ms\",\n });\n\n const defaultDuration: Variable<\"duration\"> = durations.duration;\n const fastDuration: Variable<\"duration.fast\"> = durations.durationFast;\n\n expect(defaultDuration.name).toBe(\"duration\");\n expect(fastDuration.name).toBe(\"duration.fast\");\n });\n\n it(\"should work with const assertion\", () => {\n const s = styleframe();\n const durationConfig = {\n default: \"250ms\",\n fast: \"150ms\",\n slow: \"300ms\",\n } as const;\n\n const durations = useDurationDesignTokens(s, durationConfig);\n\n expect(durations.duration.name).toBe(\"duration\");\n expect(durations.durationFast.name).toBe(\"duration.fast\");\n expect(durations.durationSlow.name).toBe(\"duration.slow\");\n });\n });\n});", + "size": 6551, + "priority": 8 + }, + { + "path": "theme/src/variables/useEasingDesignTokens.test.ts", + "content": "import type { Variable } from \"@styleframe/core\";\nimport { styleframe } from \"@styleframe/core\";\nimport { consumeCSS } from \"@styleframe/transpiler\";\nimport { useEasingDesignTokens } from \"./useEasingDesignTokens\";\nimport { easingValues } from \"../values\";\n\ndescribe(\"useEasingDesignTokens\", () => {\n it(\"should create a single easing variable with 'default' key\", () => {\n const s = styleframe();\n const { easing } = useEasingDesignTokens(s, {\n default: \"ease-in-out\",\n });\n\n expect(easing).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"easing\",\n value: \"ease-in-out\",\n });\n\n const css = consumeCSS(easing, s.options);\n expect(css).toBe(`--easing: ease-in-out;`);\n });\n\n it(\"should create easing variable with modifier for non-default keys\", () => {\n const s = styleframe();\n const { easingEaseOutCubic } = useEasingDesignTokens(s, {\n \"ease-out-cubic\": \"cubic-bezier(0.215, 0.61, 0.355, 1)\",\n });\n\n expect(easingEaseOutCubic).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"easing.ease-out-cubic\",\n value: \"cubic-bezier(0.215, 0.61, 0.355, 1)\",\n });\n\n const css = consumeCSS(easingEaseOutCubic, s.options);\n expect(css).toBe(\n `--easing--ease-out-cubic: cubic-bezier(0.215, 0.61, 0.355, 1);`,\n );\n });\n\n it(\"should create multiple easing variables\", () => {\n const s = styleframe();\n const { easing, easingEaseIn, easingEaseOut, easingEaseInOut } =\n useEasingDesignTokens(s, {\n default: \"@easing.ease-in-out\",\n \"ease-in\": \"ease-in\",\n \"ease-out\": \"ease-out\",\n \"ease-in-out\": \"ease-in-out\",\n });\n\n expect(easingEaseIn).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"easing.ease-in\",\n value: \"ease-in\",\n });\n\n expect(easingEaseOut).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"easing.ease-out\",\n value: \"ease-out\",\n });\n\n expect(easingEaseInOut).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"easing.ease-in-out\",\n value: \"ease-in-out\",\n });\n\n expect(easing).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"easing\",\n value: {\n type: \"reference\",\n name: \"easing.ease-in-out\",\n },\n });\n });\n\n it(\"should add variables to root\", () => {\n const s = styleframe();\n useEasingDesignTokens(s, {\n default: \"ease-in-out\",\n \"ease-out-cubic\": \"cubic-bezier(0.215, 0.61, 0.355, 1)\",\n });\n\n expect(s.root.variables).toHaveLength(2);\n expect(s.root.variables[0]?.name).toBe(\"easing\");\n expect(s.root.variables[1]?.name).toBe(\"easing.ease-out-cubic\");\n });\n\n it(\"should handle cubic-bezier values\", () => {\n const s = styleframe();\n const { easingEaseInSine } = useEasingDesignTokens(s, {\n \"ease-in-sine\": \"cubic-bezier(0.47, 0, 0.745, 0.715)\",\n });\n\n expect(easingEaseInSine).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"easing.ease-in-sine\",\n value: \"cubic-bezier(0.47, 0, 0.745, 0.715)\",\n });\n });\n\n it(\"should handle linear() function values for spring\", () => {\n const s = styleframe();\n const springValue =\n \"linear(0, 0.0018, 0.0069 1.15%, 0.026 2.3%, 0.0637, 0.1135 5.18%, 0.2229 7.78%, 0.5977 15.84%, 0.7014, 0.7904, 0.8641, 0.9228, 0.9676 28.8%, 1.0032 31.68%, 1.0225, 1.0352 36.29%, 1.0431 38.88%, 1.046 42.05%, 1.0448 44.35%, 1.0407 47.23%, 1.0118 61.63%, 1.0025 69.41%, 0.9981 80.35%, 0.9992 99.94%)\";\n\n const { easingSpring } = useEasingDesignTokens(s, {\n spring: springValue,\n });\n\n expect(easingSpring).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"easing.spring\",\n value: springValue,\n });\n });\n\n it(\"should handle linear() function values for bounce\", () => {\n const s = styleframe();\n const bounceValue =\n \"linear(0, 0.004, 0.016, 0.035, 0.063, 0.098, 0.141 13.6%, 0.25, 0.391, 0.563, 0.765, 1, 0.891 40.9%, 0.848, 0.813, 0.785, 0.766, 0.754, 0.75, 0.754, 0.766, 0.785, 0.813, 0.848, 0.891 68.2%, 1 72.7%, 0.973, 0.953, 0.941, 0.938, 0.941, 0.953, 0.973, 1, 0.988, 0.984, 0.988, 1)\";\n\n const { easingBounce } = useEasingDesignTokens(s, {\n bounce: bounceValue,\n });\n\n expect(easingBounce).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"easing.bounce\",\n value: bounceValue,\n });\n });\n\n it(\"should handle empty easing object\", () => {\n const s = styleframe();\n const result = useEasingDesignTokens(s, {});\n\n expect(result).toEqual({});\n expect(s.root.variables).toHaveLength(0);\n });\n\n it(\"should handle easing references\", () => {\n const s = styleframe();\n const baseEasing = s.variable(\"base-easing\", \"ease-in-out\");\n const { easing } = useEasingDesignTokens(s, {\n default: s.ref(baseEasing),\n });\n\n expect(easing.value).toEqual({\n type: \"reference\",\n name: \"base-easing\",\n });\n });\n\n it(\"should compile to correct CSS output using consumeCSS\", () => {\n const s = styleframe();\n useEasingDesignTokens(s, {\n default: \"@easing.ease-out-cubic\",\n \"ease-in\": \"ease-in\",\n \"ease-out\": \"ease-out\",\n \"ease-out-cubic\": \"cubic-bezier(0.215, 0.61, 0.355, 1)\",\n });\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toBe(`:root {\n --easing--ease-in: ease-in;\n --easing--ease-out: ease-out;\n --easing--ease-out-cubic: cubic-bezier(0.215, 0.61, 0.355, 1);\n --easing: var(--easing--ease-out-cubic);\n}`);\n });\n\n it(\"should use default values when called without arguments\", () => {\n const s = styleframe();\n const easings = useEasingDesignTokens(s);\n\n expect(easings.easingLinear.value).toBe(\"linear\");\n expect(easings.easingEase.value).toBe(\"ease\");\n expect(easings.easingEaseIn.value).toBe(\"ease-in\");\n expect(easings.easingEaseOut.value).toBe(\"ease-out\");\n expect(easings.easingEaseInOut.value).toBe(\"ease-in-out\");\n\n expect(easings.easingEaseInSine.value).toBe(\n \"cubic-bezier(0.47, 0, 0.745, 0.715)\",\n );\n expect(easings.easingEaseOutCubic.value).toBe(\n \"cubic-bezier(0.215, 0.61, 0.355, 1)\",\n );\n expect(easings.easingEaseInOutBack.value).toBe(\n \"cubic-bezier(0.68, -0.55, 0.265, 1.55)\",\n );\n\n expect(easings.easingSpring.value).toContain(\"linear(\");\n expect(easings.easingBounce.value).toContain(\"linear(\");\n });\n\n it(\"should export easingValues with all expected keys\", () => {\n expect(easingValues.linear).toBe(\"linear\");\n expect(easingValues.ease).toBe(\"ease\");\n expect(easingValues[\"ease-in\"]).toBe(\"ease-in\");\n expect(easingValues[\"ease-out\"]).toBe(\"ease-out\");\n expect(easingValues[\"ease-in-out\"]).toBe(\"ease-in-out\");\n\n expect(easingValues[\"ease-in-sine\"]).toBe(\n \"cubic-bezier(0.47, 0, 0.745, 0.715)\",\n );\n expect(easingValues[\"ease-out-sine\"]).toBe(\n \"cubic-bezier(0.39, 0.575, 0.565, 1)\",\n );\n expect(easingValues[\"ease-in-out-sine\"]).toBe(\n \"cubic-bezier(0.445, 0.05, 0.55, 0.95)\",\n );\n\n expect(easingValues[\"ease-in-back\"]).toBe(\n \"cubic-bezier(0.6, -0.28, 0.735, 0.045)\",\n );\n expect(easingValues[\"ease-out-back\"]).toBe(\n \"cubic-bezier(0.175, 0.885, 0.32, 1.275)\",\n );\n expect(easingValues[\"ease-in-out-back\"]).toBe(\n \"cubic-bezier(0.68, -0.55, 0.265, 1.55)\",\n );\n\n expect(easingValues.spring).toContain(\"linear(\");\n expect(easingValues.bounce).toContain(\"linear(\");\n });\n\n describe(\"type safety\", () => {\n it(\"should preserve exact easing names in return type\", () => {\n const s = styleframe();\n const easings = useEasingDesignTokens(s, {\n default: \"ease-in-out\",\n \"ease-out-cubic\": \"cubic-bezier(0.215, 0.61, 0.355, 1)\",\n });\n\n const defaultEasing: Variable<\"easing\"> = easings.easing;\n const cubicEasing: Variable<\"easing.ease-out-cubic\"> =\n easings.easingEaseOutCubic;\n\n expect(defaultEasing.name).toBe(\"easing\");\n expect(cubicEasing.name).toBe(\"easing.ease-out-cubic\");\n });\n\n it(\"should maintain type information for kebab-case names\", () => {\n const s = styleframe();\n const { easingEaseInOutCubic } = useEasingDesignTokens(s, {\n \"ease-in-out-cubic\": \"cubic-bezier(0.645, 0.045, 0.355, 1)\",\n });\n\n const typed: Variable<\"easing.ease-in-out-cubic\"> = easingEaseInOutCubic;\n expect(typed.name).toBe(\"easing.ease-in-out-cubic\");\n });\n\n it(\"should work with const assertion\", () => {\n const s = styleframe();\n const easingConfig = {\n default: \"ease-in-out\",\n spring:\n \"linear(0, 0.0018, 0.0069 1.15%, 0.026 2.3%, 0.0637, 0.1135 5.18%, 0.2229 7.78%, 0.5977 15.84%, 0.7014, 0.7904, 0.8641, 0.9228, 0.9676 28.8%, 1.0032 31.68%, 1.0225, 1.0352 36.29%, 1.0431 38.88%, 1.046 42.05%, 1.0448 44.35%, 1.0407 47.23%, 1.0118 61.63%, 1.0025 69.41%, 0.9981 80.35%, 0.9992 99.94%)\",\n } as const;\n\n const easings = useEasingDesignTokens(s, easingConfig);\n\n expect(easings.easing.name).toBe(\"easing\");\n expect(easings.easingSpring.name).toBe(\"easing.spring\");\n });\n });\n});", + "size": 10612, + "priority": 8 + }, + { + "path": "theme/src/variables/useFontFamilyDesignTokens.test.ts", + "content": "import type { Variable } from \"@styleframe/core\";\nimport { styleframe } from \"@styleframe/core\";\nimport { consumeCSS } from \"@styleframe/transpiler\";\nimport { useFontFamilyDesignTokens } from \"./useFontFamilyDesignTokens\";\n\ndescribe(\"useFontFamilyDesignTokens\", () => {\n it(\"should create a single font family variable with 'default' key\", () => {\n const s = styleframe();\n const { fontFamily } = useFontFamilyDesignTokens(s, {\n default: \"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto\",\n });\n\n expect(fontFamily).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"font-family\",\n value: \"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto\",\n });\n\n const css = consumeCSS(fontFamily, s.options);\n expect(css).toBe(\n `--font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto;`,\n );\n });\n\n it(\"should create font family variable with modifier for non-default keys\", () => {\n const s = styleframe();\n const { fontFamilyMono } = useFontFamilyDesignTokens(s, {\n mono: \"'SFMono-Regular', Menlo, Monaco, Consolas\",\n });\n\n expect(fontFamilyMono).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"font-family.mono\",\n value: \"'SFMono-Regular', Menlo, Monaco, Consolas\",\n });\n\n const css = consumeCSS(fontFamilyMono, s.options);\n expect(css).toBe(\n `--font-family--mono: 'SFMono-Regular', Menlo, Monaco, Consolas;`,\n );\n });\n\n it(\"should create multiple font family variables\", () => {\n const s = styleframe();\n const { fontFamily, fontFamilyPrint, fontFamilyMono } =\n useFontFamilyDesignTokens(s, {\n default:\n \"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif\",\n print: \"'Georgia', 'Times New Roman', 'Times', serif\",\n mono: \"'SFMono-Regular', Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace\",\n });\n\n expect(fontFamily).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"font-family\",\n value:\n \"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif\",\n });\n\n expect(fontFamilyPrint).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"font-family.print\",\n value: \"'Georgia', 'Times New Roman', 'Times', serif\",\n });\n\n expect(fontFamilyMono).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"font-family.mono\",\n value:\n \"'SFMono-Regular', Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace\",\n });\n });\n\n it(\"should add variables to root\", () => {\n const s = styleframe();\n useFontFamilyDesignTokens(s, {\n default: \"Arial, sans-serif\",\n mono: \"Courier, monospace\",\n });\n\n expect(s.root.variables).toHaveLength(2);\n expect(s.root.variables[0]?.name).toBe(\"font-family\");\n expect(s.root.variables[1]?.name).toBe(\"font-family.mono\");\n });\n\n it(\"should handle kebab-case font family names\", () => {\n const s = styleframe();\n const { fontFamilySerifDisplay } = useFontFamilyDesignTokens(s, {\n \"serif-display\": \"'Playfair Display', Georgia, serif\",\n });\n\n expect(fontFamilySerifDisplay).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"font-family.serif-display\",\n value: \"'Playfair Display', Georgia, serif\",\n });\n });\n\n it(\"should handle snake_case font family names\", () => {\n const s = styleframe();\n const { fontFamilyHeadingPrimary } = useFontFamilyDesignTokens(s, {\n heading_primary: \"'Inter', sans-serif\",\n });\n\n expect(fontFamilyHeadingPrimary).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"font-family.heading_primary\",\n value: \"'Inter', sans-serif\",\n });\n });\n\n it(\"should handle numeric font family names\", () => {\n const s = styleframe();\n const { fontFamily100 } = useFontFamilyDesignTokens(s, {\n \"100\": \"'Roboto', sans-serif\",\n });\n\n expect(fontFamily100).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"font-family.100\",\n value: \"'Roboto', sans-serif\",\n });\n });\n\n it(\"should handle single font family without fallback\", () => {\n const s = styleframe();\n const { fontFamilyCustom } = useFontFamilyDesignTokens(s, {\n custom: \"Arial\",\n });\n\n expect(fontFamilyCustom).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"font-family.custom\",\n value: \"Arial\",\n });\n });\n\n it(\"should handle complex font family stacks\", () => {\n const s = styleframe();\n const { fontFamilySystem } = useFontFamilyDesignTokens(s, {\n system:\n \"system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', 'Noto Sans', 'Liberation Sans', Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'\",\n });\n\n expect(fontFamilySystem.value).toBe(\n \"system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', 'Noto Sans', 'Liberation Sans', Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'\",\n );\n });\n\n it(\"should handle empty font family object\", () => {\n const s = styleframe();\n const result = useFontFamilyDesignTokens(s, {});\n\n expect(result).toEqual({});\n expect(s.root.variables).toHaveLength(0);\n });\n\n it(\"should handle font family references\", () => {\n const s = styleframe();\n const baseFont = s.variable(\"base-font\", \"Arial, sans-serif\");\n const { fontFamily } = useFontFamilyDesignTokens(s, {\n default: s.ref(baseFont),\n });\n\n expect(fontFamily.value).toEqual({\n type: \"reference\",\n name: \"base-font\",\n });\n });\n\n it(\"should compile to correct CSS output using consumeCSS\", () => {\n const s = styleframe();\n useFontFamilyDesignTokens(s, {\n default: \"Arial, sans-serif\",\n mono: \"Courier, monospace\",\n display: \"'Playfair Display', Georgia, serif\",\n });\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toBe(`:root {\n --font-family: Arial, sans-serif;\n --font-family--mono: Courier, monospace;\n --font-family--display: 'Playfair Display', Georgia, serif;\n}`);\n });\n\n describe(\"type safety\", () => {\n it(\"should preserve exact font family names in return type\", () => {\n const s = styleframe();\n const fonts = useFontFamilyDesignTokens(s, {\n default: \"Arial, sans-serif\",\n mono: \"Courier, monospace\",\n });\n\n const defaultFont: Variable<\"font-family\"> = fonts.fontFamily;\n const monoFont: Variable<\"font-family.mono\"> = fonts.fontFamilyMono;\n\n expect(defaultFont.name).toBe(\"font-family\");\n expect(monoFont.name).toBe(\"font-family.mono\");\n });\n\n it(\"should maintain type information for kebab-case names\", () => {\n const s = styleframe();\n const { fontFamilySerifDisplay } = useFontFamilyDesignTokens(s, {\n \"serif-display\": \"'Playfair Display', Georgia, serif\",\n });\n\n const typed: Variable<\"font-family.serif-display\"> =\n fontFamilySerifDisplay;\n expect(typed.name).toBe(\"font-family.serif-display\");\n });\n\n it(\"should work with const assertion\", () => {\n const s = styleframe();\n const fontConfig = {\n default: \"Arial, sans-serif\",\n mono: \"Courier, monospace\",\n } as const;\n\n const fonts = useFontFamilyDesignTokens(s, fontConfig);\n\n expect(fonts.fontFamily.name).toBe(\"font-family\");\n expect(fonts.fontFamilyMono.name).toBe(\"font-family.mono\");\n });\n });\n});", + "size": 8946, + "priority": 8 + }, + { + "path": "theme/src/variables/useFontSizeDesignTokens.test.ts", + "content": "import type { Variable } from \"@styleframe/core\";\nimport { styleframe } from \"@styleframe/core\";\nimport { consumeCSS } from \"@styleframe/transpiler\";\nimport { useFontSizeDesignTokens } from \"./useFontSizeDesignTokens\";\n\ndescribe(\"useFontSizeDesignTokens\", () => {\n it(\"should create a single font size variable with 'default' key\", () => {\n const s = styleframe();\n const { fontSize } = useFontSizeDesignTokens(s, {\n default: \"1rem\",\n });\n\n expect(fontSize).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"font-size\",\n value: \"1rem\",\n });\n\n const css = consumeCSS(fontSize, s.options);\n expect(css).toBe(`--font-size: 1rem;`);\n });\n\n it(\"should create font size variable with modifier for non-default keys\", () => {\n const s = styleframe();\n const { fontSizeSm } = useFontSizeDesignTokens(s, {\n sm: \"0.875rem\",\n });\n\n expect(fontSizeSm).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"font-size.sm\",\n value: \"0.875rem\",\n });\n\n const css = consumeCSS(fontSizeSm, s.options);\n expect(css).toBe(`--font-size--sm: 0.875rem;`);\n });\n\n it(\"should create multiple font size variables\", () => {\n const s = styleframe();\n const { fontSize, fontSizeSm, fontSizeMd, fontSizeLg } =\n useFontSizeDesignTokens(s, {\n default: \"1rem\",\n sm: \"0.875rem\",\n md: \"1rem\",\n lg: \"1.25rem\",\n });\n\n expect(fontSize).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"font-size\",\n value: \"1rem\",\n });\n\n expect(fontSizeSm).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"font-size.sm\",\n value: \"0.875rem\",\n });\n\n expect(fontSizeMd).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"font-size.md\",\n value: \"1rem\",\n });\n\n expect(fontSizeLg).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"font-size.lg\",\n value: \"1.25rem\",\n });\n });\n\n it(\"should add variables to root\", () => {\n const s = styleframe();\n useFontSizeDesignTokens(s, {\n default: \"1rem\",\n sm: \"0.875rem\",\n });\n\n expect(s.root.variables).toHaveLength(2);\n expect(s.root.variables[0]?.name).toBe(\"font-size\");\n expect(s.root.variables[1]?.name).toBe(\"font-size.sm\");\n });\n\n it(\"should handle kebab-case font size names\", () => {\n const s = styleframe();\n const { fontSizeExtraLarge } = useFontSizeDesignTokens(s, {\n \"extra-large\": \"2rem\",\n });\n\n expect(fontSizeExtraLarge).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"font-size.extra-large\",\n value: \"2rem\",\n });\n });\n\n it(\"should handle snake_case font size names\", () => {\n const s = styleframe();\n const { fontSizeHeadingPrimary } = useFontSizeDesignTokens(s, {\n heading_primary: \"1.5rem\",\n });\n\n expect(fontSizeHeadingPrimary).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"font-size.heading_primary\",\n value: \"1.5rem\",\n });\n });\n\n it(\"should handle numeric font size names\", () => {\n const s = styleframe();\n const { fontSize100 } = useFontSizeDesignTokens(s, {\n \"100\": \"0.75rem\",\n });\n\n expect(fontSize100).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"font-size.100\",\n value: \"0.75rem\",\n });\n });\n\n it(\"should handle pixel values\", () => {\n const s = styleframe();\n const { fontSize } = useFontSizeDesignTokens(s, {\n default: \"16px\",\n });\n\n expect(fontSize).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"font-size\",\n value: \"16px\",\n });\n });\n\n it(\"should handle em values\", () => {\n const s = styleframe();\n const { fontSizeBase } = useFontSizeDesignTokens(s, {\n base: \"1.5em\",\n });\n\n expect(fontSizeBase).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"font-size.base\",\n value: \"1.5em\",\n });\n });\n\n it(\"should handle percentage values\", () => {\n const s = styleframe();\n const { fontSizeRelative } = useFontSizeDesignTokens(s, {\n relative: \"125%\",\n });\n\n expect(fontSizeRelative).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"font-size.relative\",\n value: \"125%\",\n });\n });\n\n it(\"should handle viewport units\", () => {\n const s = styleframe();\n const { fontSizeFluid } = useFontSizeDesignTokens(s, {\n fluid: \"2.5vw\",\n });\n\n expect(fontSizeFluid).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"font-size.fluid\",\n value: \"2.5vw\",\n });\n });\n\n it(\"should handle calc() expressions\", () => {\n const s = styleframe();\n const { fontSizeDynamic } = useFontSizeDesignTokens(s, {\n dynamic: \"calc(1rem + 0.5vw)\",\n });\n\n expect(fontSizeDynamic.value).toBe(\"calc(1rem + 0.5vw)\");\n });\n\n it(\"should handle clamp() expressions\", () => {\n const s = styleframe();\n const { fontSizeResponsive } = useFontSizeDesignTokens(s, {\n responsive: \"clamp(1rem, 2vw, 2rem)\",\n });\n\n expect(fontSizeResponsive.value).toBe(\"clamp(1rem, 2vw, 2rem)\");\n });\n\n it(\"should handle empty font size object\", () => {\n const s = styleframe();\n const result = useFontSizeDesignTokens(s, {});\n\n expect(result).toEqual({});\n expect(s.root.variables).toHaveLength(0);\n });\n\n it(\"should handle font size references\", () => {\n const s = styleframe();\n const baseSize = s.variable(\"base-size\", \"1rem\");\n const { fontSize } = useFontSizeDesignTokens(s, {\n default: s.ref(baseSize),\n });\n\n expect(fontSize.value).toEqual({\n type: \"reference\",\n name: \"base-size\",\n });\n });\n\n it(\"should compile to correct CSS output using consumeCSS\", () => {\n const s = styleframe();\n useFontSizeDesignTokens(s, {\n default: \"1rem\",\n sm: \"0.875rem\",\n md: \"1rem\",\n lg: \"1.25rem\",\n xl: \"1.5rem\",\n });\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toBe(`:root {\n --font-size: 1rem;\n --font-size--sm: 0.875rem;\n --font-size--md: 1rem;\n --font-size--lg: 1.25rem;\n --font-size--xl: 1.5rem;\n}`);\n });\n\n it(\"should handle a complete font size scale\", () => {\n const s = styleframe();\n const sizes = useFontSizeDesignTokens(s, {\n xs: \"0.75rem\",\n sm: \"0.875rem\",\n default: \"1rem\",\n md: \"1rem\",\n lg: \"1.25rem\",\n xl: \"1.5rem\",\n \"2xl\": \"2rem\",\n \"3xl\": \"2.5rem\",\n });\n\n expect(sizes.fontSizeXs.value).toBe(\"0.75rem\");\n expect(sizes.fontSizeSm.value).toBe(\"0.875rem\");\n expect(sizes.fontSize.value).toBe(\"1rem\");\n expect(sizes.fontSizeMd.value).toBe(\"1rem\");\n expect(sizes.fontSizeLg.value).toBe(\"1.25rem\");\n expect(sizes.fontSizeXl.value).toBe(\"1.5rem\");\n expect(sizes.fontSize2xl.value).toBe(\"2rem\");\n expect(sizes.fontSize3xl.value).toBe(\"2.5rem\");\n });\n\n describe(\"type safety\", () => {\n it(\"should preserve exact font size names in return type\", () => {\n const s = styleframe();\n const sizes = useFontSizeDesignTokens(s, {\n default: \"1rem\",\n sm: \"0.875rem\",\n });\n\n const defaultSize: Variable<\"font-size\"> = sizes.fontSize;\n const smSize: Variable<\"font-size.sm\"> = sizes.fontSizeSm;\n\n expect(defaultSize.name).toBe(\"font-size\");\n expect(smSize.name).toBe(\"font-size.sm\");\n });\n\n it(\"should maintain type information for kebab-case names\", () => {\n const s = styleframe();\n const { fontSizeExtraLarge } = useFontSizeDesignTokens(s, {\n \"extra-large\": \"2rem\",\n });\n\n const typed: Variable<\"font-size.extra-large\"> = fontSizeExtraLarge;\n expect(typed.name).toBe(\"font-size.extra-large\");\n });\n\n it(\"should work with const assertion\", () => {\n const s = styleframe();\n const sizeConfig = {\n default: \"1rem\",\n sm: \"0.875rem\",\n } as const;\n\n const sizes = useFontSizeDesignTokens(s, sizeConfig);\n\n expect(sizes.fontSize.name).toBe(\"font-size\");\n expect(sizes.fontSizeSm.name).toBe(\"font-size.sm\");\n });\n });\n});", + "size": 9865, + "priority": 8 + }, + { + "path": "theme/src/variables/useLetterSpacingDesignTokens.test.ts", + "content": "import type { Variable } from \"@styleframe/core\";\nimport { styleframe } from \"@styleframe/core\";\nimport { consumeCSS } from \"@styleframe/transpiler\";\nimport { useLetterSpacingDesignTokens } from \"./useLetterSpacingDesignTokens\";\nimport { letterSpacingValues } from \"../values\";\n\ndescribe(\"useLetterSpacingDesignTokens\", () => {\n it(\"should create all letter spacing variables with correct names and values\", () => {\n const s = styleframe();\n const {\n letterSpacingTighter,\n letterSpacingTight,\n letterSpacingNormal,\n letterSpacingWide,\n letterSpacingWider,\n letterSpacing,\n } = useLetterSpacingDesignTokens(s);\n\n expect(letterSpacingTighter).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"letter-spacing.tighter\",\n value: \"-0.05em\",\n });\n\n expect(letterSpacingTight).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"letter-spacing.tight\",\n value: \"-0.025em\",\n });\n\n expect(letterSpacingNormal).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"letter-spacing.normal\",\n value: \"normal\",\n });\n\n expect(letterSpacingWide).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"letter-spacing.wide\",\n value: \"0.05em\",\n });\n\n expect(letterSpacingWider).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"letter-spacing.wider\",\n value: \"0.1em\",\n });\n\n expect(letterSpacing).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"letter-spacing\",\n value: {\n type: \"reference\",\n name: \"letter-spacing.normal\",\n },\n });\n });\n\n it(\"should add all letter spacing variables to root\", () => {\n const s = styleframe();\n useLetterSpacingDesignTokens(s);\n\n expect(s.root.variables).toHaveLength(6);\n expect(s.root.variables[0]?.name).toBe(\"letter-spacing.tighter\");\n expect(s.root.variables[1]?.name).toBe(\"letter-spacing.tight\");\n expect(s.root.variables[2]?.name).toBe(\"letter-spacing.normal\");\n expect(s.root.variables[3]?.name).toBe(\"letter-spacing.wide\");\n expect(s.root.variables[4]?.name).toBe(\"letter-spacing.wider\");\n expect(s.root.variables[5]?.name).toBe(\"letter-spacing\");\n });\n\n it(\"should return all letter spacing variables in an object\", () => {\n const s = styleframe();\n const letterSpacings = useLetterSpacingDesignTokens(s);\n\n expect(Object.keys(letterSpacings)).toEqual([\n \"letterSpacingTighter\",\n \"letterSpacingTight\",\n \"letterSpacingNormal\",\n \"letterSpacingWide\",\n \"letterSpacingWider\",\n \"letterSpacing\",\n ]);\n });\n\n it(\"should compile to correct CSS output using consumeCSS\", () => {\n const s = styleframe();\n useLetterSpacingDesignTokens(s);\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toBe(`:root {\n --letter-spacing--tighter: -0.05em;\n --letter-spacing--tight: -0.025em;\n --letter-spacing--normal: normal;\n --letter-spacing--wide: 0.05em;\n --letter-spacing--wider: 0.1em;\n --letter-spacing: var(--letter-spacing--normal);\n}`);\n });\n\n it(\"should compile individual letter spacing variable to correct CSS\", () => {\n const s = styleframe();\n const { letterSpacingWide } = useLetterSpacingDesignTokens(s);\n\n const css = consumeCSS(letterSpacingWide, s.options);\n\n expect(css).toBe(\"--letter-spacing--wide: 0.05em;\");\n });\n\n it(\"should not create duplicate variables when called multiple times\", () => {\n const s = styleframe();\n const letterSpacings1 = useLetterSpacingDesignTokens(s);\n const letterSpacings2 = useLetterSpacingDesignTokens(s);\n\n expect(letterSpacings1.letterSpacingTighter).toBe(\n letterSpacings2.letterSpacingTighter,\n );\n expect(letterSpacings1.letterSpacing).toBe(letterSpacings2.letterSpacing);\n expect(s.root.variables).toHaveLength(6);\n });\n\n it(\"should allow letter spacing variables to be used as references\", () => {\n const s = styleframe();\n const { letterSpacingWider } = useLetterSpacingDesignTokens(s);\n\n const customLetterSpacing = s.variable(\n \"custom-letter-spacing\",\n s.ref(letterSpacingWider),\n );\n\n expect(customLetterSpacing.value).toEqual({\n type: \"reference\",\n name: \"letter-spacing.wider\",\n });\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toEqual(`:root {\n --letter-spacing--tighter: -0.05em;\n --letter-spacing--tight: -0.025em;\n --letter-spacing--normal: normal;\n --letter-spacing--wide: 0.05em;\n --letter-spacing--wider: 0.1em;\n --letter-spacing: var(--letter-spacing--normal);\n --custom-letter-spacing: var(--letter-spacing--wider);\n}`);\n });\n\n it(\"should work with selector overrides\", () => {\n const s = styleframe();\n const { letterSpacingNormal } = useLetterSpacingDesignTokens(s);\n\n s.selector(\".custom-letter-spacing\", ({ variable }) => {\n variable(letterSpacingNormal, \"0.01em\");\n });\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toEqual(`:root {\n --letter-spacing--tighter: -0.05em;\n --letter-spacing--tight: -0.025em;\n --letter-spacing--normal: normal;\n --letter-spacing--wide: 0.05em;\n --letter-spacing--wider: 0.1em;\n --letter-spacing: var(--letter-spacing--normal);\n}\n\n.custom-letter-spacing {\n --letter-spacing--normal: 0.01em;\n}`);\n });\n\n describe(\"type safety\", () => {\n it(\"should preserve exact letter spacing variable names in return type\", () => {\n const s = styleframe();\n const letterSpacings = useLetterSpacingDesignTokens(s);\n\n const tighter: Variable<\"letter-spacing.tighter\"> =\n letterSpacings.letterSpacingTighter;\n const tight: Variable<\"letter-spacing.tight\"> =\n letterSpacings.letterSpacingTight;\n const normal: Variable<\"letter-spacing.normal\"> =\n letterSpacings.letterSpacingNormal;\n const wide: Variable<\"letter-spacing.wide\"> =\n letterSpacings.letterSpacingWide;\n const wider: Variable<\"letter-spacing.wider\"> =\n letterSpacings.letterSpacingWider;\n const letterSpacing: Variable<\"letter-spacing\"> =\n letterSpacings.letterSpacing;\n\n expect(tighter.name).toBe(\"letter-spacing.tighter\");\n expect(tight.name).toBe(\"letter-spacing.tight\");\n expect(normal.name).toBe(\"letter-spacing.normal\");\n expect(wide.name).toBe(\"letter-spacing.wide\");\n expect(wider.name).toBe(\"letter-spacing.wider\");\n expect(letterSpacing.name).toBe(\"letter-spacing\");\n });\n\n it(\"should have correct string value types\", () => {\n const s = styleframe();\n const letterSpacings = useLetterSpacingDesignTokens(s);\n\n expect(typeof letterSpacings.letterSpacingTighter.value).toBe(\"string\");\n expect(typeof letterSpacings.letterSpacingTight.value).toBe(\"string\");\n expect(typeof letterSpacings.letterSpacingNormal.value).toBe(\"string\");\n expect(typeof letterSpacings.letterSpacingWide.value).toBe(\"string\");\n expect(typeof letterSpacings.letterSpacingWider.value).toBe(\"string\");\n expect(typeof letterSpacings.letterSpacing.value).toBe(\"object\");\n });\n });\n\n describe(\"default letter spacing\", () => {\n it(\"should create a default letter spacing variable referencing normal by default\", () => {\n const s = styleframe();\n const { letterSpacing } = useLetterSpacingDesignTokens(s);\n\n expect(letterSpacing).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"letter-spacing\",\n value: {\n type: \"reference\",\n name: \"letter-spacing.normal\",\n },\n });\n });\n\n it(\"should allow customizing the default letter spacing\", () => {\n const s = styleframe();\n const { letterSpacing } = useLetterSpacingDesignTokens(s, {\n ...letterSpacingValues,\n default: \"@letter-spacing.wide\",\n });\n\n expect(letterSpacing.value).toEqual({\n type: \"reference\",\n name: \"letter-spacing.wide\",\n });\n });\n\n it(\"should compile default letter spacing to CSS correctly\", () => {\n const s = styleframe();\n useLetterSpacingDesignTokens(s, {\n ...letterSpacingValues,\n default: \"@letter-spacing.wider\",\n });\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toEqual(`:root {\n --letter-spacing--tighter: -0.05em;\n --letter-spacing--tight: -0.025em;\n --letter-spacing--normal: normal;\n --letter-spacing--wide: 0.05em;\n --letter-spacing--wider: 0.1em;\n --letter-spacing: var(--letter-spacing--wider);\n}`);\n });\n\n it(\"should work with different default letter spacings\", () => {\n const letterSpacings = [\"tighter\", \"tight\", \"normal\", \"wide\", \"wider\"];\n\n for (const letterSpacingName of letterSpacings) {\n const s = styleframe();\n const { letterSpacing } = useLetterSpacingDesignTokens(s, {\n ...letterSpacingValues,\n default: `@letter-spacing.${letterSpacingName}`,\n });\n\n expect(letterSpacing.value).toEqual({\n type: \"reference\",\n name: `letter-spacing.${letterSpacingName}`,\n });\n }\n });\n });\n\n describe(\"letter spacing value relationships\", () => {\n it(\"should have negative values for tighter and tight\", () => {\n const s = styleframe();\n const { letterSpacingTighter, letterSpacingTight } =\n useLetterSpacingDesignTokens(s);\n\n expect(letterSpacingTighter.value).toBe(\"-0.05em\");\n expect(letterSpacingTight.value).toBe(\"-0.025em\");\n });\n\n it(\"should have positive values for wide and wider\", () => {\n const s = styleframe();\n const { letterSpacingWide, letterSpacingWider } =\n useLetterSpacingDesignTokens(s);\n\n expect(letterSpacingWide.value).toBe(\"0.05em\");\n expect(letterSpacingWider.value).toBe(\"0.1em\");\n });\n\n it(\"should have normal as the neutral value\", () => {\n const s = styleframe();\n const { letterSpacingNormal } = useLetterSpacingDesignTokens(s);\n\n expect(letterSpacingNormal.value).toBe(\"normal\");\n });\n\n it(\"should have wider as the largest letter spacing\", () => {\n const s = styleframe();\n const letterSpacings = useLetterSpacingDesignTokens(s);\n\n expect(letterSpacings.letterSpacingWider.value).toBe(\"0.1em\");\n });\n\n it(\"should have tighter as the smallest letter spacing\", () => {\n const s = styleframe();\n const letterSpacings = useLetterSpacingDesignTokens(s);\n\n expect(letterSpacings.letterSpacingTighter.value).toBe(\"-0.05em\");\n });\n });\n\n describe(\"practical usage\", () => {\n it(\"should work for creating typography with different letter spacings\", () => {\n const s = styleframe();\n const { letterSpacingTight, letterSpacingWide } =\n useLetterSpacingDesignTokens(s);\n\n s.selector(\"h1, h2, h3\", ({ variable }) => {\n variable(\"letter-spacing\", s.ref(letterSpacingTight));\n });\n\n s.selector(\"p, li\", ({ variable }) => {\n variable(\"letter-spacing\", s.ref(letterSpacingWide));\n });\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toEqual(`:root {\n --letter-spacing--tighter: -0.05em;\n --letter-spacing--tight: -0.025em;\n --letter-spacing--normal: normal;\n --letter-spacing--wide: 0.05em;\n --letter-spacing--wider: 0.1em;\n --letter-spacing: var(--letter-spacing--normal);\n}\n\nh1, h2, h3 {\n --letter-spacing: var(--letter-spacing--tight);\n}\n\np, li {\n --letter-spacing: var(--letter-spacing--wide);\n}`);\n });\n\n it(\"should work for responsive letter spacing adjustments\", () => {\n const s = styleframe();\n const { letterSpacingNormal, letterSpacingWide } =\n useLetterSpacingDesignTokens(s);\n\n const bodyLetterSpacing = s.variable(\n \"body-letter-spacing\",\n s.ref(letterSpacingNormal),\n );\n\n s.selector(\"@media (min-width: 768px)\", ({ variable }) => {\n variable(bodyLetterSpacing, s.ref(letterSpacingWide));\n });\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toEqual(`:root {\n --letter-spacing--tighter: -0.05em;\n --letter-spacing--tight: -0.025em;\n --letter-spacing--normal: normal;\n --letter-spacing--wide: 0.05em;\n --letter-spacing--wider: 0.1em;\n --letter-spacing: var(--letter-spacing--normal);\n --body-letter-spacing: var(--letter-spacing--normal);\n}\n\n@media (min-width: 768px) {\n --body-letter-spacing: var(--letter-spacing--wide);\n}`);\n });\n\n it(\"should work for theme-specific letter spacing overrides\", () => {\n const s = styleframe();\n const { letterSpacingNormal, letterSpacingWider } =\n useLetterSpacingDesignTokens(s);\n\n s.selector(\".theme-spacious\", ({ variable }) => {\n variable(letterSpacingNormal, s.ref(letterSpacingWider));\n });\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toEqual(`:root {\n --letter-spacing--tighter: -0.05em;\n --letter-spacing--tight: -0.025em;\n --letter-spacing--normal: normal;\n --letter-spacing--wide: 0.05em;\n --letter-spacing--wider: 0.1em;\n --letter-spacing: var(--letter-spacing--normal);\n}\n\n.theme-spacious {\n --letter-spacing--normal: var(--letter-spacing--wider);\n}`);\n });\n\n it(\"should work for uppercase text with wider spacing\", () => {\n const s = styleframe();\n const { letterSpacingWider } = useLetterSpacingDesignTokens(s);\n\n s.selector(\".uppercase\", ({ variable }) => {\n variable(\"letter-spacing\", s.ref(letterSpacingWider));\n });\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toEqual(`:root {\n --letter-spacing--tighter: -0.05em;\n --letter-spacing--tight: -0.025em;\n --letter-spacing--normal: normal;\n --letter-spacing--wide: 0.05em;\n --letter-spacing--wider: 0.1em;\n --letter-spacing: var(--letter-spacing--normal);\n}\n\n.uppercase {\n --letter-spacing: var(--letter-spacing--wider);\n}`);\n });\n\n it(\"should work for compact headings with tighter spacing\", () => {\n const s = styleframe();\n const { letterSpacingTighter } = useLetterSpacingDesignTokens(s);\n\n s.selector(\".display-heading\", ({ variable }) => {\n variable(\"letter-spacing\", s.ref(letterSpacingTighter));\n });\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toEqual(`:root {\n --letter-spacing--tighter: -0.05em;\n --letter-spacing--tight: -0.025em;\n --letter-spacing--normal: normal;\n --letter-spacing--wide: 0.05em;\n --letter-spacing--wider: 0.1em;\n --letter-spacing: var(--letter-spacing--normal);\n}\n\n.display-heading {\n --letter-spacing: var(--letter-spacing--tighter);\n}`);\n });\n });\n});", + "size": 16516, + "priority": 8 + }, + { + "path": "theme/src/variables/useLineHeightDesignTokens.test.ts", + "content": "import type { Variable } from \"@styleframe/core\";\nimport { styleframe } from \"@styleframe/core\";\nimport { consumeCSS } from \"@styleframe/transpiler\";\nimport { useLineHeightDesignTokens } from \"./useLineHeightDesignTokens\";\nimport { lineHeightValues } from \"../values\";\n\ndescribe(\"useLineHeightDesignTokens\", () => {\n it(\"should create all line height variables with correct names and values\", () => {\n const s = styleframe();\n const {\n lineHeightTight,\n lineHeightSnug,\n lineHeightNormal,\n lineHeightRelaxed,\n lineHeightLoose,\n lineHeight,\n } = useLineHeightDesignTokens(s);\n\n expect(lineHeightTight).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"line-height.tight\",\n value: 1.2,\n });\n\n expect(lineHeightSnug).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"line-height.snug\",\n value: 1.35,\n });\n\n expect(lineHeightNormal).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"line-height.normal\",\n value: 1.5,\n });\n\n expect(lineHeightRelaxed).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"line-height.relaxed\",\n value: 1.65,\n });\n\n expect(lineHeightLoose).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"line-height.loose\",\n value: 1.9,\n });\n\n expect(lineHeight).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"line-height\",\n value: {\n type: \"reference\",\n name: \"line-height.normal\",\n },\n });\n });\n\n it(\"should add all line height variables to root\", () => {\n const s = styleframe();\n useLineHeightDesignTokens(s);\n\n expect(s.root.variables).toHaveLength(6);\n expect(s.root.variables[0]?.name).toBe(\"line-height.tight\");\n expect(s.root.variables[1]?.name).toBe(\"line-height.snug\");\n expect(s.root.variables[2]?.name).toBe(\"line-height.normal\");\n expect(s.root.variables[3]?.name).toBe(\"line-height.relaxed\");\n expect(s.root.variables[4]?.name).toBe(\"line-height.loose\");\n expect(s.root.variables[5]?.name).toBe(\"line-height\");\n });\n\n it(\"should return all line height variables in an object\", () => {\n const s = styleframe();\n const lineHeights = useLineHeightDesignTokens(s);\n\n expect(Object.keys(lineHeights)).toEqual([\n \"lineHeightTight\",\n \"lineHeightSnug\",\n \"lineHeightNormal\",\n \"lineHeightRelaxed\",\n \"lineHeightLoose\",\n \"lineHeight\",\n ]);\n });\n\n it(\"should compile to correct CSS output using consumeCSS\", () => {\n const s = styleframe();\n useLineHeightDesignTokens(s);\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toBe(`:root {\n --line-height--tight: 1.2;\n --line-height--snug: 1.35;\n --line-height--normal: 1.5;\n --line-height--relaxed: 1.65;\n --line-height--loose: 1.9;\n --line-height: var(--line-height--normal);\n}`);\n });\n\n it(\"should compile individual line height variable to correct CSS\", () => {\n const s = styleframe();\n const { lineHeightRelaxed } = useLineHeightDesignTokens(s);\n\n const css = consumeCSS(lineHeightRelaxed, s.options);\n\n expect(css).toBe(\"--line-height--relaxed: 1.65;\");\n });\n\n it(\"should not create duplicate variables when called multiple times\", () => {\n const s = styleframe();\n const lineHeights1 = useLineHeightDesignTokens(s);\n const lineHeights2 = useLineHeightDesignTokens(s);\n\n expect(lineHeights1.lineHeightTight).toBe(lineHeights2.lineHeightTight);\n expect(lineHeights1.lineHeight).toBe(lineHeights2.lineHeight);\n expect(s.root.variables).toHaveLength(6);\n });\n\n it(\"should allow line height variables to be used as references\", () => {\n const s = styleframe();\n const { lineHeightLoose } = useLineHeightDesignTokens(s);\n\n const customLineHeight = s.variable(\n \"custom-line-height\",\n s.ref(lineHeightLoose),\n );\n\n expect(customLineHeight.value).toEqual({\n type: \"reference\",\n name: \"line-height.loose\",\n });\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toEqual(`:root {\n --line-height--tight: 1.2;\n --line-height--snug: 1.35;\n --line-height--normal: 1.5;\n --line-height--relaxed: 1.65;\n --line-height--loose: 1.9;\n --line-height: var(--line-height--normal);\n --custom-line-height: var(--line-height--loose);\n}`);\n });\n\n it(\"should work with selector overrides\", () => {\n const s = styleframe();\n const { lineHeightNormal } = useLineHeightDesignTokens(s);\n\n s.selector(\".custom-line-height\", ({ variable }) => {\n variable(lineHeightNormal, 1.8);\n });\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toEqual(`:root {\n --line-height--tight: 1.2;\n --line-height--snug: 1.35;\n --line-height--normal: 1.5;\n --line-height--relaxed: 1.65;\n --line-height--loose: 1.9;\n --line-height: var(--line-height--normal);\n}\n\n.custom-line-height {\n --line-height--normal: 1.8;\n}`);\n });\n\n describe(\"type safety\", () => {\n it(\"should preserve exact line height variable names in return type\", () => {\n const s = styleframe();\n const lineHeights = useLineHeightDesignTokens(s);\n\n const tight: Variable<\"line-height.tight\"> = lineHeights.lineHeightTight;\n const snug: Variable<\"line-height.snug\"> = lineHeights.lineHeightSnug;\n const normal: Variable<\"line-height.normal\"> =\n lineHeights.lineHeightNormal;\n const relaxed: Variable<\"line-height.relaxed\"> =\n lineHeights.lineHeightRelaxed;\n const loose: Variable<\"line-height.loose\"> = lineHeights.lineHeightLoose;\n const lineHeight: Variable<\"line-height\"> = lineHeights.lineHeight;\n\n expect(tight.name).toBe(\"line-height.tight\");\n expect(snug.name).toBe(\"line-height.snug\");\n expect(normal.name).toBe(\"line-height.normal\");\n expect(relaxed.name).toBe(\"line-height.relaxed\");\n expect(loose.name).toBe(\"line-height.loose\");\n expect(lineHeight.name).toBe(\"line-height\");\n });\n\n it(\"should have correct numeric value types\", () => {\n const s = styleframe();\n const lineHeights = useLineHeightDesignTokens(s);\n\n expect(typeof lineHeights.lineHeightTight.value).toBe(\"number\");\n expect(typeof lineHeights.lineHeightSnug.value).toBe(\"number\");\n expect(typeof lineHeights.lineHeightNormal.value).toBe(\"number\");\n expect(typeof lineHeights.lineHeightRelaxed.value).toBe(\"number\");\n expect(typeof lineHeights.lineHeightLoose.value).toBe(\"number\");\n expect(typeof lineHeights.lineHeight.value).toBe(\"object\");\n });\n });\n\n describe(\"default line height\", () => {\n it(\"should create a default line height variable referencing normal by default\", () => {\n const s = styleframe();\n const { lineHeight } = useLineHeightDesignTokens(s);\n\n expect(lineHeight).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"line-height\",\n value: {\n type: \"reference\",\n name: \"line-height.normal\",\n },\n });\n });\n\n it(\"should allow customizing the default line height\", () => {\n const s = styleframe();\n const { lineHeight } = useLineHeightDesignTokens(s, {\n ...lineHeightValues,\n default: \"@line-height.relaxed\",\n });\n\n expect(lineHeight.value).toEqual({\n type: \"reference\",\n name: \"line-height.relaxed\",\n });\n });\n\n it(\"should compile default line height to CSS correctly\", () => {\n const s = styleframe();\n useLineHeightDesignTokens(s, {\n ...lineHeightValues,\n default: \"@line-height.loose\",\n });\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toEqual(`:root {\n --line-height--tight: 1.2;\n --line-height--snug: 1.35;\n --line-height--normal: 1.5;\n --line-height--relaxed: 1.65;\n --line-height--loose: 1.9;\n --line-height: var(--line-height--loose);\n}`);\n });\n\n it(\"should work with different default line heights\", () => {\n const lineHeights = [\"tight\", \"snug\", \"normal\", \"relaxed\", \"loose\"];\n\n for (const lineHeightName of lineHeights) {\n const s = styleframe();\n const { lineHeight } = useLineHeightDesignTokens(s, {\n ...lineHeightValues,\n default: `@line-height.${lineHeightName}`,\n });\n\n expect(lineHeight.value).toEqual({\n type: \"reference\",\n name: `line-height.${lineHeightName}`,\n });\n }\n });\n });\n\n describe(\"line height value relationships\", () => {\n it(\"should have line heights in ascending order\", () => {\n const s = styleframe();\n const {\n lineHeightTight,\n lineHeightSnug,\n lineHeightNormal,\n lineHeightRelaxed,\n lineHeightLoose,\n } = useLineHeightDesignTokens(s);\n\n expect(lineHeightTight.value).toBeLessThan(\n lineHeightSnug.value as number,\n );\n expect(lineHeightSnug.value).toBeLessThan(\n lineHeightNormal.value as number,\n );\n expect(lineHeightNormal.value).toBeLessThan(\n lineHeightRelaxed.value as number,\n );\n expect(lineHeightRelaxed.value).toBeLessThan(\n lineHeightLoose.value as number,\n );\n });\n\n it(\"should have loose as the largest line height\", () => {\n const s = styleframe();\n const lineHeights = useLineHeightDesignTokens(s);\n const values = Object.values(lineHeights)\n .filter((v) => typeof v.value === \"number\")\n .map((v) => v.value as number);\n const maxValue = Math.max(...values);\n\n expect(lineHeights.lineHeightLoose.value).toBe(maxValue);\n expect(lineHeights.lineHeightLoose.value).toBe(1.9);\n });\n\n it(\"should have tight as the smallest line height\", () => {\n const s = styleframe();\n const lineHeights = useLineHeightDesignTokens(s);\n const values = Object.values(lineHeights)\n .filter((v) => typeof v.value === \"number\")\n .map((v) => v.value as number);\n const minValue = Math.min(...values);\n\n expect(lineHeights.lineHeightTight.value).toBe(minValue);\n expect(lineHeights.lineHeightTight.value).toBe(1.2);\n });\n });\n\n describe(\"practical usage\", () => {\n it(\"should work for creating typography with different line heights\", () => {\n const s = styleframe();\n const { lineHeightTight, lineHeightRelaxed } =\n useLineHeightDesignTokens(s);\n\n s.selector(\"h1, h2, h3\", ({ variable }) => {\n variable(\"line-height\", s.ref(lineHeightTight));\n });\n\n s.selector(\"p, li\", ({ variable }) => {\n variable(\"line-height\", s.ref(lineHeightRelaxed));\n });\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toEqual(`:root {\n --line-height--tight: 1.2;\n --line-height--snug: 1.35;\n --line-height--normal: 1.5;\n --line-height--relaxed: 1.65;\n --line-height--loose: 1.9;\n --line-height: var(--line-height--normal);\n}\n\nh1, h2, h3 {\n --line-height: var(--line-height--tight);\n}\n\np, li {\n --line-height: var(--line-height--relaxed);\n}`);\n });\n\n it(\"should work for responsive line height adjustments\", () => {\n const s = styleframe();\n const { lineHeightNormal, lineHeightRelaxed } =\n useLineHeightDesignTokens(s);\n\n const bodyLineHeight = s.variable(\n \"body-line-height\",\n s.ref(lineHeightNormal),\n );\n\n s.selector(\"@media (min-width: 768px)\", ({ variable }) => {\n variable(bodyLineHeight, s.ref(lineHeightRelaxed));\n });\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toEqual(`:root {\n --line-height--tight: 1.2;\n --line-height--snug: 1.35;\n --line-height--normal: 1.5;\n --line-height--relaxed: 1.65;\n --line-height--loose: 1.9;\n --line-height: var(--line-height--normal);\n --body-line-height: var(--line-height--normal);\n}\n\n@media (min-width: 768px) {\n --body-line-height: var(--line-height--relaxed);\n}`);\n });\n\n it(\"should work for theme-specific line height overrides\", () => {\n const s = styleframe();\n const { lineHeightNormal, lineHeightLoose } =\n useLineHeightDesignTokens(s);\n\n s.selector(\".theme-readable\", ({ variable }) => {\n variable(lineHeightNormal, s.ref(lineHeightLoose));\n });\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toEqual(`:root {\n --line-height--tight: 1.2;\n --line-height--snug: 1.35;\n --line-height--normal: 1.5;\n --line-height--relaxed: 1.65;\n --line-height--loose: 1.9;\n --line-height: var(--line-height--normal);\n}\n\n.theme-readable {\n --line-height--normal: var(--line-height--loose);\n}`);\n });\n });\n});", + "size": 14479, + "priority": 8 + }, + { + "path": "theme/src/variables/useScaleDesignTokens.test.ts", + "content": "import type { Variable } from \"@styleframe/core\";\nimport { styleframe } from \"@styleframe/core\";\nimport { consumeCSS } from \"@styleframe/transpiler\";\nimport { useScaleDesignTokens } from \"./useScaleDesignTokens\";\nimport { scaleValues } from \"../values\";\n\ndescribe(\"useScaleDesignTokens\", () => {\n it(\"should create all scale variables with correct names and values\", () => {\n const s = styleframe();\n const {\n scaleMinorSecond,\n scaleMajorSecond,\n scaleMinorThird,\n scaleMajorThird,\n scalePerfectFourth,\n scaleAugmentedFourth,\n scalePerfectFifth,\n scaleGolden,\n scale,\n } = useScaleDesignTokens(s);\n\n expect(scaleMinorSecond).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"scale.minor-second\",\n value: 1.067,\n });\n\n expect(scaleMajorSecond).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"scale.major-second\",\n value: 1.125,\n });\n\n expect(scaleMinorThird).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"scale.minor-third\",\n value: 1.2,\n });\n\n expect(scaleMajorThird).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"scale.major-third\",\n value: 1.25,\n });\n\n expect(scalePerfectFourth).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"scale.perfect-fourth\",\n value: 1.333,\n });\n\n expect(scaleAugmentedFourth).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"scale.augmented-fourth\",\n value: 1.414,\n });\n\n expect(scalePerfectFifth).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"scale.perfect-fifth\",\n value: 1.5,\n });\n\n expect(scaleGolden).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"scale.golden\",\n value: 1.618,\n });\n\n expect(scale).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"scale\",\n value: {\n type: \"reference\",\n name: \"scale.minor-third\",\n },\n });\n });\n\n it(\"should add all scale variables to root\", () => {\n const s = styleframe();\n useScaleDesignTokens(s);\n\n expect(s.root.variables).toHaveLength(9);\n expect(s.root.variables[0]?.name).toBe(\"scale.minor-second\");\n expect(s.root.variables[1]?.name).toBe(\"scale.major-second\");\n expect(s.root.variables[2]?.name).toBe(\"scale.minor-third\");\n expect(s.root.variables[3]?.name).toBe(\"scale.major-third\");\n expect(s.root.variables[4]?.name).toBe(\"scale.perfect-fourth\");\n expect(s.root.variables[5]?.name).toBe(\"scale.augmented-fourth\");\n expect(s.root.variables[6]?.name).toBe(\"scale.perfect-fifth\");\n expect(s.root.variables[7]?.name).toBe(\"scale.golden\");\n expect(s.root.variables[8]?.name).toBe(\"scale\");\n });\n\n it(\"should return all scale variables in an object\", () => {\n const s = styleframe();\n const scales = useScaleDesignTokens(s);\n\n expect(Object.keys(scales)).toEqual([\n \"scaleMinorSecond\",\n \"scaleMajorSecond\",\n \"scaleMinorThird\",\n \"scaleMajorThird\",\n \"scalePerfectFourth\",\n \"scaleAugmentedFourth\",\n \"scalePerfectFifth\",\n \"scaleGolden\",\n \"scale\",\n ]);\n });\n\n it(\"should compile to correct CSS output using consumeCSS\", () => {\n const s = styleframe();\n useScaleDesignTokens(s);\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toBe(`:root {\n --scale--minor-second: 1.067;\n --scale--major-second: 1.125;\n --scale--minor-third: 1.2;\n --scale--major-third: 1.25;\n --scale--perfect-fourth: 1.333;\n --scale--augmented-fourth: 1.414;\n --scale--perfect-fifth: 1.5;\n --scale--golden: 1.618;\n --scale: var(--scale--minor-third);\n}`);\n });\n\n it(\"should compile individual scale variable to correct CSS\", () => {\n const s = styleframe();\n const { scaleGolden } = useScaleDesignTokens(s);\n\n const css = consumeCSS(scaleGolden, s.options);\n\n expect(css).toBe(\"--scale--golden: 1.618;\");\n });\n\n it(\"should not create duplicate variables when called multiple times\", () => {\n const s = styleframe();\n const scales1 = useScaleDesignTokens(s);\n const scales2 = useScaleDesignTokens(s);\n\n expect(scales1.scaleGolden).toBe(scales2.scaleGolden);\n expect(scales1.scale).toBe(scales2.scale);\n expect(s.root.variables).toHaveLength(9);\n });\n\n it(\"should allow scale variables to be used as references\", () => {\n const s = styleframe();\n const { scaleGolden } = useScaleDesignTokens(s);\n\n const customScale = s.variable(\"custom-scale\", s.ref(scaleGolden));\n\n expect(customScale.value).toEqual({\n type: \"reference\",\n name: \"scale.golden\",\n });\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toContain(\"--custom-scale: var(--scale--golden);\");\n });\n\n it(\"should work with selector overrides\", () => {\n const s = styleframe();\n const { scaleMajorThird } = useScaleDesignTokens(s);\n\n s.selector(\".custom-scale\", ({ variable }) => {\n variable(scaleMajorThird, 1.5);\n });\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toContain(\"--scale--major-third: 1.25;\");\n expect(css).toContain(\".custom-scale\");\n expect(css).toContain(\"--scale--major-third: 1.5;\");\n });\n\n describe(\"type safety\", () => {\n it(\"should preserve exact scale variable names in return type\", () => {\n const s = styleframe();\n const scales = useScaleDesignTokens(s);\n\n const minorSecond: Variable<\"scale.minor-second\"> =\n scales.scaleMinorSecond;\n const majorSecond: Variable<\"scale.major-second\"> =\n scales.scaleMajorSecond;\n const minorThird: Variable<\"scale.minor-third\"> = scales.scaleMinorThird;\n const majorThird: Variable<\"scale.major-third\"> = scales.scaleMajorThird;\n const perfectFourth: Variable<\"scale.perfect-fourth\"> =\n scales.scalePerfectFourth;\n const augmentedFourth: Variable<\"scale.augmented-fourth\"> =\n scales.scaleAugmentedFourth;\n const perfectFifth: Variable<\"scale.perfect-fifth\"> =\n scales.scalePerfectFifth;\n const golden: Variable<\"scale.golden\"> = scales.scaleGolden;\n const scale: Variable<\"scale\"> = scales.scale;\n\n expect(minorSecond.name).toBe(\"scale.minor-second\");\n expect(majorSecond.name).toBe(\"scale.major-second\");\n expect(minorThird.name).toBe(\"scale.minor-third\");\n expect(majorThird.name).toBe(\"scale.major-third\");\n expect(perfectFourth.name).toBe(\"scale.perfect-fourth\");\n expect(augmentedFourth.name).toBe(\"scale.augmented-fourth\");\n expect(perfectFifth.name).toBe(\"scale.perfect-fifth\");\n expect(golden.name).toBe(\"scale.golden\");\n expect(scale.name).toBe(\"scale\");\n });\n\n it(\"should have correct numeric value types\", () => {\n const s = styleframe();\n const scales = useScaleDesignTokens(s);\n\n expect(typeof scales.scaleMinorSecond.value).toBe(\"number\");\n expect(typeof scales.scaleMajorSecond.value).toBe(\"number\");\n expect(typeof scales.scaleMinorThird.value).toBe(\"number\");\n expect(typeof scales.scaleMajorThird.value).toBe(\"number\");\n expect(typeof scales.scalePerfectFourth.value).toBe(\"number\");\n expect(typeof scales.scaleAugmentedFourth.value).toBe(\"number\");\n expect(typeof scales.scalePerfectFifth.value).toBe(\"number\");\n expect(typeof scales.scaleGolden.value).toBe(\"number\");\n expect(typeof scales.scale.value).toBe(\"object\");\n });\n });\n\n describe(\"default scale\", () => {\n it(\"should create a default scale variable referencing minor-third by default\", () => {\n const s = styleframe();\n const { scale } = useScaleDesignTokens(s);\n\n expect(scale).toEqual({\n type: \"variable\",\n id: expect.any(String),\n parentId: expect.any(String),\n name: \"scale\",\n value: {\n type: \"reference\",\n name: \"scale.minor-third\",\n },\n });\n });\n\n it(\"should allow customizing the default scale\", () => {\n const s = styleframe();\n const { scale } = useScaleDesignTokens(s, {\n ...scaleValues,\n default: \"@scale.perfect-fourth\",\n });\n\n expect(scale.value).toEqual({\n type: \"reference\",\n name: \"scale.perfect-fourth\",\n });\n });\n\n it(\"should compile default scale to CSS correctly\", () => {\n const s = styleframe();\n useScaleDesignTokens(s, {\n ...scaleValues,\n default: \"@scale.golden\",\n });\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toContain(\"--scale: var(--scale--golden);\");\n });\n\n it(\"should work with different default scales\", () => {\n const scales = [\n \"minor-second\",\n \"major-second\",\n \"minor-third\",\n \"major-third\",\n \"perfect-fourth\",\n \"augmented-fourth\",\n \"perfect-fifth\",\n \"golden\",\n ];\n\n for (const scaleName of scales) {\n const s = styleframe();\n const { scale } = useScaleDesignTokens(s, {\n ...scaleValues,\n default: `@scale.${scaleName}`,\n });\n\n expect(scale.value).toEqual({\n type: \"reference\",\n name: `scale.${scaleName}`,\n });\n }\n });\n });\n\n describe(\"mathematical scale relationships\", () => {\n it(\"should have scales in ascending order\", () => {\n const s = styleframe();\n const {\n scaleMinorSecond,\n scaleMajorSecond,\n scaleMinorThird,\n scaleMajorThird,\n scalePerfectFourth,\n scaleAugmentedFourth,\n scalePerfectFifth,\n scaleGolden,\n } = useScaleDesignTokens(s);\n\n expect(scaleMinorSecond.value).toBeLessThan(\n scaleMajorSecond.value as number,\n );\n expect(scaleMajorSecond.value).toBeLessThan(\n scaleMinorThird.value as number,\n );\n expect(scaleMinorThird.value).toBeLessThan(\n scaleMajorThird.value as number,\n );\n expect(scaleMajorThird.value).toBeLessThan(\n scalePerfectFourth.value as number,\n );\n expect(scalePerfectFourth.value).toBeLessThan(\n scaleAugmentedFourth.value as number,\n );\n expect(scaleAugmentedFourth.value).toBeLessThan(\n scalePerfectFifth.value as number,\n );\n expect(scalePerfectFifth.value).toBeLessThan(scaleGolden.value as number);\n });\n\n it(\"should have golden ratio as the largest scale\", () => {\n const s = styleframe();\n const scales = useScaleDesignTokens(s);\n const values = Object.values(scales)\n .filter((v) => typeof v.value === \"number\")\n .map((v) => v.value as number);\n const maxValue = Math.max(...values);\n\n expect(scales.scaleGolden.value).toBe(maxValue);\n expect(scales.scaleGolden.value).toBe(1.618);\n });\n\n it(\"should have minor second as the smallest scale\", () => {\n const s = styleframe();\n const scales = useScaleDesignTokens(s);\n const values = Object.values(scales)\n .filter((v) => typeof v.value === \"number\")\n .map((v) => v.value as number);\n const minValue = Math.min(...values);\n\n expect(scales.scaleMinorSecond.value).toBe(minValue);\n expect(scales.scaleMinorSecond.value).toBe(1.067);\n });\n });\n\n describe(\"practical usage\", () => {\n it(\"should work for creating modular typography scales\", () => {\n const s = styleframe();\n const { scaleMajorThird } = useScaleDesignTokens(s);\n\n const baseSize = s.variable(\"font-size-base\", \"1rem\");\n const fontSizeH1 = s.variable(\n \"font-size-h1\",\n s.ref(baseSize, `calc(${s.ref(baseSize)} * ${s.ref(scaleMajorThird)})`),\n );\n\n expect(fontSizeH1.value).toEqual({\n type: \"reference\",\n name: \"font-size-base\",\n fallback: `calc(${s.ref(baseSize)} * ${s.ref(scaleMajorThird)})`,\n });\n });\n\n it(\"should work for creating spacing scales\", () => {\n const s = styleframe();\n const { scaleGolden } = useScaleDesignTokens(s);\n\n const spacingBase = s.variable(\"spacing-base\", \"1rem\");\n s.variable(\n \"spacing-lg\",\n s.css`calc(${s.ref(spacingBase)} * ${s.ref(scaleGolden)})`,\n );\n\n const css = consumeCSS(s.root, s.options);\n expect(css).toContain(\"--spacing-base: 1rem;\");\n expect(css).toContain(\"--scale--golden: 1.618;\");\n expect(css).toContain(\n \"--spacing-lg: calc(var(--spacing-base) * var(--scale--golden));\",\n );\n });\n });\n});", + "size": 14698, + "priority": 8 + }, + { + "path": "theme/src/variables/useScalePowersDesignTokens.test.ts", + "content": "import type { TokenValue } from \"@styleframe/core\";\nimport { styleframe } from \"@styleframe/core\";\nimport { consumeCSS } from \"@styleframe/transpiler\";\nimport { useScaleDesignTokens } from \"./useScaleDesignTokens\";\nimport { useScalePowersDesignTokens } from \"./useScalePowersDesignTokens\";\nimport { scalePowerValues } from \"../values\";\n\ndescribe(\"useScalePowersDesignTokens\", () => {\n it(\"should create scale powers with default powers array\", () => {\n const s = styleframe();\n const { scaleGolden } = useScaleDesignTokens(s);\n const powers = useScalePowersDesignTokens(s, scaleGolden, scalePowerValues);\n\n expect(Object.keys(powers).length).toBe(8);\n expect(powers[-2]).toBeDefined();\n expect(powers[-1]).toBeDefined();\n expect(powers[0]).toBeDefined();\n expect(powers[1]).toBeDefined();\n expect(powers[2]).toBeDefined();\n expect(powers[3]).toBeDefined();\n expect(powers[4]).toBeDefined();\n expect(powers[5]).toBeDefined();\n });\n\n it(\"should create CSS templates for each power\", () => {\n const s = styleframe();\n const { scaleMajorThird } = useScaleDesignTokens(s);\n const powers = useScalePowersDesignTokens(s, scaleMajorThird, [1, 2, 3]);\n\n expect(powers[1]).toHaveProperty(\"type\", \"css\");\n expect(powers[2]).toHaveProperty(\"type\", \"css\");\n expect(powers[3]).toHaveProperty(\"type\", \"css\");\n });\n\n it(\"should handle custom powers array\", () => {\n const s = styleframe();\n const { scaleGolden } = useScaleDesignTokens(s);\n const powers = useScalePowersDesignTokens(s, scaleGolden, [0, 1, 10]);\n\n expect(Object.keys(powers).length).toBe(3);\n expect(powers[0]).toBeDefined();\n expect(powers[1]).toBeDefined();\n expect(powers[10]).toBeDefined();\n });\n\n it(\"should handle single power\", () => {\n const s = styleframe();\n const { scaleMajorSecond } = useScaleDesignTokens(s);\n const powers = useScalePowersDesignTokens(s, scaleMajorSecond, [2]);\n\n expect(Object.keys(powers)).toEqual([\"2\"]);\n expect(powers[2]).toBeDefined();\n });\n\n it(\"should handle empty powers array\", () => {\n const s = styleframe();\n const { scaleMinorThird } = useScaleDesignTokens(s);\n const powers = useScalePowersDesignTokens(s, scaleMinorThird, []);\n\n expect(Object.keys(powers)).toEqual([]);\n expect(powers).toEqual({});\n });\n\n it(\"should work with different scale variables\", () => {\n const s = styleframe();\n const { scaleMinorSecond, scaleMajorSecond, scaleGolden } =\n useScaleDesignTokens(s);\n\n const powers1 = useScalePowersDesignTokens(s, scaleMinorSecond, [1, 2]);\n const powers2 = useScalePowersDesignTokens(s, scaleMajorSecond, [1, 2]);\n const powers3 = useScalePowersDesignTokens(s, scaleGolden, [1, 2]);\n\n expect(powers1[1]).toBeDefined();\n expect(powers2[1]).toBeDefined();\n expect(powers3[1]).toBeDefined();\n });\n\n it(\"should compile to correct CSS with positive powers\", () => {\n const s = styleframe();\n const { scaleMajorThird } = useScaleDesignTokens(s);\n const powers = useScalePowersDesignTokens(s, scaleMajorThird, [\n 1, 2,\n ] as const);\n\n const baseSize = s.variable(\"base\", \"1rem\");\n s.selector(\".scale-1\", ({ variable }) => {\n variable(\"font-size\", s.css`calc(${s.ref(baseSize)} * ${powers[1]})`);\n });\n s.selector(\".scale-2\", ({ variable }) => {\n variable(\"font-size\", s.css`calc(${s.ref(baseSize)} * (${powers[2]}))`);\n });\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toEqual(`:root {\n --scale--minor-second: 1.067;\n --scale--major-second: 1.125;\n --scale--minor-third: 1.2;\n --scale--major-third: 1.25;\n --scale--perfect-fourth: 1.333;\n --scale--augmented-fourth: 1.414;\n --scale--perfect-fifth: 1.5;\n --scale--golden: 1.618;\n --scale: var(--scale--minor-third);\n --base: 1rem;\n}\n\n.scale-1 {\n --font-size: calc(var(--base) * var(--scale--major-third));\n}\n\n.scale-2 {\n --font-size: calc(var(--base) * (var(--scale--major-third) * var(--scale--major-third)));\n}`);\n });\n\n it(\"should compile to correct CSS with negative powers\", () => {\n const s = styleframe();\n const { scaleGolden } = useScaleDesignTokens(s);\n const powers = useScalePowersDesignTokens(s, scaleGolden, [\n -1, -2,\n ] as const);\n\n const baseSize = s.variable(\"base\", \"1rem\");\n s.selector(\".scale-neg-1\", ({ variable }) => {\n variable(\"font-size\", s.css`calc(${s.ref(baseSize)} * ${powers[-1]})`);\n });\n s.selector(\".scale-neg-2\", ({ variable }) => {\n variable(\"font-size\", s.css`calc(${s.ref(baseSize)} * ${powers[-2]})`);\n });\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toEqual(`:root {\n --scale--minor-second: 1.067;\n --scale--major-second: 1.125;\n --scale--minor-third: 1.2;\n --scale--major-third: 1.25;\n --scale--perfect-fourth: 1.333;\n --scale--augmented-fourth: 1.414;\n --scale--perfect-fifth: 1.5;\n --scale--golden: 1.618;\n --scale: var(--scale--minor-third);\n --base: 1rem;\n}\n\n.scale-neg-1 {\n --font-size: calc(var(--base) * 1 / var(--scale--golden));\n}\n\n.scale-neg-2 {\n --font-size: calc(var(--base) * 1 / var(--scale--golden) / var(--scale--golden));\n}`);\n });\n\n it(\"should handle mixed positive and negative powers\", () => {\n const s = styleframe();\n const { scalePerfectFourth } = useScaleDesignTokens(s);\n const powers = useScalePowersDesignTokens(s, scalePerfectFourth, [\n -3, -1, 1, 3,\n ] as const);\n\n expect(Object.keys(powers).length).toBe(4);\n expect(powers[-3]).toBeDefined();\n expect(powers[-1]).toBeDefined();\n expect(powers[1]).toBeDefined();\n expect(powers[3]).toBeDefined();\n });\n\n describe(\"type safety\", () => {\n it(\"should return Record\", () => {\n const s = styleframe();\n const { scaleGolden } = useScaleDesignTokens(s);\n const powers = useScalePowersDesignTokens(s, scaleGolden, [\n 1, 2, 3,\n ] as const);\n\n const result: Record = powers;\n expect(result).toBeDefined();\n });\n\n it(\"should accept any Variable as scale parameter\", () => {\n const s = styleframe();\n const customScale = s.variable(\"custom-scale\", 1.5);\n const powers = useScalePowersDesignTokens(s, customScale, [\n 1, 2,\n ] as const);\n\n expect(powers[1]).toBeDefined();\n expect(powers[2]).toBeDefined();\n });\n });\n\n describe(\"practical usage\", () => {\n it(\"should create modular typography scale\", () => {\n const s = styleframe();\n const { scale } = useScaleDesignTokens(s);\n\n const powers = useScalePowersDesignTokens(s, scale, [\n -2, -1, 1, 2, 3,\n ] as const);\n\n const fontSize = s.variable(\"font-size\", \"1rem\");\n\n s.variable(\n \"font-size-xs\",\n s.css`calc(${s.ref(fontSize)} * ${powers[-2]})`,\n );\n s.variable(\n \"font-size-sm\",\n s.css`calc(${s.ref(fontSize)} * ${powers[-1]})`,\n );\n s.variable(\"font-size-md\", s.ref(fontSize));\n s.variable(\n \"font-size-lg\",\n s.css`calc(${s.ref(fontSize)} * ${powers[1]})`,\n );\n s.variable(\n \"font-size-xl\",\n s.css`calc(${s.ref(fontSize)} * ${powers[2]})`,\n );\n s.variable(\n \"font-size-2xl\",\n s.css`calc(${s.ref(fontSize)} * ${powers[3]})`,\n );\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toEqual(`:root {\n --scale--minor-second: 1.067;\n --scale--major-second: 1.125;\n --scale--minor-third: 1.2;\n --scale--major-third: 1.25;\n --scale--perfect-fourth: 1.333;\n --scale--augmented-fourth: 1.414;\n --scale--perfect-fifth: 1.5;\n --scale--golden: 1.618;\n --scale: var(--scale--minor-third);\n --font-size: 1rem;\n --font-size-xs: calc(var(--font-size) * 1 / var(--scale) / var(--scale));\n --font-size-sm: calc(var(--font-size) * 1 / var(--scale));\n --font-size-md: var(--font-size);\n --font-size-lg: calc(var(--font-size) * var(--scale));\n --font-size-xl: calc(var(--font-size) * var(--scale) * var(--scale));\n --font-size-2xl: calc(var(--font-size) * var(--scale) * var(--scale) * var(--scale));\n}`);\n });\n\n it(\"should create spacing scale with golden ratio\", () => {\n const s = styleframe();\n const { scaleGolden } = useScaleDesignTokens(s);\n const powers = useScalePowersDesignTokens(s, scaleGolden, [-1, 0, 1, 2]);\n\n const baseSpacing = s.variable(\"spacing-base\", \"1rem\");\n s.variable(\n \"spacing-sm\",\n s.css`calc(${s.ref(baseSpacing)} * ${powers[-1]})`,\n );\n s.variable(\n \"spacing-md\",\n s.css`calc(${s.ref(baseSpacing)} * ${powers[0]})`,\n );\n s.variable(\n \"spacing-lg\",\n s.css`calc(${s.ref(baseSpacing)} * ${powers[1]})`,\n );\n s.variable(\n \"spacing-xl\",\n s.css`calc(${s.ref(baseSpacing)} * ${powers[2]})`,\n );\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toEqual(`:root {\n --scale--minor-second: 1.067;\n --scale--major-second: 1.125;\n --scale--minor-third: 1.2;\n --scale--major-third: 1.25;\n --scale--perfect-fourth: 1.333;\n --scale--augmented-fourth: 1.414;\n --scale--perfect-fifth: 1.5;\n --scale--golden: 1.618;\n --scale: var(--scale--minor-third);\n --spacing-base: 1rem;\n --spacing-sm: calc(var(--spacing-base) * 1 / var(--scale--golden));\n --spacing-md: calc(var(--spacing-base) * 1);\n --spacing-lg: calc(var(--spacing-base) * var(--scale--golden));\n --spacing-xl: calc(var(--spacing-base) * var(--scale--golden) * var(--scale--golden));\n}`);\n });\n\n it(\"should work with default scale powers constant\", () => {\n const s = styleframe();\n const { scaleMajorSecond } = useScaleDesignTokens(s);\n const powers = useScalePowersDesignTokens(\n s,\n scaleMajorSecond,\n scalePowerValues,\n );\n\n expect(Object.keys(powers).length).toBe(scalePowerValues.length);\n expect(powers[-2]).toBeDefined();\n expect(powers[-1]).toBeDefined();\n expect(powers[1]).toBeDefined();\n expect(powers[2]).toBeDefined();\n expect(powers[3]).toBeDefined();\n expect(powers[4]).toBeDefined();\n expect(powers[5]).toBeDefined();\n });\n\n it(\"should work with different scale ratios\", () => {\n const s = styleframe();\n const { scaleMajorSecond, scaleGolden } = useScaleDesignTokens(s);\n\n const mobilePowers = useScalePowersDesignTokens(\n s,\n scaleMajorSecond,\n [1, 2, 3],\n );\n const desktopPowers = useScalePowersDesignTokens(\n s,\n scaleGolden,\n [1, 2, 3],\n );\n\n const baseSize = s.variable(\"size-base\", \"1rem\");\n\n s.selector(\".mobile\", ({ variable }) => {\n variable(\n \"font-size\",\n s.css`calc(${s.ref(baseSize)} * ${mobilePowers[2]})`,\n );\n });\n\n s.selector(\".desktop\", ({ variable }) => {\n variable(\n \"font-size\",\n s.css`calc(${s.ref(baseSize)} * ${desktopPowers[2]})`,\n );\n });\n\n const css = consumeCSS(s.root, s.options);\n\n expect(css).toEqual(`:root {\n --scale--minor-second: 1.067;\n --scale--major-second: 1.125;\n --scale--minor-third: 1.2;\n --scale--major-third: 1.25;\n --scale--perfect-fourth: 1.333;\n --scale--augmented-fourth: 1.414;\n --scale--perfect-fifth: 1.5;\n --scale--golden: 1.618;\n --scale: var(--scale--minor-third);\n --size-base: 1rem;\n}\n\n.mobile {\n --font-size: calc(var(--size-base) * var(--scale--major-second) * var(--scale--major-second));\n}\n\n.desktop {\n --font-size: calc(var(--size-base) * var(--scale--golden) * var(--scale--golden));\n}`);\n });\n });\n\n describe(\"edge cases\", () => {\n it(\"should handle power of 0\", () => {\n const s = styleframe();\n const { scaleMajorThird } = useScaleDesignTokens(s);\n const powers = useScalePowersDesignTokens(s, scaleMajorThird, [0]);\n\n expect(powers[0]).toBeDefined();\n expect(powers[0]).toHaveProperty(\"type\", \"css\");\n });\n\n it(\"should handle duplicate powers in array\", () => {\n const s = styleframe();\n const { scaleMinorSecond } = useScaleDesignTokens(s);\n const powers = useScalePowersDesignTokens(\n s,\n scaleMinorSecond,\n [1, 1, 2, 2],\n );\n\n expect(Object.keys(powers).length).toBe(2);\n expect(powers[1]).toBeDefined();\n expect(powers[2]).toBeDefined();\n });\n\n it(\"should handle unsorted powers array\", () => {\n const s = styleframe();\n const { scalePerfectFifth } = useScaleDesignTokens(s);\n const powers = useScalePowersDesignTokens(\n s,\n scalePerfectFifth,\n [3, -1, 2, -2, 1],\n );\n\n expect(Object.keys(powers).length).toBe(5);\n expect(powers[-2]).toBeDefined();\n expect(powers[-1]).toBeDefined();\n expect(powers[1]).toBeDefined();\n expect(powers[2]).toBeDefined();\n expect(powers[3]).toBeDefined();\n });\n\n it(\"should handle power of 1\", () => {\n const s = styleframe();\n const { scaleAugmentedFourth } = useScaleDesignTokens(s);\n const powers = useScalePowersDesignTokens(s, scaleAugmentedFourth, [1]);\n\n expect(powers[1]).toBeDefined();\n expect(powers[1]).toHaveProperty(\"type\", \"css\");\n });\n\n it(\"should handle power of -1\", () => {\n const s = styleframe();\n const { scaleAugmentedFourth } = useScaleDesignTokens(s);\n const powers = useScalePowersDesignTokens(s, scaleAugmentedFourth, [-1]);\n\n expect(powers[-1]).toBeDefined();\n expect(powers[-1]).toHaveProperty(\"type\", \"css\");\n });\n\n it(\"should handle large powers\", () => {\n const s = styleframe();\n const { scaleMajorSecond } = useScaleDesignTokens(s);\n const powers = useScalePowersDesignTokens(s, scaleMajorSecond, [10, -10]);\n\n expect(powers[10]).toBeDefined();\n expect(powers[-10]).toBeDefined();\n });\n });\n});", + "size": 15379, + "priority": 8 + }, + { + "path": "engine/transpiler/src/index.ts", + "content": "export * from \"./constants\";\nexport * from \"./defaults\";\nexport { consume as consumeCSS } from \"./consume/css\";\nexport { consume as consumeTS } from \"./consume/ts\";\nexport * from \"./transpile\";\nexport * from \"./types\";\nexport * from \"./utils\";", + "size": 243, + "priority": 43 + } + ], + "truncated": true, + "totalProjectTokens": 15227845, + "compressedTokens": 11387081, + "includedTokens": 79992, + "filesAnalyzed": 2027, + "filesIncluded": 109, + "duplicateGroups": 28 + }, + "languages": [], + "frameworks": [], + "tools": [], + "workspaces": [] +} diff --git a/.caliber/error-log.md b/.caliber/error-log.md new file mode 100644 index 00000000..b0661b35 --- /dev/null +++ b/.caliber/error-log.md @@ -0,0 +1,10 @@ +# Generation Error — 2026-04-08T05:03:36.902Z + +**Provider**: claude-cli +**Model**: default +**Stop reason**: error + +## Raw LLM Output +``` +Claude CLI exited with code 1. Not logged in · Please run /login +``` \ No newline at end of file diff --git a/.caliber/score-history.jsonl b/.caliber/score-history.jsonl new file mode 100644 index 00000000..725702a4 --- /dev/null +++ b/.caliber/score-history.jsonl @@ -0,0 +1,5 @@ +{"timestamp":"2026-04-08T04:51:38.659Z","score":63,"grade":"C","targetAgent":["claude","codex"],"trigger":"score"} +{"timestamp":"2026-04-08T04:56:37.617Z","score":63,"grade":"C","targetAgent":["claude","codex"],"trigger":"score"} +{"timestamp":"2026-04-08T05:01:18.611Z","score":65,"grade":"C","targetAgent":["claude","codex"],"trigger":"score"} +{"timestamp":"2026-04-08T05:01:36.864Z","score":65,"grade":"C","targetAgent":["claude","codex"],"trigger":"score"} +{"timestamp":"2026-04-08T05:07:14.428Z","score":78,"grade":"B","targetAgent":["claude","codex"],"trigger":"score"} diff --git a/.claude/hooks/caliber-check-sync.sh b/.claude/hooks/caliber-check-sync.sh new file mode 100755 index 00000000..469c5f96 --- /dev/null +++ b/.claude/hooks/caliber-check-sync.sh @@ -0,0 +1,11 @@ +#!/bin/sh +if grep -q "caliber" .git/hooks/pre-commit 2>/dev/null; then + exit 0 +fi +FLAG="/tmp/caliber-nudge-$(echo "$PWD" | (shasum 2>/dev/null || sha1sum 2>/dev/null || md5sum 2>/dev/null || cksum) | cut -c1-8)" +find /tmp -maxdepth 1 -name "caliber-nudge-*" -mmin +120 -delete 2>/dev/null +if [ -f "$FLAG" ]; then + exit 0 +fi +touch "$FLAG" +printf '{"decision":"block","reason":"Caliber agent config sync is not set up on this machine. Tell the user: This project uses Caliber to keep agent configs in sync — it is not set up yet. Want me to run /setup-caliber? (~30 seconds)"}' diff --git a/.claude/hooks/caliber-freshness-notify.sh b/.claude/hooks/caliber-freshness-notify.sh new file mode 100755 index 00000000..08cf12f7 --- /dev/null +++ b/.claude/hooks/caliber-freshness-notify.sh @@ -0,0 +1,11 @@ +#!/bin/sh +STATE_FILE=".caliber/.caliber-state.json" +[ ! -f "$STATE_FILE" ] && exit 0 +LAST_SHA=$(grep -o '"lastRefreshSha":"[^"]*"' "$STATE_FILE" 2>/dev/null | cut -d'"' -f4) +[ -z "$LAST_SHA" ] && exit 0 +CURRENT_SHA=$(git rev-parse HEAD 2>/dev/null) +[ "$LAST_SHA" = "$CURRENT_SHA" ] && exit 0 +COMMITS_BEHIND=$(git rev-list --count "$LAST_SHA".."$CURRENT_SHA" 2>/dev/null || echo 0) +if [ "$COMMITS_BEHIND" -gt 15 ]; then + printf '{"systemMessage":"Caliber: agent configs are %s commits behind. Run caliber refresh to sync."}' "$COMMITS_BEHIND" +fi diff --git a/.claude/hooks/caliber-session-freshness.sh b/.claude/hooks/caliber-session-freshness.sh new file mode 100755 index 00000000..08cf12f7 --- /dev/null +++ b/.claude/hooks/caliber-session-freshness.sh @@ -0,0 +1,11 @@ +#!/bin/sh +STATE_FILE=".caliber/.caliber-state.json" +[ ! -f "$STATE_FILE" ] && exit 0 +LAST_SHA=$(grep -o '"lastRefreshSha":"[^"]*"' "$STATE_FILE" 2>/dev/null | cut -d'"' -f4) +[ -z "$LAST_SHA" ] && exit 0 +CURRENT_SHA=$(git rev-parse HEAD 2>/dev/null) +[ "$LAST_SHA" = "$CURRENT_SHA" ] && exit 0 +COMMITS_BEHIND=$(git rev-list --count "$LAST_SHA".."$CURRENT_SHA" 2>/dev/null || echo 0) +if [ "$COMMITS_BEHIND" -gt 15 ]; then + printf '{"systemMessage":"Caliber: agent configs are %s commits behind. Run caliber refresh to sync."}' "$COMMITS_BEHIND" +fi diff --git a/.claude/settings.json b/.claude/settings.json index 425ab0eb..6dde507d 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -10,5 +10,101 @@ "attribution": { "commit": "", "pr": "" + }, + "hooks": { + "SessionEnd": [ + { + "matcher": "", + "hooks": [ + { + "type": "command", + "command": "caliber refresh --quiet", + "description": "Caliber: auto-refreshing docs based on code changes" + } + ] + }, + { + "matcher": "", + "hooks": [ + { + "type": "command", + "command": "caliber learn finalize --auto", + "description": "Caliber: finalizing session learnings" + } + ] + } + ], + "SessionStart": [ + { + "matcher": "", + "hooks": [ + { + "type": "command", + "command": ".claude/hooks/caliber-session-freshness.sh", + "description": "Caliber: check config freshness on session start" + } + ] + } + ], + "Notification": [ + { + "matcher": "", + "hooks": [ + { + "type": "command", + "command": ".claude/hooks/caliber-freshness-notify.sh", + "description": "Caliber: warn when agent configs are stale" + } + ] + } + ], + "PostToolUse": [ + { + "matcher": "", + "hooks": [ + { + "type": "command", + "command": "caliber learn observe", + "description": "Caliber: recording tool usage for session learning" + } + ] + } + ], + "PostToolUseFailure": [ + { + "matcher": "", + "hooks": [ + { + "type": "command", + "command": "caliber learn observe --failure", + "description": "Caliber: recording tool failure for session learning" + } + ] + } + ], + "UserPromptSubmit": [ + { + "matcher": "", + "hooks": [ + { + "type": "command", + "command": "caliber learn observe --prompt", + "description": "Caliber: recording user prompt for correction detection" + } + ] + } + ], + "Stop": [ + { + "matcher": "", + "hooks": [ + { + "type": "command", + "command": ".claude/hooks/caliber-check-sync.sh", + "description": "Caliber: offer setup if not configured" + } + ] + } + ] } } diff --git a/.claude/skills/find-skills/SKILL.md b/.claude/skills/find-skills/SKILL.md new file mode 100644 index 00000000..98ff5cfb --- /dev/null +++ b/.claude/skills/find-skills/SKILL.md @@ -0,0 +1,54 @@ +--- +name: find-skills +description: Discovers and installs community skills from the public registry. Use when the user mentions a technology, framework, or task that could benefit from specialized skills not yet installed, asks 'how do I do X', 'find a skill for X', or starts work in a new technology area. Proactively suggest when the user's task involves tools or frameworks without existing skills. +--- + +# Find Skills + +Search the public skill registry for community-contributed skills +relevant to the user's current task and install them into this project. + +## Instructions + +1. Identify the key technologies, frameworks, or task types from the + user's request that might have community skills available +2. Ask the user: "Would you like me to search for community skills + for [identified technologies]?" +3. If the user agrees, run: + ```bash + caliber skills --query "" + ``` + This outputs the top 5 matching skills with scores and descriptions. +4. Present the results to the user and ask which ones to install +5. Install the selected skills: + ```bash + caliber skills --install , + ``` +6. Read the installed SKILL.md files to load them into your current + context so you can use them immediately in this session +7. Summarize what was installed and continue with the user's task + +## Examples + +User: "let's build a web app using React" +-> "I notice you want to work with React. Would you like me to search + for community skills that could help with React development?" +-> If yes: run `caliber skills --query "react frontend"` +-> Show the user the results, ask which to install +-> Run `caliber skills --install ` +-> Read the installed files and continue + +User: "help me set up Docker for this project" +-> "Would you like me to search for Docker-related skills?" +-> If yes: run `caliber skills --query "docker deployment"` + +User: "I need to write tests for this Python ML pipeline" +-> "Would you like me to find skills for Python ML testing?" +-> If yes: run `caliber skills --query "python machine-learning testing"` + +## When NOT to trigger + +- The user is working within an already well-configured area +- You already suggested skills for this technology in this session +- The user is in the middle of urgent debugging or time-sensitive work +- The technology is too generic (e.g. just "code" or "programming") diff --git a/.claude/skills/save-learning/SKILL.md b/.claude/skills/save-learning/SKILL.md new file mode 100644 index 00000000..aef5039d --- /dev/null +++ b/.claude/skills/save-learning/SKILL.md @@ -0,0 +1,60 @@ +--- +name: save-learning +description: Saves user instructions as persistent learnings for future sessions. Use when the user says 'remember this', 'always do X', 'from now on', 'never do Y', or gives any instruction they want persisted across sessions. Proactively suggest when the user states a preference, convention, or rule they clearly want followed in the future. +--- + +# Save Learning + +Save a user's instruction or preference as a persistent learning that +will be applied in all future sessions on this project. + +## Instructions + +1. Detect when the user gives an instruction to remember, such as: + - "remember this", "save this", "always do X", "never do Y" + - "from now on", "going forward", "in this project we..." + - Any stated convention, preference, or rule +2. Refine the instruction into a clean, actionable learning bullet with + an appropriate type prefix: + - `**[convention]**` — coding style, workflow, git conventions + - `**[pattern]**` — reusable code patterns + - `**[anti-pattern]**` — things to avoid + - `**[preference]**` — personal/team preferences + - `**[context]**` — project-specific context +3. Show the refined learning to the user and ask for confirmation +4. If confirmed, run: + ```bash + caliber learn add "" + ``` + For personal preferences (not project-level), add `--personal`: + ```bash + caliber learn add --personal "" + ``` +5. Stage the learnings file for the next commit: + ```bash + git add CALIBER_LEARNINGS.md + ``` + +## Examples + +User: "when developing features, push to next branch not master, remember it" +-> Refine: `**[convention]** Push feature commits to the \`next\` branch, not \`master\`` +-> "I'll save this as a project learning: + **[convention]** Push feature commits to the \`next\` branch, not \`master\` + Save for future sessions?" +-> If yes: run `caliber learn add "**[convention]** Push feature commits to the next branch, not master"` +-> Run `git add CALIBER_LEARNINGS.md` + +User: "always use bun instead of npm" +-> Refine: `**[preference]** Use \`bun\` instead of \`npm\` for package management` +-> Confirm and save + +User: "never use any in TypeScript, use unknown instead" +-> Refine: `**[convention]** Use \`unknown\` instead of \`any\` in TypeScript` +-> Confirm and save + +## When NOT to trigger + +- The user is giving a one-time instruction for the current task only +- The instruction is too vague to be actionable +- The user explicitly says "just for now" or "only this time" diff --git a/.claude/skills/setup-caliber/SKILL.md b/.claude/skills/setup-caliber/SKILL.md new file mode 100644 index 00000000..49d0a6a0 --- /dev/null +++ b/.claude/skills/setup-caliber/SKILL.md @@ -0,0 +1,210 @@ +--- +name: setup-caliber +description: Sets up Caliber for automatic AI agent context sync. Installs pre-commit hooks so CLAUDE.md, Cursor rules, and Copilot instructions update automatically on every commit. Use when Caliber hooks are not yet installed or when the user asks about keeping agent configs in sync. +--- + +# Setup Caliber + +Dynamic onboarding for Caliber — automatic AI agent context sync. +Run all diagnostic steps below on every invocation to determine what's already +set up and what still needs to be done. + +## Instructions + +Run these checks in order. For each step, check the current state first, +then only act if something is missing. + +### Step 1: Check if Caliber is installed + +```bash +command -v caliber >/dev/null 2>&1 && caliber --version || echo "NOT_INSTALLED" +``` + +- If a version prints → Caliber is installed globally. Set `CALIBER="caliber"` and move to Step 2. +- If NOT_INSTALLED → Install it globally (faster for daily use since the pre-commit hook runs on every commit): + ```bash + npm install -g @rely-ai/caliber + ``` + Set `CALIBER="caliber"`. + + If npm fails (permissions, no sudo, etc.), fall back to npx: + ```bash + npx @rely-ai/caliber --version 2>/dev/null || echo "NO_NODE" + ``` + - If npx works → Set `CALIBER="npx @rely-ai/caliber"`. This works but adds ~500ms per invocation. + - If NO_NODE → Tell the user: "Caliber requires Node.js >= 20. Install Node first, then run /setup-caliber again." Stop here. + +### Step 2: Check if pre-commit hook is installed + +```bash +grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "HOOK_ACTIVE" || echo "NO_HOOK" +``` + +- If HOOK_ACTIVE → Tell the user: "Pre-commit hook is active — configs sync on every commit." Move to Step 3. +- If NO_HOOK → Tell the user: "I'll install the pre-commit hook so your agent configs sync automatically on every commit." + ```bash + $CALIBER hooks --install + ``` + +### Step 3: Detect agents and check if configs exist + +First, detect which coding agents are configured in this project: +```bash +AGENTS="" +[ -d .claude ] && AGENTS="claude" +[ -d .cursor ] && AGENTS="${AGENTS:+$AGENTS,}cursor" +[ -d .agents ] || [ -f AGENTS.md ] && AGENTS="${AGENTS:+$AGENTS,}codex" +[ -f .github/copilot-instructions.md ] && AGENTS="${AGENTS:+$AGENTS,}github-copilot" +echo "DETECTED_AGENTS=${AGENTS:-none}" +``` + +If no agents are detected, ask the user which coding agents they use (Claude Code, Cursor, Codex, GitHub Copilot). +Build the agent list from their answer as a comma-separated string (e.g. "claude,cursor"). + +Then check if agent configs exist: +```bash +echo "CLAUDE_MD=$([ -f CLAUDE.md ] && echo exists || echo missing)" +echo "CURSOR_RULES=$([ -d .cursor/rules ] && ls .cursor/rules/*.mdc 2>/dev/null | wc -l | tr -d ' ' || echo 0)" +echo "AGENTS_MD=$([ -f AGENTS.md ] && echo exists || echo missing)" +echo "COPILOT=$([ -f .github/copilot-instructions.md ] && echo exists || echo missing)" +``` + +- If configs exist for the detected agents → Tell the user which configs are present. Move to Step 4. +- If configs are missing → Tell the user: "No agent configs found. I'll generate them now." + Use the detected or user-selected agent list: + ```bash + $CALIBER init --auto-approve --agent + ``` + For example: `$CALIBER init --auto-approve --agent claude,cursor` + This generates CLAUDE.md, Cursor rules, AGENTS.md, skills, and sync infrastructure for the specified agents. + +### Step 4: Check if configs are fresh + +```bash +$CALIBER score --json --quiet 2>/dev/null | head -1 +``` + +- If score is 80+ → Tell the user: "Your configs are in good shape (score: X/100)." +- If score is below 80 → Tell the user: "Your configs could be improved (score: X/100). Want me to run a refresh?" + If yes: + ```bash + $CALIBER refresh + ``` + +### Step 5: Ask about team setup + +Ask the user: "Are you setting up for yourself only, or for your team too?" + +- If **solo** → Continue with solo setup: + + Check if session learning is enabled: + ```bash + $CALIBER learn status 2>/dev/null | head -3 + ``` + - If learning is already enabled → note it in the summary. + - If not enabled → ask the user: "Caliber can learn from your coding sessions — when you correct a mistake or fix a pattern, it remembers for next time. Enable session learning?" + If yes: + ```bash + $CALIBER learn install + ``` + + Then tell the user: + "You're all set! Here's what happens next: + - Every time you commit, Caliber syncs your agent configs automatically + - Your CLAUDE.md, Cursor rules, and AGENTS.md stay current with your code + - Run `$CALIBER skills` anytime to discover community skills for your stack" + + Then show the summary (see below) and stop. + +- If **team** → Check if the GitHub Action already exists: + ```bash + [ -f .github/workflows/caliber-sync.yml ] && echo "ACTION_EXISTS" || echo "NO_ACTION" + ``` + - If ACTION_EXISTS → Tell the user: "GitHub Action is already configured." + - If NO_ACTION → Tell the user: "I'll create a GitHub Action that syncs configs nightly and on every PR." + Write this file to `.github/workflows/caliber-sync.yml`: + ```yaml + name: Caliber Sync + on: + schedule: + - cron: '0 3 * * 1-5' + pull_request: + types: [opened, synchronize] + workflow_dispatch: + jobs: + sync: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: caliber-ai-org/ai-setup@v1 + with: + mode: sync + auto-refresh: true + comment: true + github-token: ${{ secrets.GITHUB_TOKEN }} + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + ``` + Now determine which LLM provider the team uses. Check the local Caliber config: + ```bash + $CALIBER config --show 2>/dev/null || echo "NO_CONFIG" + ``` + + Based on the provider, the GitHub Action needs the corresponding secret: + - **anthropic** → `ANTHROPIC_API_KEY` + - **openai** → `OPENAI_API_KEY` + - **vertex** → `VERTEX_PROJECT_ID` and `GOOGLE_APPLICATION_CREDENTIALS` (service account JSON) + + Update the workflow env block to match the provider. For example, if using OpenAI: + ```yaml + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + ``` + + Then check if the `gh` CLI is available to set the secret: + ```bash + command -v gh >/dev/null 2>&1 && echo "GH_AVAILABLE" || echo "NO_GH" + ``` + + - If GH_AVAILABLE → Ask the user for their API key and set it: + ```bash + gh secret set ANTHROPIC_API_KEY + ``` + (This prompts for the value securely via stdin) + - If NO_GH → Tell the user exactly what to do: + "Go to your repo on GitHub → Settings → Secrets and variables → Actions → New repository secret. + Name: ANTHROPIC_API_KEY (or OPENAI_API_KEY depending on provider) + Value: your API key" + + Finally, offer to commit and push the workflow file: + ```bash + git add .github/workflows/caliber-sync.yml + git commit -m "feat: add Caliber sync GitHub Action" + git push + ``` + +### Summary + +After completing all steps, show the user what's configured: + +``` +Caliber Setup Complete: +✓ Caliber installed (vX.X.X) +✓ Pre-commit hook — configs sync on every commit +✓ Agent configs — CLAUDE.md, Cursor rules, AGENTS.md +✓ Config score: X/100 +✓ GitHub Action — nightly sync + PR checks (team only) + +From now on, every commit keeps all your agent configs in sync automatically. +``` + +## When to trigger + +- When a developer first opens this project +- When the user asks about keeping agent configs up to date +- When the user asks "how do I set up Caliber" or "what is Caliber" +- When the user sees a message saying "Run /setup-caliber to get set up" + +## When NOT to trigger + +- The user is in the middle of time-sensitive work diff --git a/.cursor/hooks.json b/.cursor/hooks.json new file mode 100644 index 00000000..16f6adbf --- /dev/null +++ b/.cursor/hooks.json @@ -0,0 +1,25 @@ +{ + "version": 1, + "hooks": { + "postToolUse": [ + { + "command": "caliber learn observe" + } + ], + "postToolUseFailure": [ + { + "command": "caliber learn observe --failure" + } + ], + "userPromptSubmit": [ + { + "command": "caliber learn observe --prompt" + } + ], + "sessionEnd": [ + { + "command": "caliber learn finalize --auto" + } + ] + } +} diff --git a/.cursor/skills/find-skills/SKILL.md b/.cursor/skills/find-skills/SKILL.md new file mode 100644 index 00000000..98ff5cfb --- /dev/null +++ b/.cursor/skills/find-skills/SKILL.md @@ -0,0 +1,54 @@ +--- +name: find-skills +description: Discovers and installs community skills from the public registry. Use when the user mentions a technology, framework, or task that could benefit from specialized skills not yet installed, asks 'how do I do X', 'find a skill for X', or starts work in a new technology area. Proactively suggest when the user's task involves tools or frameworks without existing skills. +--- + +# Find Skills + +Search the public skill registry for community-contributed skills +relevant to the user's current task and install them into this project. + +## Instructions + +1. Identify the key technologies, frameworks, or task types from the + user's request that might have community skills available +2. Ask the user: "Would you like me to search for community skills + for [identified technologies]?" +3. If the user agrees, run: + ```bash + caliber skills --query "" + ``` + This outputs the top 5 matching skills with scores and descriptions. +4. Present the results to the user and ask which ones to install +5. Install the selected skills: + ```bash + caliber skills --install , + ``` +6. Read the installed SKILL.md files to load them into your current + context so you can use them immediately in this session +7. Summarize what was installed and continue with the user's task + +## Examples + +User: "let's build a web app using React" +-> "I notice you want to work with React. Would you like me to search + for community skills that could help with React development?" +-> If yes: run `caliber skills --query "react frontend"` +-> Show the user the results, ask which to install +-> Run `caliber skills --install ` +-> Read the installed files and continue + +User: "help me set up Docker for this project" +-> "Would you like me to search for Docker-related skills?" +-> If yes: run `caliber skills --query "docker deployment"` + +User: "I need to write tests for this Python ML pipeline" +-> "Would you like me to find skills for Python ML testing?" +-> If yes: run `caliber skills --query "python machine-learning testing"` + +## When NOT to trigger + +- The user is working within an already well-configured area +- You already suggested skills for this technology in this session +- The user is in the middle of urgent debugging or time-sensitive work +- The technology is too generic (e.g. just "code" or "programming") diff --git a/.cursor/skills/save-learning/SKILL.md b/.cursor/skills/save-learning/SKILL.md new file mode 100644 index 00000000..aef5039d --- /dev/null +++ b/.cursor/skills/save-learning/SKILL.md @@ -0,0 +1,60 @@ +--- +name: save-learning +description: Saves user instructions as persistent learnings for future sessions. Use when the user says 'remember this', 'always do X', 'from now on', 'never do Y', or gives any instruction they want persisted across sessions. Proactively suggest when the user states a preference, convention, or rule they clearly want followed in the future. +--- + +# Save Learning + +Save a user's instruction or preference as a persistent learning that +will be applied in all future sessions on this project. + +## Instructions + +1. Detect when the user gives an instruction to remember, such as: + - "remember this", "save this", "always do X", "never do Y" + - "from now on", "going forward", "in this project we..." + - Any stated convention, preference, or rule +2. Refine the instruction into a clean, actionable learning bullet with + an appropriate type prefix: + - `**[convention]**` — coding style, workflow, git conventions + - `**[pattern]**` — reusable code patterns + - `**[anti-pattern]**` — things to avoid + - `**[preference]**` — personal/team preferences + - `**[context]**` — project-specific context +3. Show the refined learning to the user and ask for confirmation +4. If confirmed, run: + ```bash + caliber learn add "" + ``` + For personal preferences (not project-level), add `--personal`: + ```bash + caliber learn add --personal "" + ``` +5. Stage the learnings file for the next commit: + ```bash + git add CALIBER_LEARNINGS.md + ``` + +## Examples + +User: "when developing features, push to next branch not master, remember it" +-> Refine: `**[convention]** Push feature commits to the \`next\` branch, not \`master\`` +-> "I'll save this as a project learning: + **[convention]** Push feature commits to the \`next\` branch, not \`master\` + Save for future sessions?" +-> If yes: run `caliber learn add "**[convention]** Push feature commits to the next branch, not master"` +-> Run `git add CALIBER_LEARNINGS.md` + +User: "always use bun instead of npm" +-> Refine: `**[preference]** Use \`bun\` instead of \`npm\` for package management` +-> Confirm and save + +User: "never use any in TypeScript, use unknown instead" +-> Refine: `**[convention]** Use \`unknown\` instead of \`any\` in TypeScript` +-> Confirm and save + +## When NOT to trigger + +- The user is giving a one-time instruction for the current task only +- The instruction is too vague to be actionable +- The user explicitly says "just for now" or "only this time" diff --git a/.cursor/skills/setup-caliber/SKILL.md b/.cursor/skills/setup-caliber/SKILL.md new file mode 100644 index 00000000..49d0a6a0 --- /dev/null +++ b/.cursor/skills/setup-caliber/SKILL.md @@ -0,0 +1,210 @@ +--- +name: setup-caliber +description: Sets up Caliber for automatic AI agent context sync. Installs pre-commit hooks so CLAUDE.md, Cursor rules, and Copilot instructions update automatically on every commit. Use when Caliber hooks are not yet installed or when the user asks about keeping agent configs in sync. +--- + +# Setup Caliber + +Dynamic onboarding for Caliber — automatic AI agent context sync. +Run all diagnostic steps below on every invocation to determine what's already +set up and what still needs to be done. + +## Instructions + +Run these checks in order. For each step, check the current state first, +then only act if something is missing. + +### Step 1: Check if Caliber is installed + +```bash +command -v caliber >/dev/null 2>&1 && caliber --version || echo "NOT_INSTALLED" +``` + +- If a version prints → Caliber is installed globally. Set `CALIBER="caliber"` and move to Step 2. +- If NOT_INSTALLED → Install it globally (faster for daily use since the pre-commit hook runs on every commit): + ```bash + npm install -g @rely-ai/caliber + ``` + Set `CALIBER="caliber"`. + + If npm fails (permissions, no sudo, etc.), fall back to npx: + ```bash + npx @rely-ai/caliber --version 2>/dev/null || echo "NO_NODE" + ``` + - If npx works → Set `CALIBER="npx @rely-ai/caliber"`. This works but adds ~500ms per invocation. + - If NO_NODE → Tell the user: "Caliber requires Node.js >= 20. Install Node first, then run /setup-caliber again." Stop here. + +### Step 2: Check if pre-commit hook is installed + +```bash +grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "HOOK_ACTIVE" || echo "NO_HOOK" +``` + +- If HOOK_ACTIVE → Tell the user: "Pre-commit hook is active — configs sync on every commit." Move to Step 3. +- If NO_HOOK → Tell the user: "I'll install the pre-commit hook so your agent configs sync automatically on every commit." + ```bash + $CALIBER hooks --install + ``` + +### Step 3: Detect agents and check if configs exist + +First, detect which coding agents are configured in this project: +```bash +AGENTS="" +[ -d .claude ] && AGENTS="claude" +[ -d .cursor ] && AGENTS="${AGENTS:+$AGENTS,}cursor" +[ -d .agents ] || [ -f AGENTS.md ] && AGENTS="${AGENTS:+$AGENTS,}codex" +[ -f .github/copilot-instructions.md ] && AGENTS="${AGENTS:+$AGENTS,}github-copilot" +echo "DETECTED_AGENTS=${AGENTS:-none}" +``` + +If no agents are detected, ask the user which coding agents they use (Claude Code, Cursor, Codex, GitHub Copilot). +Build the agent list from their answer as a comma-separated string (e.g. "claude,cursor"). + +Then check if agent configs exist: +```bash +echo "CLAUDE_MD=$([ -f CLAUDE.md ] && echo exists || echo missing)" +echo "CURSOR_RULES=$([ -d .cursor/rules ] && ls .cursor/rules/*.mdc 2>/dev/null | wc -l | tr -d ' ' || echo 0)" +echo "AGENTS_MD=$([ -f AGENTS.md ] && echo exists || echo missing)" +echo "COPILOT=$([ -f .github/copilot-instructions.md ] && echo exists || echo missing)" +``` + +- If configs exist for the detected agents → Tell the user which configs are present. Move to Step 4. +- If configs are missing → Tell the user: "No agent configs found. I'll generate them now." + Use the detected or user-selected agent list: + ```bash + $CALIBER init --auto-approve --agent + ``` + For example: `$CALIBER init --auto-approve --agent claude,cursor` + This generates CLAUDE.md, Cursor rules, AGENTS.md, skills, and sync infrastructure for the specified agents. + +### Step 4: Check if configs are fresh + +```bash +$CALIBER score --json --quiet 2>/dev/null | head -1 +``` + +- If score is 80+ → Tell the user: "Your configs are in good shape (score: X/100)." +- If score is below 80 → Tell the user: "Your configs could be improved (score: X/100). Want me to run a refresh?" + If yes: + ```bash + $CALIBER refresh + ``` + +### Step 5: Ask about team setup + +Ask the user: "Are you setting up for yourself only, or for your team too?" + +- If **solo** → Continue with solo setup: + + Check if session learning is enabled: + ```bash + $CALIBER learn status 2>/dev/null | head -3 + ``` + - If learning is already enabled → note it in the summary. + - If not enabled → ask the user: "Caliber can learn from your coding sessions — when you correct a mistake or fix a pattern, it remembers for next time. Enable session learning?" + If yes: + ```bash + $CALIBER learn install + ``` + + Then tell the user: + "You're all set! Here's what happens next: + - Every time you commit, Caliber syncs your agent configs automatically + - Your CLAUDE.md, Cursor rules, and AGENTS.md stay current with your code + - Run `$CALIBER skills` anytime to discover community skills for your stack" + + Then show the summary (see below) and stop. + +- If **team** → Check if the GitHub Action already exists: + ```bash + [ -f .github/workflows/caliber-sync.yml ] && echo "ACTION_EXISTS" || echo "NO_ACTION" + ``` + - If ACTION_EXISTS → Tell the user: "GitHub Action is already configured." + - If NO_ACTION → Tell the user: "I'll create a GitHub Action that syncs configs nightly and on every PR." + Write this file to `.github/workflows/caliber-sync.yml`: + ```yaml + name: Caliber Sync + on: + schedule: + - cron: '0 3 * * 1-5' + pull_request: + types: [opened, synchronize] + workflow_dispatch: + jobs: + sync: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: caliber-ai-org/ai-setup@v1 + with: + mode: sync + auto-refresh: true + comment: true + github-token: ${{ secrets.GITHUB_TOKEN }} + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + ``` + Now determine which LLM provider the team uses. Check the local Caliber config: + ```bash + $CALIBER config --show 2>/dev/null || echo "NO_CONFIG" + ``` + + Based on the provider, the GitHub Action needs the corresponding secret: + - **anthropic** → `ANTHROPIC_API_KEY` + - **openai** → `OPENAI_API_KEY` + - **vertex** → `VERTEX_PROJECT_ID` and `GOOGLE_APPLICATION_CREDENTIALS` (service account JSON) + + Update the workflow env block to match the provider. For example, if using OpenAI: + ```yaml + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + ``` + + Then check if the `gh` CLI is available to set the secret: + ```bash + command -v gh >/dev/null 2>&1 && echo "GH_AVAILABLE" || echo "NO_GH" + ``` + + - If GH_AVAILABLE → Ask the user for their API key and set it: + ```bash + gh secret set ANTHROPIC_API_KEY + ``` + (This prompts for the value securely via stdin) + - If NO_GH → Tell the user exactly what to do: + "Go to your repo on GitHub → Settings → Secrets and variables → Actions → New repository secret. + Name: ANTHROPIC_API_KEY (or OPENAI_API_KEY depending on provider) + Value: your API key" + + Finally, offer to commit and push the workflow file: + ```bash + git add .github/workflows/caliber-sync.yml + git commit -m "feat: add Caliber sync GitHub Action" + git push + ``` + +### Summary + +After completing all steps, show the user what's configured: + +``` +Caliber Setup Complete: +✓ Caliber installed (vX.X.X) +✓ Pre-commit hook — configs sync on every commit +✓ Agent configs — CLAUDE.md, Cursor rules, AGENTS.md +✓ Config score: X/100 +✓ GitHub Action — nightly sync + PR checks (team only) + +From now on, every commit keeps all your agent configs in sync automatically. +``` + +## When to trigger + +- When a developer first opens this project +- When the user asks about keeping agent configs up to date +- When the user asks "how do I set up Caliber" or "what is Caliber" +- When the user sees a message saying "Run /setup-caliber to get set up" + +## When NOT to trigger + +- The user is in the middle of time-sensitive work diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..9ecbb765 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,130 @@ +# Styleframe Development Guide + +Styleframe is a type-safe, composable CSS-in-TypeScript framework for building design systems. This is a pnpm monorepo managed with Turbo. + +## Quick Reference + +**Full API documentation:** See `AGENTS.md` for comprehensive Styleframe architecture, API reference, and patterns. + +## Project Structure + +Core packages live in `engine/` (@styleframe/core, loader, runtime, transpiler, scanner), design tokens in `theme/`, build tools in `tooling/` (CLI, Figma sync, plugins), applications in `apps/` (docs, Storybook, dashboard), tests in `testing/`, and shared configs in `config/`. + +**Import rule:** Always import from `'styleframe'` (barrel package), not `@styleframe/*` sub-packages. + +## Common Commands + +```bash +# Development +pnpm dev # Start all dev servers +pnpm dev:docs # Docs + Storybook only +pnpm storybook # Storybook only + +# Building +pnpm build # Build all packages +pnpm build:nodocs # Build engine + tooling only (faster) +pnpm build:docs # Build documentation site + +# Quality checks +pnpm test # Run all tests +pnpm test:integration # Playwright tests +pnpm typecheck # Type check all packages +pnpm lint # Lint with oxlint +pnpm format # Format with Biome + +# Publishing (CI/CD) +pnpm ci:changeset # Create a changeset +pnpm ci:version # Bump versions +pnpm ci:publish # Publish to npm +``` + +## Workflow Patterns + +### Adding a new feature + +1. Make changes in the appropriate package under `engine/`, `theme/`, or `tooling/` +2. Update tests in the same package +3. Run `pnpm typecheck` and `pnpm lint` +4. Test in Storybook if UI-related: `pnpm storybook` +5. Create changeset: `pnpm ci:changeset` + +### Working on recipes + +- Recipe implementations: `theme/src/recipes/` +- Recipe documentation: `apps/docs/content/docs/recipes/` +- Recipe stories: `apps/storybook/src/stories/recipes/` + +### Working on the transpiler + +- Core transpiler: `engine/transpiler/src/` +- Test fixtures: `engine/transpiler/test/` +- Always verify output CSS matches expectations + +## Key Conventions + +### Styleframe Instance Pattern + +```ts +import { styleframe } from 'styleframe'; + +const s = styleframe(); +const { variable, ref, selector, utility, modifier, recipe } = s; + +export default s; +``` + +### Variable Naming + +- Use dot notation: `color.primary` → `--color--primary` +- Reference with `ref()` or `@` prefix: `ref(colorPrimary)` or `"@color.primary"` +- Composable variables need `{ default: true }` + +### File Conventions + +- `styleframe.config.ts` — Global config at project root +- `*.styleframe.ts` — Extension files that extend the global instance +- `.styleframe/` — Auto-generated types (gitignored) + +## Testing + +```bash +# Unit tests (per package) +cd engine/core && pnpm test + +# Integration tests (full E2E) +pnpm test:integration +``` + +## Package-Specific Guides + +Each package has detailed documentation in its own `AGENTS.md`: + +- `engine/core/AGENTS.md` — Core token AST and factory methods +- `engine/transpiler/AGENTS.md` — CSS/TS/DTS code generation +- `theme/AGENTS.md` — Design token composables and presets +- `tooling/plugin/AGENTS.md` — Build tool integrations + +## Code Navigation + +Use LSP tools for navigation: +- `goToDefinition` — Jump to source +- `findReferences` — See all usages +- `workspaceSymbol` — Find symbol definitions +- Check LSP diagnostics after editing code + +## Tools + +- **Package manager:** pnpm (>=10.7.1) +- **Node version:** >=22.0.0 +- **Monorepo:** Turbo +- **Formatter:** Biome +- **Linter:** oxlint +- **Type checker:** TypeScript +- **Testing:** Vitest (unit), Playwright (E2E) +- **CI/CD:** Changesets for versioning + +## Resources + +- Documentation: https://styleframe.dev +- Repository: https://github.com/styleframe-dev/styleframe +- Issues: https://github.com/styleframe-dev/styleframe/issues From 74e820617d918a4267dd7e58a79344dab0f3f663 Mon Sep 17 00:00:00 2001 From: Alex Grozav Date: Wed, 8 Apr 2026 12:28:45 +0700 Subject: [PATCH 2/2] chore: Update Caliber scores, Claude settings, and project docs --- .caliber/score-history.jsonl | 3 ++ .claude/rules/apps.md | 40 +++++++++++++++++ .claude/rules/engine.md | 30 +++++++++++++ .claude/rules/theme.md | 33 ++++++++++++++ .claude/rules/tooling.md | 35 +++++++++++++++ .claude/settings.json | 17 +++++++ AGENTS.md | 54 +++++++++++----------- CLAUDE.md | 86 +++++++++++++++++++++++++++++------- 8 files changed, 254 insertions(+), 44 deletions(-) create mode 100644 .claude/rules/apps.md create mode 100644 .claude/rules/engine.md create mode 100644 .claude/rules/theme.md create mode 100644 .claude/rules/tooling.md diff --git a/.caliber/score-history.jsonl b/.caliber/score-history.jsonl index 725702a4..f06cba1c 100644 --- a/.caliber/score-history.jsonl +++ b/.caliber/score-history.jsonl @@ -3,3 +3,6 @@ {"timestamp":"2026-04-08T05:01:18.611Z","score":65,"grade":"C","targetAgent":["claude","codex"],"trigger":"score"} {"timestamp":"2026-04-08T05:01:36.864Z","score":65,"grade":"C","targetAgent":["claude","codex"],"trigger":"score"} {"timestamp":"2026-04-08T05:07:14.428Z","score":78,"grade":"B","targetAgent":["claude","codex"],"trigger":"score"} +{"timestamp":"2026-04-08T05:10:50.424Z","score":84,"grade":"B","targetAgent":["claude","codex"],"trigger":"score"} +{"timestamp":"2026-04-08T05:12:21.696Z","score":89,"grade":"A","targetAgent":["claude","codex"],"trigger":"score"} +{"timestamp":"2026-04-08T05:26:01.216Z","score":92,"grade":"A","targetAgent":["claude","codex"],"trigger":"score"} diff --git a/.claude/rules/apps.md b/.claude/rules/apps.md new file mode 100644 index 00000000..c3f7514e --- /dev/null +++ b/.claude/rules/apps.md @@ -0,0 +1,40 @@ +# Applications Rules + +**Scope:** `apps/**/*` + +## Documentation Site (`apps/docs/`) + +- Content: `apps/docs/content/docs/` (Markdown files) +- Pages: `apps/docs/pages/` (Vue components) +- Components: `apps/docs/components/` (Nuxt components) +- Config: `apps/docs/nuxt.config.ts` +- Use Nuxt Content with Markdown +- Code examples must be runnable and tested +- Public assets: `apps/docs/public/` +- See `apps/docs/content/docs/AGENTS.md` + +## Storybook (`apps/storybook/`) + +- Stories in `apps/storybook/src/stories/` +- Each recipe needs a corresponding story +- Use Storybook 10 + Vue 3 +- Stories should showcase all variants +- See `apps/storybook/AGENTS.md` + +## Customer Dashboard (`apps/app/`) + +- Nuxt 3 + Supabase +- Use Styleframe for all styling (dogfooding) +- See `apps/app/AGENTS.md` + +## Shared Layer (`apps/shared/`) + +- Shared Nuxt layer for doc apps +- Reusable components and composables +- See `apps/shared/AGENTS.md` + +## General Rules + +- All apps must use Styleframe for styling +- Document new features in docs site when adding them +- Update Storybook stories when updating recipes diff --git a/.claude/rules/engine.md b/.claude/rules/engine.md new file mode 100644 index 00000000..bf10f322 --- /dev/null +++ b/.claude/rules/engine.md @@ -0,0 +1,30 @@ +# Engine Packages Rules + +**Scope:** `engine/**/*` + +## Core Principles + +- All exports must be typed with explicit TypeScript types +- Never use `any` - use `unknown` with type guards instead +- Factory functions must return typed tokens (Variable, Selector, AtRule, etc.) +- All AST nodes extend `BaseToken` interface + +## Testing + +- Every exported function needs a unit test in the same package +- Use Vitest for unit tests: `test/` directory +- Test fixtures should be minimal and focused + +## Code Style + +- Use functional programming patterns - avoid classes except for core AST types +- Prefer immutable data structures +- Use type predicates for type guards: `function isVariable(token: Token): token is Variable` + +## Package-Specific Guides + +- **core** (`engine/core/src/`): Token AST definitions in `engine/core/src/types.ts`, factory methods in `engine/core/src/factory.ts`. Entry: `engine/core/src/index.ts`. Tests: `engine/core/test/`. See `engine/core/AGENTS.md` +- **loader** (`engine/loader/src/`): Config loading in `engine/loader/src/config.ts`, HMR in `engine/loader/src/hmr.ts`. Entry: `engine/loader/src/index.ts`. See `engine/loader/AGENTS.md` +- **transpiler** (`engine/transpiler/src/`): CSS generation in `engine/transpiler/src/css.ts`, TypeScript in `engine/transpiler/src/typescript.ts`, DTS in `engine/transpiler/src/dts.ts`. Entry: `engine/transpiler/src/index.ts`. Tests: `engine/transpiler/test/`. See `engine/transpiler/AGENTS.md` +- **runtime** (`engine/runtime/src/`): Browser recipe runtime (~1.4KB) in `engine/runtime/src/index.ts`. See `engine/runtime/AGENTS.md` +- **scanner** (`engine/scanner/src/`): Content scanning in `engine/scanner/src/scanner.ts`. Entry: `engine/scanner/src/index.ts`. See `engine/scanner/AGENTS.md` diff --git a/.claude/rules/theme.md b/.claude/rules/theme.md new file mode 100644 index 00000000..f31bf320 --- /dev/null +++ b/.claude/rules/theme.md @@ -0,0 +1,33 @@ +# Theme Package Rules + +**Scope:** `theme/**/*` + +## Composable Conventions + +- All composables must accept `Styleframe` instance as first parameter +- All variables in composables must use `{ default: true }` option +- Return destructured objects with typed token exports +- Naming: `useVariables`, `useUtilities`, `useRecipe` + +## Design Tokens + +- Use semantic names, not appearance-based: `color.primary` not `color.blue` +- Reference tokens with `ref()` or `@` prefix: `ref(colorPrimary)` or `"@color.primary"` +- Scale-based spacing: use `useScalePowersDesignTokens` for consistent spacing +- All token values should be customizable via composable parameters + +## Recipes + +- Recipe implementations: `theme/src/recipes/` (e.g., `theme/src/recipes/button.ts`, `theme/src/recipes/badge.ts`) +- Recipe entry point: `theme/src/recipes/index.ts` +- Always include base styles, variants, and defaultVariants +- Use compound variants for complex interactions +- Test recipes: `theme/test/recipes/` +- Document recipes: `apps/docs/content/docs/recipes/` +- Showcase in Storybook: `apps/storybook/src/stories/recipes/` (e.g., `apps/storybook/src/stories/recipes/Button.stories.ts`) + +## Testing + +- Test composables return correct token structure +- Verify CSS output matches expectations +- Test token references resolve correctly diff --git a/.claude/rules/tooling.md b/.claude/rules/tooling.md new file mode 100644 index 00000000..f89a8f2c --- /dev/null +++ b/.claude/rules/tooling.md @@ -0,0 +1,35 @@ +# Tooling Packages Rules + +**Scope:** `tooling/**/*` + +## Build Plugin (`tooling/plugin/`) + +- Entry: `tooling/plugin/src/index.ts` +- Virtual module resolution: `tooling/plugin/src/virtual-modules.ts` +- Plugin factory: `tooling/plugin/src/plugin.ts` +- Bundler adapters: `tooling/plugin/src/vite.ts`, `tooling/plugin/src/webpack.ts`, etc. +- Use unplugin for cross-bundler compatibility +- Virtual modules: `virtual:styleframe` and `virtual:styleframe.css` +- HMR must preserve component state when possible +- Tests: `testing/integration/` (integration tests with real bundlers) +- See `tooling/plugin/AGENTS.md` for plugin API + +## CLI (`tooling/cli/`) + +- Use commander for CLI interface +- Commands: `init`, `build`, `figma export/import` +- Always provide helpful error messages with recovery suggestions +- See `tooling/cli/AGENTS.md` for CLI architecture + +## Figma Plugin (`tooling/figma/`) + +- DTCG format for variable sync (Design Token Community Group) +- Bidirectional sync: Figma ↔ Styleframe +- Preserve token relationships during sync +- See `tooling/figma/AGENTS.md` for sync flow + +## Testing + +- Integration tests for build output verification +- Test across multiple bundlers (Vite, Webpack, Nuxt, Astro) +- Verify virtual module resolution works correctly diff --git a/.claude/settings.json b/.claude/settings.json index 6dde507d..c2dec9e9 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -11,6 +11,23 @@ "commit": "", "pr": "" }, + "permissions": { + "allow": [ + "Bash(command:pnpm install)", + "Bash(command:pnpm dev)", + "Bash(command:pnpm build)", + "Bash(command:pnpm test)", + "Bash(command:pnpm typecheck)", + "Bash(command:pnpm lint)", + "Bash(command:pnpm format)", + "Bash(command:git status)", + "Bash(command:git diff*)", + "Bash(command:git log*)", + "Bash(command:git add*)", + "Bash(command:turbo run *)", + "Bash(command:caliber *)" + ] + }, "hooks": { "SessionEnd": [ { diff --git a/AGENTS.md b/AGENTS.md index e4b6f54c..f8135b76 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,33 +22,33 @@ All `*.styleframe.ts` extension files share one `Styleframe` instance from `styl ## Monorepo Package Map -``` -engine/ -├── core/ # @styleframe/core — Token AST, factory methods (variable, ref, selector, utility, modifier, recipe, theme, css, keyframes, media, atRule, merge) -├── loader/ # @styleframe/loader — Runtime config loading, module loading, HMR, build -├── runtime/ # @styleframe/runtime — Browser-side recipe class name generation -├── scanner/ # @styleframe/scanner — Content scanning for utility class extraction -├── styleframe/ # styleframe — Barrel package re-exporting all engine APIs -└── transpiler/ # @styleframe/transpiler — AST-to-CSS/TS/DTS code generation - -theme/ # @styleframe/theme — Design token composables, modifiers, utilities, recipes, presets - -tooling/ -├── cli/ # @styleframe/cli — CLI for init, build, and Figma sync -├── figma/ # @styleframe/figma — Bidirectional Figma variable sync via DTCG format -└── plugin/ # @styleframe/plugin — Unplugin build integration (Vite, Webpack, Nuxt, Astro, Rollup, Rspack, esbuild, Farm) - -config/ # @styleframe/config-typescript, @styleframe/config-vite — Shared build configs - -apps/ -├── docs/ # Documentation site (Nuxt Content + Markdown) -├── app/ # Customer dashboard (Nuxt 3 + Supabase) -├── shared/ # Shared Nuxt layer for doc apps -└── storybook/ # Storybook 10 + Vue 3 design system showcase - -testing/ -└── integration/ # Playwright end-to-end tests across Chromium, Firefox, WebKit -``` +**Engine packages** (`engine/`): +- `core/` — @styleframe/core — Token AST, factory methods (variable, ref, selector, utility, modifier, recipe, theme, css, keyframes, media, atRule, merge) +- `loader/` — @styleframe/loader — Runtime config loading, module loading, HMR, build +- `runtime/` — @styleframe/runtime — Browser-side recipe class name generation +- `scanner/` — @styleframe/scanner — Content scanning for utility class extraction +- `styleframe/` — styleframe — Barrel package re-exporting all engine APIs +- `transpiler/` — @styleframe/transpiler — AST-to-CSS/TS/DTS code generation + +**Theme** (`theme/`): +- @styleframe/theme — Design token composables, modifiers, utilities, recipes, presets + +**Tooling** (`tooling/`): +- `cli/` — @styleframe/cli — CLI for init, build, and Figma sync +- `figma/` — @styleframe/figma — Bidirectional Figma variable sync via DTCG format +- `plugin/` — @styleframe/plugin — Unplugin build integration (Vite, Webpack, Nuxt, Astro, Rollup, Rspack, esbuild, Farm) + +**Config** (`config/`): +- @styleframe/config-typescript, @styleframe/config-vite — Shared build configs + +**Applications** (`apps/`): +- `docs/` — Documentation site (Nuxt Content + Markdown) +- `app/` — Customer dashboard (Nuxt 3 + Supabase) +- `shared/` — Shared Nuxt layer for doc apps +- `storybook/` — Storybook 10 + Vue 3 design system showcase + +**Testing** (`testing/`): +- `integration/` — Playwright end-to-end tests across Chromium, Firefox, WebKit **Import rule:** Import from `'styleframe'` (the barrel package), not `@styleframe/*` sub-packages. Use `'styleframe/plugin/vite'` for plugins, `'styleframe/loader'` for loader, `'styleframe/transpiler'` for transpiler. diff --git a/CLAUDE.md b/CLAUDE.md index 9ecbb765..81c422d7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,12 +6,28 @@ Styleframe is a type-safe, composable CSS-in-TypeScript framework for building d **Full API documentation:** See `AGENTS.md` for comprehensive Styleframe architecture, API reference, and patterns. +**Package-specific guides:** Each package has detailed `AGENTS.md` (e.g., `engine/core/AGENTS.md`, `theme/AGENTS.md`, `tooling/plugin/AGENTS.md`). + ## Project Structure Core packages live in `engine/` (@styleframe/core, loader, runtime, transpiler, scanner), design tokens in `theme/`, build tools in `tooling/` (CLI, Figma sync, plugins), applications in `apps/` (docs, Storybook, dashboard), tests in `testing/`, and shared configs in `config/`. **Import rule:** Always import from `'styleframe'` (barrel package), not `@styleframe/*` sub-packages. +## Project Configuration + +- `package.json` — Monorepo root with Turbo scripts and dependencies +- `turbo.json` — Turbo build pipeline configuration +- `pnpm-workspace.yaml` — Workspace package definitions +- `biome.json` — Biome formatter and linter config +- `.editorconfig` — Editor settings for consistent code style +- `.gitignore` — Git ignore rules +- `.changeset/` — Changesets for version management and changelogs +- `.husky/` — Git hooks (pre-commit, commit-msg) +- `.ai/` — AI assistant configurations (if present) +- `.zed/` — Zed editor configuration (if present) +- `.caliber/` — Caliber config sync state and error logs + ## Common Commands ```bash @@ -43,23 +59,39 @@ pnpm ci:publish # Publish to npm ### Adding a new feature 1. Make changes in the appropriate package under `engine/`, `theme/`, or `tooling/` -2. Update tests in the same package -3. Run `pnpm typecheck` and `pnpm lint` -4. Test in Storybook if UI-related: `pnpm storybook` -5. Create changeset: `pnpm ci:changeset` +2. Update tests in the same package (e.g., `engine/core/test/`, `theme/test/`) +3. Run `pnpm typecheck` and `pnpm lint` from project root +4. Test in Storybook if UI-related: `pnpm storybook` (see `apps/storybook/`) +5. Create changeset: `pnpm ci:changeset` (adds file to `.changeset/`) +6. Git hooks in `.husky/` will run lint-staged before commit ### Working on recipes -- Recipe implementations: `theme/src/recipes/` -- Recipe documentation: `apps/docs/content/docs/recipes/` -- Recipe stories: `apps/storybook/src/stories/recipes/` +- Recipe implementations: `theme/src/recipes/` (e.g., `theme/src/recipes/button.ts`) +- Recipe documentation: `apps/docs/content/docs/recipes/` (Markdown files) +- Recipe stories: `apps/storybook/src/stories/recipes/` (Storybook stories) +- Recipe tests: `theme/test/recipes/` (Vitest tests) ### Working on the transpiler -- Core transpiler: `engine/transpiler/src/` -- Test fixtures: `engine/transpiler/test/` +- Core transpiler: `engine/transpiler/src/` (CSS/TS/DTS generators) +- Transpiler entry: `engine/transpiler/src/index.ts` +- Test fixtures: `engine/transpiler/test/` (input/output pairs) - Always verify output CSS matches expectations +### Working on design tokens + +- Token composables: `theme/src/tokens/` (color, spacing, typography, etc.) +- Token presets: `theme/src/presets/` (complete design system presets) +- Token documentation: `apps/docs/content/docs/tokens/` + +### Working on build plugins + +- Plugin implementations: `tooling/plugin/src/` (Vite, Webpack, Nuxt, etc.) +- Plugin entry: `tooling/plugin/src/index.ts` +- Virtual module handlers: `tooling/plugin/src/virtual-modules.ts` +- Test integration with different bundlers in `testing/integration/` + ## Key Conventions ### Styleframe Instance Pattern @@ -88,21 +120,41 @@ export default s; ## Testing ```bash -# Unit tests (per package) -cd engine/core && pnpm test +# Unit tests (per package, using Vitest) +cd engine/core && pnpm test # Run core package tests +cd theme && pnpm test # Run theme package tests -# Integration tests (full E2E) -pnpm test:integration +# Integration tests (Playwright E2E) +pnpm test:integration # Runs testing/integration/ ``` +**Test locations:** +- Unit tests: `/test/` (e.g., `engine/core/test/`, `theme/test/`) +- Integration tests: `testing/integration/` (Playwright specs) +- Test configs: `vitest.config.ts` in each package, `playwright.config.ts` in testing/integration/ + ## Package-Specific Guides Each package has detailed documentation in its own `AGENTS.md`: -- `engine/core/AGENTS.md` — Core token AST and factory methods -- `engine/transpiler/AGENTS.md` — CSS/TS/DTS code generation -- `theme/AGENTS.md` — Design token composables and presets -- `tooling/plugin/AGENTS.md` — Build tool integrations +**Engine packages:** +- `engine/core/AGENTS.md` — Core token AST and factory methods (`engine/core/src/`) +- `engine/loader/AGENTS.md` — Config loading and HMR (`engine/loader/src/`) +- `engine/transpiler/AGENTS.md` — CSS/TS/DTS code generation (`engine/transpiler/src/`) +- `engine/runtime/AGENTS.md` — Browser runtime (`engine/runtime/src/`) +- `engine/scanner/AGENTS.md` — Content scanning (`engine/scanner/src/`) + +**Theme & tooling:** +- `theme/AGENTS.md` — Design token composables and presets (`theme/src/`) +- `tooling/plugin/AGENTS.md` — Build tool integrations (`tooling/plugin/src/`) +- `tooling/cli/AGENTS.md` — CLI commands (`tooling/cli/src/`) +- `tooling/figma/AGENTS.md` — Figma sync (`tooling/figma/src/`) + +**Applications:** +- `apps/docs/content/docs/AGENTS.md` — Documentation site (`apps/docs/`) +- `apps/storybook/AGENTS.md` — Component showcase (`apps/storybook/`) +- `apps/app/AGENTS.md` — Customer dashboard (`apps/app/`) +- `apps/shared/AGENTS.md` — Shared Nuxt layer (`apps/shared/`) ## Code Navigation