@motir/design-system 0.10.1 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -38,8 +38,11 @@ var buttonVariants = cva(
38
38
  cn(
39
39
  // Base layout
40
40
  "inline-flex items-center justify-center gap-2",
41
- // Typography
42
- "font-sans text-sm font-medium leading-none",
41
+ // Typography. `whitespace-nowrap`: a label longer than its English draft
42
+ // (German and Polish run 30–40% longer) WIDENS the button instead of
43
+ // wrapping to a second line inside a box one line tall, where the text
44
+ // spills out of the control (MOTIR-7759).
45
+ "font-sans text-sm font-medium leading-none whitespace-nowrap",
43
46
  // Shape — semantic tokens that flip with the active style
44
47
  "rounded-(--radius-btn)",
45
48
  // Interaction
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/utils/cn.ts","../../../src/components/ui/Spinner.tsx","../../../src/components/ui/Button.tsx","../../../src/components/ui/Card.tsx","../../../src/components/ui/Pill.tsx","../../../src/components/theme/StyleVignette.tsx"],"names":["Spinner","cva","forwardRef","Button","jsx","Card","jsxs","Pill"],"mappings":"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","file":"StyleVignette.js","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\n/**\n * Merge Tailwind class names without conflicts.\n *\n * `clsx` handles conditional inclusion (booleans, undefineds, arrays);\n * `twMerge` resolves conflicting Tailwind classes (later wins, e.g.\n * `px-2 px-4` → `px-4`). Together they are the de facto standard for\n * composing Tailwind classes in React components.\n *\n * @example\n * cn('px-2 py-1', isActive && 'bg-primary', className)\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","import { forwardRef, type HTMLAttributes } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '../../utils/cn';\n\n/**\n * Spinner — pure CSS rotation indicator.\n *\n * Three sizes; works standalone or inside a Button's loading state.\n *\n * @example\n * <Spinner size=\"md\" />\n * <Spinner size=\"sm\" aria-label=\"Loading\" />\n */\nconst spinnerVariants = cva(\n 'inline-block animate-spin rounded-full border-current border-t-transparent',\n {\n variants: {\n size: {\n sm: 'h-4 w-4 border-[1.5px]',\n md: 'h-5 w-5 border-2',\n lg: 'h-8 w-8 border-[3px]',\n },\n },\n defaultVariants: { size: 'md' },\n },\n);\n\nexport interface SpinnerProps\n extends Omit<HTMLAttributes<HTMLSpanElement>, 'role'>, VariantProps<typeof spinnerVariants> {}\n\nexport const Spinner = forwardRef<HTMLSpanElement, SpinnerProps>(function Spinner(\n { className, size, ...rest },\n ref,\n) {\n return (\n <span\n ref={ref}\n role=\"status\"\n aria-label={rest['aria-label'] ?? 'Loading'}\n className={cn(spinnerVariants({ size }), className)}\n {...rest}\n />\n );\n});\n","import { forwardRef, type ButtonHTMLAttributes, type ReactNode } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { Spinner } from './Spinner';\nimport { cn } from '../../utils/cn';\n\n/**\n * Button — primary interactive primitive.\n *\n * Variants × sizes × states. The `loading` state shows an inline Spinner\n * and disables the button. Use semantic Tailwind token classes; never\n * hardcode colors. Shape responds to `data-style` via\n * `--radius-btn` (rectangles in default, pills in `soft`).\n *\n * @example\n * <Button variant=\"primary\" size=\"md\">Save</Button>\n * <Button variant=\"ghost\" leftIcon={<Plus />} loading>Saving…</Button>\n */\n// Exported so a Next `<Link>` can render AS a styled button (an anchor styled\n// with the button variants — keeps real-link semantics for navigation) without\n// nesting a <button> inside an <a>. Used by the /ready empty state's \"View all\n// issues\" link (Subtask 7.0.6).\nexport const buttonVariants = cva(\n cn(\n // Base layout\n 'inline-flex items-center justify-center gap-2',\n // Typography\n 'font-sans text-sm font-medium leading-none',\n // Shape — semantic tokens that flip with the active style\n 'rounded-(--radius-btn)',\n // Interaction\n 'transition-[transform,background-color,border-color,color] duration-(--transition-duration)',\n 'active:scale-(--active-scale)',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--focus-ring-color) focus-visible:ring-offset-2 focus-visible:ring-offset-background',\n 'disabled:pointer-events-none disabled:opacity-50',\n ),\n {\n variants: {\n variant: {\n primary: 'bg-(--el-accent) text-(--el-accent-text) hover:opacity-90',\n secondary:\n 'bg-transparent text-(--el-text) border border-(--el-button-border) hover:bg-(--el-surface)',\n ghost: 'bg-transparent text-(--el-text) hover:bg-(--el-surface)',\n danger: 'bg-(--el-danger) text-(--el-danger-text) hover:opacity-90',\n },\n size: {\n sm: 'h-(--height-btn-sm) px-3 text-xs',\n md: 'h-(--height-btn-md) px-(--spacing-btn-x)',\n lg: 'h-(--height-btn-lg) px-6 text-base',\n },\n },\n defaultVariants: {\n variant: 'primary',\n size: 'md',\n },\n },\n);\n\nexport interface ButtonProps\n extends ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {\n /** Shows inline Spinner + disables interaction. */\n loading?: boolean;\n /** Icon rendered before the label. */\n leftIcon?: ReactNode;\n /** Icon rendered after the label. */\n rightIcon?: ReactNode;\n}\n\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(\n { variant, size, loading, leftIcon, rightIcon, disabled, className, children, ...rest },\n ref,\n) {\n return (\n <button\n ref={ref}\n type=\"button\"\n // `data-variant` is the hook the 3D / Immersive style uses to give FILLED\n // buttons physical thickness (a solid palette-derived base edge) + a\n // press-DOWN (7.3.39). Inert under every other style.\n data-variant={variant ?? 'primary'}\n className={cn(buttonVariants({ variant, size }), className)}\n disabled={disabled || loading}\n aria-busy={loading || undefined}\n {...rest}\n >\n {loading ? (\n <Spinner size={size === 'lg' ? 'md' : 'sm'} aria-hidden />\n ) : leftIcon ? (\n <span aria-hidden className=\"inline-flex\">\n {leftIcon}\n </span>\n ) : null}\n <span>{children}</span>\n {!loading && rightIcon ? (\n <span aria-hidden className=\"inline-flex\">\n {rightIcon}\n </span>\n ) : null}\n </button>\n );\n});\n","import { forwardRef, type HTMLAttributes, type ReactNode } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '../../utils/cn';\n\n/**\n * Card — content container with optional header/footer slots and tint variants.\n *\n * Compose with `header` / `footer` props for the common pattern, or just\n * pass children for a bare card. `clickable` makes the whole card a button\n * (use `onClick` + Enter/Space; pass `href` to render as `<a>` instead).\n *\n * Tint variants use the pastel feature tints from the palette\n * (peach/rose/mint/lavender/sky/yellow) — apply sparingly, never to\n * page-level surfaces.\n *\n * @example\n * <Card header={<h3>Title</h3>}>Body content</Card>\n * <Card tint=\"lavender\" clickable onClick={open}>Pastel card</Card>\n * <Card tint=\"mint\"><a href=\"/x\">Link wrapper</a></Card>\n */\nconst cardVariants = cva(\n cn(\n 'rounded-(--radius-card) border border-(--el-border)',\n 'p-(--spacing-card-padding)',\n 'transition-shadow duration-(--transition-duration)',\n ),\n {\n variants: {\n tint: {\n none: 'bg-(--el-card)',\n peach: 'bg-(--el-tint-peach) border-transparent',\n rose: 'bg-(--el-tint-rose) border-transparent',\n mint: 'bg-(--el-tint-mint) border-transparent',\n lavender: 'bg-(--el-tint-lavender) border-transparent',\n sky: 'bg-(--el-tint-sky) border-transparent',\n yellow: 'bg-(--el-tint-yellow) border-transparent',\n },\n clickable: {\n true: 'cursor-pointer hover:shadow-(--shadow-card) focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--focus-ring-color) focus-visible:ring-offset-2 focus-visible:ring-offset-background',\n false: '',\n },\n },\n defaultVariants: { tint: 'none', clickable: false },\n },\n);\n\nexport interface CardProps\n extends Omit<HTMLAttributes<HTMLDivElement>, 'children'>, VariantProps<typeof cardVariants> {\n header?: ReactNode;\n footer?: ReactNode;\n children?: ReactNode;\n}\n\nexport const Card = forwardRef<HTMLDivElement, CardProps>(function Card(\n { tint, clickable, header, footer, className, children, ...rest },\n ref,\n) {\n return (\n <div\n ref={ref}\n className={cn(cardVariants({ tint, clickable }), className)}\n // `data-tilt` is the hook the 3D / Immersive style's pointer-parallax\n // engine (ImmersiveTilt, 7.3.39) uses to tip this tile toward the cursor.\n // Inert for every other style + under reduced motion; harmless otherwise.\n data-tilt=\"\"\n // `data-surface` is the hook a surface-MATERIAL style (e.g. glassmorphism)\n // uses to frost this panel — see globals.css's material layer. Only the\n // default (untinted) card opts in; a pastel-tint feature card keeps its\n // opaque tint, never a frosted overlay that would wash the hue out.\n {...(tint && tint !== 'none' ? {} : { 'data-surface': 'card' })}\n {...(clickable ? { tabIndex: 0, role: 'button' } : {})}\n {...rest}\n >\n {/* `data-tilt-layer` lifts the slot onto a translateZ plane under the 3D /\n Immersive style (7.3.39) so it PARALLAXES above the card face as the\n card tilts — `front` (the header/title pops most), `back` (the footer\n sits nearer the face). Inert under every other style + reduced motion.\n The body stays on the face so arbitrary content never renders on a\n skewed Z-plane. */}\n {header ? (\n <div data-tilt-layer=\"front\" className=\"mb-(--spacing-md)\">\n {header}\n </div>\n ) : null}\n <div>{children}</div>\n {footer ? (\n <div\n data-tilt-layer=\"back\"\n className=\"border-(--el-border) mt-(--spacing-md) border-t pt-(--spacing-md)\"\n >\n {footer}\n </div>\n ) : null}\n </div>\n );\n});\n","import { forwardRef, type HTMLAttributes } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '../../utils/cn';\n\n/**\n * Pill — compact status or severity label. Pure span, no interaction.\n *\n * Variant axes (use exactly one):\n * - `status`: planned | in-progress | implemented | done — Motir's Subtask\n * lifecycle states. The first three are per-CATEGORY tones; `implemented` is\n * the one PER-STATUS tone (MOTIR-3103), because it shares a category with\n * In Progress / Planning / In Review and must not share their chip.\n * - `severity`: info | success | warning | danger — semantic UI states.\n * - `priority`: highest | high | medium | low | lowest — work-item priority, a\n * 5-step diverging ramp (`--el-priority-*` hue tinted over the surface, plus a\n * redundant direction icon). The single source of truth is `PRIORITY_META`.\n * - `memberRole`: admin | member | viewer | custom — a project membership role\n * (Story 6.4), one hued tint per role. (Named `memberRole`, not `role`, so\n * it can't collide with the DOM `role` ARIA attribute on the span.)\n * - `orgRole`: owner | admin | member — an ORGANIZATION role (Story 6.10),\n * sitting above the workspace `memberRole`. owner → lavender (the highest,\n * brand-purple tier), admin → sky, member → mint — per the org-admin design.\n * - `tone`: neutral — a non-semantic chip (counts, metadata). Dark text on a\n * neutral surface. `private` — the epic-level-privacy \"Not public\" badge\n * (Story 6.14 · design epic-privacy panels 1/2/6b): lavender tint + charcoal\n * text, the same AA-safe recipe as `status=\"planned\"` / `memberRole=\"admin\"`.\n *\n * All tones use adaptive dark/light text (`--color-charcoal`) on a hued tint,\n * so every variant clears WCAG AA contrast in both light and dark modes\n * (the hue lives in the background, not the text — PRODECT_FINDINGS #35).\n *\n * Always uses `--radius-badge` for fully rounded ends regardless of display style.\n *\n * @example\n * <Pill status=\"in-progress\">In progress</Pill>\n * <Pill severity=\"danger\">Validation failed</Pill>\n * <Pill tone=\"neutral\">3 members</Pill>\n */\nconst pillVariants = cva(\n cn(\n 'inline-flex items-center gap-1',\n 'rounded-(--radius-badge)',\n 'px-(--spacing-chip-x) py-(--spacing-chip-y)',\n 'font-sans text-xs font-medium',\n 'border',\n ),\n {\n variants: {\n // All colored tones carry the semantic hue in the TINT BACKGROUND and use\n // adaptive dark/light TEXT (`--color-charcoal`: #37352f light, #e5e5e5\n // dark) — the same pattern `planned` always used. The previous saturated\n // hue-on-tint TEXT failed WCAG AA (≈2.5–3.8:1 light, 3.2–4.0:1 dark);\n // charcoal-on-tint clears ~10:1 in both modes (PRODECT_FINDINGS #35).\n status: {\n planned: 'bg-(--el-tint-lavender) text-(--el-text-strong) border-transparent',\n 'in-progress': 'bg-(--el-tint-sky) text-(--el-text-strong) border-transparent',\n // BUILT, waiting on checks (MOTIR-3103). The first PER-STATUS tone on\n // this axis — the other three are per-CATEGORY, and `implemented` sits in\n // the same `in_progress` category as In Progress, Planning and In Review,\n // so without its own tone four statuses render one chip.\n //\n // It uses the recipe theme.css's own `--el-status-*` header prescribes —\n // \"a status CHIP bg = color-mix(var(--el-status-X) 14%, var(--el-surface))\n // with --el-text-strong\" — which is the same dilution the `priority`\n // variant below already renders. The hue is not spent by any other\n // status in any palette (measured at ΔE2000 >= 10 across all ten by\n // tests/theme/statusHueSeparation.test.ts), and the chip additionally\n // carries a GLYPH, because a 14% tint is a subtle mark by construction\n // and finding #35 forbids resting a state on colour alone.\n implemented:\n 'bg-[color-mix(in_srgb,var(--el-status-implemented)_14%,var(--el-surface))] text-(--el-text-strong) border-transparent',\n // A person's YES, and not yet shipped (MOTIR-5141). The SECOND\n // per-status tone, on the same documented recipe `implemented` uses\n // rather than a second one — `--el-status-approved` is the `done` green\n // one ink step deeper, measured at ΔE2000 >= 10 against every other\n // status hue in all ten palettes. Like `implemented` it carries a GLYPH\n // (`CircleCheck`, in `StatusPill`), because a 14% tint is a subtle mark\n // and finding #35 forbids resting a state on colour alone — and here it\n // does more work than usual: at 14% the whole ramp compresses, and this\n // chip's nearest neighbour is `done`'s.\n approved:\n 'bg-[color-mix(in_srgb,var(--el-status-approved)_14%,var(--el-surface))] text-(--el-text-strong) border-transparent',\n done: 'bg-(--el-tint-mint) text-(--el-text-strong) border-transparent',\n },\n severity: {\n info: 'bg-(--el-tint-sky) text-(--el-text-strong) border-transparent',\n success: 'bg-(--el-tint-mint) text-(--el-text-strong) border-transparent',\n warning: 'bg-(--el-tint-peach) text-(--el-text-strong) border-transparent',\n danger: 'bg-(--el-tint-rose) text-(--el-text-strong) border-transparent',\n },\n // Priority chips (MOTIR-1273 · 1266.2) — a 5-step diverging ramp keyed off\n // the `--el-priority-*` hues, un-collapsing `medium` and `lowest` (both\n // were `tone=\"neutral\"` grey). Each hue is diluted to a 14% tint over\n // `--el-surface` so charcoal `--el-text-strong` clears AA in both themes\n // (the hue lives in the background — finding #35); the redundant direction\n // icon (PRIORITY_META) is the non-colour cue. `medium` (slate) and\n // `lowest` (stone) resolve to two distinct greys in every palette.\n priority: {\n highest:\n 'bg-[color-mix(in_srgb,var(--el-priority-highest)_14%,var(--el-surface))] text-(--el-text-strong) border-transparent',\n high: 'bg-[color-mix(in_srgb,var(--el-priority-high)_14%,var(--el-surface))] text-(--el-text-strong) border-transparent',\n medium:\n 'bg-[color-mix(in_srgb,var(--el-priority-medium)_14%,var(--el-surface))] text-(--el-text-strong) border-transparent',\n low: 'bg-[color-mix(in_srgb,var(--el-priority-low)_14%,var(--el-surface))] text-(--el-text-strong) border-transparent',\n lowest:\n 'bg-[color-mix(in_srgb,var(--el-priority-lowest)_14%,var(--el-surface))] text-(--el-text-strong) border-transparent',\n },\n // Project membership roles (Story 6.4 · design/projects access-members):\n // admin → lavender, member → sky, viewer → mint — the hue in the tint,\n // charcoal text (AA-safe, finding #35), same recipe as `status`. Routed\n // through the DEDICATED `--el-role-*` tokens (MOTIR-1274 · 1266.3) so a\n // palette can tune roles apart from the other --el-tint-* meanings; each\n // defaults to its prior tint (zero visual change).\n // `custom` (MOTIR-2257 · MOTIR-2485) is a project's OWN role, which has no\n // enum value — it takes the next free tint slot (peach) because this\n // surface had already spent lavender / sky / mint on the built-ins, so a\n // custom chip in any of those would read as one of them. The KIND is\n // carried in WORDS by the chip's accessible name; nothing rests on the hue.\n memberRole: {\n admin: 'bg-(--el-role-admin) text-(--el-text-strong) border-transparent',\n member: 'bg-(--el-role-member) text-(--el-text-strong) border-transparent',\n viewer: 'bg-(--el-role-viewer) text-(--el-text-strong) border-transparent',\n custom: 'bg-(--el-role-custom) text-(--el-text-strong) border-transparent',\n },\n // Organization roles (Story 6.10 · design/org-admin): owner → lavender\n // (the highest, brand-purple tier), admin → sky, member → mint — the hue\n // in the tint, charcoal text (AA-safe, finding #35), same recipe as above.\n // Dedicated `--el-org-role-*` tokens (MOTIR-1274 · 1266.3); zero change.\n orgRole: {\n owner: 'bg-(--el-org-role-owner) text-(--el-text-strong) border-transparent',\n admin: 'bg-(--el-org-role-admin) text-(--el-text-strong) border-transparent',\n member: 'bg-(--el-org-role-member) text-(--el-text-strong) border-transparent',\n },\n tone: {\n neutral: 'bg-(--el-chip-bg) text-(--el-text-secondary) border-(--el-chip-border)',\n // Epic-level privacy \"Not public\" badge (Story 6.14 · design\n // epic-privacy panels 1/2/6b) — lavender tint, charcoal text, the same\n // AA-safe recipe as `status=\"planned\"` / `memberRole=\"admin\"` (≈9.7:1).\n // Dedicated `--el-privacy-private` token (MOTIR-1274 · 1266.3); the\n // matching `--el-privacy-public` is defined ahead of a \"public\" badge.\n private: 'bg-(--el-privacy-private) text-(--el-text-strong) border-transparent',\n // Inactive \"Archived\" badge (MOTIR-1276 · 1266.5) — a QUIET muted fill\n // with slate ink, not a hued tint: archived is an inactive state, not a\n // severity. Dedicated `--el-archived-pill-*` tokens so a palette can tune\n // the inactive badge apart from the neutral chip; AA-safe (slate on muted\n // clears AA both themes).\n archived: 'bg-(--el-archived-pill-bg) text-(--el-archived-pill-text) border-transparent',\n // A decision is WAITING on somebody (Story MOTIR-4778 · MOTIR-4792,\n // design `design/work-items/approval-control.mock.html` — the approval\n // frame's `pill-await`). Yellow, and the tint is chosen for DISTINCTNESS\n // rather than for mood: the frame renders four chips a reader must tell\n // apart at a glance — awaiting, recording (`severity=\"info\"`, sky),\n // approved (`severity=\"success\"`, mint) and changes-requested\n // (`severity=\"warning\"`, peach) — and `--el-tint-yellow` was the only\n // slot none of the other three had spent. `tone` rather than `severity`\n // because waiting is not a severity; it sits beside `private` and\n // `archived`, which are states rather than judgements.\n awaiting: 'bg-(--el-tint-yellow) text-(--el-text-strong) border-transparent',\n },\n },\n defaultVariants: {},\n },\n);\n\nexport interface PillProps\n extends HTMLAttributes<HTMLSpanElement>, VariantProps<typeof pillVariants> {}\n\nexport const Pill = forwardRef<HTMLSpanElement, PillProps>(function Pill(\n { status, severity, priority, memberRole, orgRole, tone, className, children, ...rest },\n ref,\n) {\n return (\n <span\n ref={ref}\n className={cn(\n pillVariants({ status, severity, priority, memberRole, orgRole, tone }),\n className,\n )}\n {...rest}\n >\n {children}\n </span>\n );\n});\n","import type { ReactNode } from 'react';\nimport { BookOpen, Search, SlidersHorizontal } from 'lucide-react';\nimport { cn } from '../../utils/cn';\nimport { buttonVariants } from '../ui/Button';\nimport { Card } from '../ui/Card';\nimport { Pill } from '../ui/Pill';\nimport type { StyleId } from '../../theme/styles';\nimport type { PaletteId } from '../../theme/palettes';\nimport type { TypeId } from '../../theme/typography';\n\n/**\n * StyleVignette — the LIVE preview specimen that makes a design axis's FEEL\n * legible (Subtask 7.3.37 / MOTIR-1050).\n *\n * The fix for \"I only see typography change\": instead of a token-swatch table,\n * this is a composed, realistic MINI-SURFACE — a nav rail, a work-item card, a\n * search input, a button row, and a floating modal — rendered LIVE under a\n * style's shape/feel tokens + component overrides (and, independently, a palette\n * and a type pairing). Switching any axis re-shapes / re-skins / re-types the\n * whole vignette, so the user sees the product's feel, not a colour chip.\n *\n * ── Two modes ────────────────────────────────────────────────────────────\n * • LIVE (no axis props) — the wrapper sets NO `data-*` axis attribute, so it\n * INHERITS the active theme from `<html>` (driven by `theme-context`). Use\n * this for the appearance-settings preview and the design-step showcase: the\n * vignette re-renders automatically as the global selection changes.\n * • SCOPED (any of `styleId` / `palette` / `type`) — the wrapper carries that\n * axis attribute, so the globals.css axis block recomputes the tokens for\n * this subtree ONLY. Use this for the onboarding Style gallery (one vignette\n * per style) and the Palette step (one style re-rendered under each palette).\n *\n * Built on the `app/tokens/page.tsx` specimen pattern: real `--el-*` colour\n * tokens + element-semantic shape tokens + the `data-surface` material hooks, so\n * surface-material styles (glassmorphism's frosted glass, cybercore's glow grid)\n * apply exactly as they do in the shipped app. NEVER a Tier-0 `--color-*` or a\n * raw `rounded-*`/`p-*`/`h-*` (the colour + shape token rules in CLAUDE.md).\n *\n * ⚠️ WHAT A SCOPED VIGNETTE IS AND IS NOT ISOLATED FROM (rewritten, MOTIR-3933).\n * This paragraph used to describe the nested-base leak and then discharge it\n * with \"in the real consumers this never bites\". That was a claim about the\n * consumer set at the time of writing, recorded as though it were a property of\n * the component — and it is what let a public page ship ten identical palette\n * previews. The consumer set grew. Measured, not assumed:\n *\n * • COLOUR (`palette`) — ISOLATED. The wrapper carries `data-appearance-scope`\n * alongside the axis attribute, which re-emits the Tier-3 `--el-*` layer\n * onto this element so it reads the `--color-*` this subtree overrides\n * rather than the ones `:root` declared. theme.css states that requirement\n * at the declaration site; this component did not honour it, and without it\n * a scoped palette changed nothing the vignette paints with. The base\n * `motir` entry now has its own block, and every per-palette dark block has\n * a descendant arm, so a scoped tile is correct in BOTH themes and for the\n * base entry too.\n * • TYPE (`type`) — ISOLATED, same mechanism plus a base `[data-type='motir']`\n * block. That axis ships no descendant rules, so the tokens are all of it.\n * • SHAPE (`styleId`, the token block) — ISOLATED. Every style, including the\n * Tier-0 `warm-editorial` base, has a `[data-style]` block that sets plain\n * custom properties on the wrapper; they inherit down only that subtree.\n * • MATERIAL (`styleId`, the descendant layer) — ISOLATED by the SCOPING\n * LIMIT, since MOTIR-3997. The layer used to ship as descendant rules\n * (`[data-style='X'] [data-surface=…]`, `[data-style='X'] body`, …), and a\n * descendant combinator does not stop at a nested `data-style`: under an\n * active `glassmorphism` only 1 of 11 scoped tiles wore its own material,\n * under `aurora` 2 of 11, 64 of 121 over the full matrix. All 69 rules now\n * ship as `@scope ([data-style='X']) to ([data-style]) { … }`, where\n * `to (…)` is a scoping LIMIT — THIS component's `data-style` wrapper is\n * what an ancestor style's scope stops at, so \"the nearest `[data-style]`\n * ancestor wins\" is structural rather than a per-rule opt-out. The full\n * matrix measures 121 / 121. See\n * `docs/decisions/scoped-preview-isolation.md` for the measurement, the two\n * rejected candidates and what the mechanism costs.\n * The mechanism is structural, not automatic: a material rule\n * authored as a plain descendant crosses the boundary exactly as these did,\n * which is what `tests/theme/styleRegistry.test.ts`'s 69-rule ratchet\n * exists to catch.\n */\n\nexport interface StyleVignetteProps {\n /** Scope the vignette to a specific style; omit to inherit the active style. */\n styleId?: StyleId;\n /** Scope the vignette to a specific palette; omit to inherit the active palette. */\n palette?: PaletteId;\n /** Scope the vignette to a specific type pairing; omit to inherit the active type. */\n type?: TypeId;\n /**\n * Accessible name for the preview region. The vignette is decorative chrome,\n * so it is announced as a single labelled image rather than a pile of\n * read-out controls.\n */\n label?: string;\n className?: string;\n}\n\n/** A muted progress bar; `accent` tints it with the active accent. */\nfunction Bar({ accent, widthPct }: { accent?: boolean; widthPct: number }) {\n return (\n <div className=\"h-[6px] w-full overflow-hidden rounded-(--radius-badge) bg-(--el-muted)\">\n <div\n className={cn(\n 'h-full rounded-(--radius-badge)',\n accent ? 'bg-(--el-accent)' : 'bg-(--el-border-strong)',\n )}\n style={{ width: `${widthPct}%` }}\n />\n </div>\n );\n}\n\n/** One row of the work-item card's mini list — a status dot + label + chip. */\nfunction ListRow({ dotVar, label, chip }: { dotVar: string; label: string; chip: string }) {\n return (\n <div className=\"flex items-center gap-2 text-[11px] text-(--el-text-secondary)\">\n <span\n className=\"size-2 shrink-0 rounded-full\"\n style={{ background: `var(${dotVar})` }}\n aria-hidden\n />\n <span className=\"truncate\">{label}</span>\n <span className=\"ml-auto shrink-0 rounded-(--radius-badge) bg-(--el-surface) px-(--spacing-chip-x) py-(--spacing-chip-y) text-[9px] font-medium text-(--el-text-secondary)\">\n {chip}\n </span>\n </div>\n );\n}\n\nexport function StyleVignette({ styleId, palette, type, label, className }: StyleVignetteProps) {\n // Only emit an axis attribute when the caller pins it; an omitted axis stays\n // inherited (LIVE mode), which is how the appearance preview follows the\n // global selection without re-implementing the theme context.\n const axisAttrs: Record<string, string> = {};\n if (styleId) axisAttrs['data-style'] = styleId;\n if (palette) axisAttrs['data-palette'] = palette;\n if (type) axisAttrs['data-type'] = type;\n // SCOPED mode only. `data-appearance-scope` re-emits the Tier-3 `--el-*`\n // layer onto this element, which is what makes the axis attributes above\n // actually change what the subtree paints with: `--el-*` reference\n // `--color-*` and `var()` resolves at the DECLARING element, so without it a\n // nested `data-palette` overrides Tier-0 that nothing here reads.\n //\n // ⚠️ NOT in LIVE mode. With no axis prop the vignette is meant to FOLLOW the\n // global selection, and re-emitting the layer locally would pin the subtree\n // to whatever `--color-*` resolved at mount — the exact opposite of what the\n // appearance preview mounts it for.\n if (Object.keys(axisAttrs).length > 0) axisAttrs['data-appearance-scope'] = '';\n\n return (\n <div\n {...axisAttrs}\n role=\"img\"\n aria-label={label ?? 'Design style preview'}\n className={cn(\n 'style-vignette relative isolate overflow-hidden',\n 'rounded-(--radius-card) border border-(--el-border) bg-(--el-surface)',\n 'shadow-(--shadow-subtle)',\n className,\n )}\n >\n {/* Material canvas: transparent for flat styles; the glassmorphism gradient\n / cybercore grid is painted here via the descendant rules in\n globals.css (a preview can't lean on the body-scoped canvas). */}\n <span className=\"sv-canvas pointer-events-none absolute inset-0 -z-10\" aria-hidden />\n\n <div className=\"flex flex-col gap-3 p-3\">\n {/* NAV — a compact rail; data-surface lets a material style frost it. */}\n <div\n data-surface=\"sidebar\"\n className=\"flex items-center gap-2 rounded-(--radius-control) bg-(--el-sidebar-bg) px-3 py-2\"\n >\n <span className=\"size-2.5 rounded-full bg-(--el-accent)\" aria-hidden />\n <span className=\"font-serif text-[12px] font-semibold text-(--el-text)\">Acme</span>\n <span className=\"ml-2 rounded-(--radius-control) bg-(--el-accent) px-2 py-[3px] text-[10px] font-medium text-(--el-accent-text)\">\n Board\n </span>\n <span className=\"text-[10px] text-(--el-text-secondary)\">Backlog</span>\n <span className=\"text-[10px] text-(--el-text-secondary)\">Reports</span>\n <span className=\"ml-auto size-5 rounded-full bg-(--el-muted)\" aria-hidden />\n </div>\n\n <div className=\"grid grid-cols-[1.4fr_1fr] gap-3\">\n {/* WORK-ITEM CARD — the centrepiece: type hue + serif title + status. */}\n <Card className=\"flex flex-col gap-2 !p-3\">\n <div className=\"flex items-center gap-2\">\n {/* A decorative story-type icon in its `--el-type-story` hue. The\n package can't depend on motir-core's domain `IssueTypeIcon`\n (which maps work-item kinds → glyphs), so the vignette inlines\n the same glyph + hue directly — pixel-identical, boundary-clean. */}\n <BookOpen className=\"size-4 text-(--el-type-story)\" aria-hidden />\n <span className=\"truncate font-serif text-[13px] font-semibold text-(--el-text)\">\n Ship the billing flow\n </span>\n <Pill status=\"in-progress\" className=\"ml-auto !text-[9px]\">\n In progress\n </Pill>\n </div>\n <div className=\"flex flex-col gap-1.5\">\n <ListRow dotVar=\"--el-success\" label=\"Invoice schema\" chip=\"Done\" />\n <ListRow dotVar=\"--el-warning\" label=\"Dunning emails\" chip=\"Review\" />\n <ListRow dotVar=\"--el-info\" label=\"Stripe webhook\" chip=\"To do\" />\n </div>\n <div className=\"mt-0.5 flex flex-col gap-1.5\">\n <Bar accent widthPct={66} />\n <Bar widthPct={38} />\n </div>\n </Card>\n\n {/* MODAL — a floating dialog panel; modal radius + elevation + scrim. */}\n <div className=\"relative\">\n <span\n className=\"pointer-events-none absolute -inset-1 rounded-(--radius-card) bg-black/10\"\n aria-hidden\n />\n <div\n data-surface=\"modal\"\n className=\"relative flex flex-col gap-2 rounded-(--radius-modal) border border-(--el-border) bg-(--el-page-bg) p-3 shadow-(--shadow-modal)\"\n >\n <span className=\"font-serif text-[12px] font-semibold text-(--el-text)\">\n New invoice\n </span>\n <Bar widthPct={100} />\n <Bar widthPct={72} />\n <div className=\"mt-1 flex gap-2\">\n <span\n className={cn(\n buttonVariants({ variant: 'primary', size: 'sm' }),\n '!h-7 !px-3 !text-[10px]',\n )}\n >\n Create\n </span>\n <span\n className={cn(\n buttonVariants({ variant: 'ghost', size: 'sm' }),\n '!h-7 !px-3 !text-[10px]',\n )}\n >\n Cancel\n </span>\n </div>\n </div>\n </div>\n </div>\n\n {/* INPUT — a search field; data-surface lets a material style fill it. */}\n <div\n data-surface=\"input\"\n className=\"flex h-(--height-input) items-center gap-2 rounded-(--radius-input) border border-(--el-border-strong) bg-(--el-page-bg) px-(--spacing-input-x) text-[11px] text-(--el-text-secondary)\"\n >\n <Search className=\"size-3.5\" aria-hidden />\n <span>Search work items…</span>\n </div>\n\n {/* BUTTON ROW — primary + ghost, exact Button silhouettes (non-interactive). */}\n <div className=\"flex gap-2\">\n <span className={cn(buttonVariants({ variant: 'primary', size: 'sm' }), '!text-[11px]')}>\n New work item\n </span>\n <span className={cn(buttonVariants({ variant: 'ghost', size: 'sm' }), '!text-[11px]')}>\n <SlidersHorizontal className=\"size-3.5\" aria-hidden />\n Filter\n </span>\n </div>\n </div>\n </div>\n );\n}\n\nexport default StyleVignette as (props: StyleVignetteProps) => ReactNode;\n"]}
1
+ {"version":3,"sources":["../../../src/utils/cn.ts","../../../src/components/ui/Spinner.tsx","../../../src/components/ui/Button.tsx","../../../src/components/ui/Card.tsx","../../../src/components/ui/Pill.tsx","../../../src/components/theme/StyleVignette.tsx"],"names":["Spinner","cva","forwardRef","Button","jsx","Card","jsxs","Pill"],"mappings":"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","file":"StyleVignette.js","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\n/**\n * Merge Tailwind class names without conflicts.\n *\n * `clsx` handles conditional inclusion (booleans, undefineds, arrays);\n * `twMerge` resolves conflicting Tailwind classes (later wins, e.g.\n * `px-2 px-4` → `px-4`). Together they are the de facto standard for\n * composing Tailwind classes in React components.\n *\n * @example\n * cn('px-2 py-1', isActive && 'bg-primary', className)\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","import { forwardRef, type HTMLAttributes } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '../../utils/cn';\n\n/**\n * Spinner — pure CSS rotation indicator.\n *\n * Three sizes; works standalone or inside a Button's loading state.\n *\n * @example\n * <Spinner size=\"md\" />\n * <Spinner size=\"sm\" aria-label=\"Loading\" />\n */\nconst spinnerVariants = cva(\n 'inline-block animate-spin rounded-full border-current border-t-transparent',\n {\n variants: {\n size: {\n sm: 'h-4 w-4 border-[1.5px]',\n md: 'h-5 w-5 border-2',\n lg: 'h-8 w-8 border-[3px]',\n },\n },\n defaultVariants: { size: 'md' },\n },\n);\n\nexport interface SpinnerProps\n extends Omit<HTMLAttributes<HTMLSpanElement>, 'role'>, VariantProps<typeof spinnerVariants> {}\n\nexport const Spinner = forwardRef<HTMLSpanElement, SpinnerProps>(function Spinner(\n { className, size, ...rest },\n ref,\n) {\n return (\n <span\n ref={ref}\n role=\"status\"\n aria-label={rest['aria-label'] ?? 'Loading'}\n className={cn(spinnerVariants({ size }), className)}\n {...rest}\n />\n );\n});\n","import { forwardRef, type ButtonHTMLAttributes, type ReactNode } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { Spinner } from './Spinner';\nimport { cn } from '../../utils/cn';\n\n/**\n * Button — primary interactive primitive.\n *\n * Variants × sizes × states. The `loading` state shows an inline Spinner\n * and disables the button. Use semantic Tailwind token classes; never\n * hardcode colors. Shape responds to `data-style` via\n * `--radius-btn` (rectangles in default, pills in `soft`).\n *\n * @example\n * <Button variant=\"primary\" size=\"md\">Save</Button>\n * <Button variant=\"ghost\" leftIcon={<Plus />} loading>Saving…</Button>\n */\n// Exported so a Next `<Link>` can render AS a styled button (an anchor styled\n// with the button variants — keeps real-link semantics for navigation) without\n// nesting a <button> inside an <a>. Used by the /ready empty state's \"View all\n// issues\" link (Subtask 7.0.6).\nexport const buttonVariants = cva(\n cn(\n // Base layout\n 'inline-flex items-center justify-center gap-2',\n // Typography. `whitespace-nowrap`: a label longer than its English draft\n // (German and Polish run 30–40% longer) WIDENS the button instead of\n // wrapping to a second line inside a box one line tall, where the text\n // spills out of the control (MOTIR-7759).\n 'font-sans text-sm font-medium leading-none whitespace-nowrap',\n // Shape — semantic tokens that flip with the active style\n 'rounded-(--radius-btn)',\n // Interaction\n 'transition-[transform,background-color,border-color,color] duration-(--transition-duration)',\n 'active:scale-(--active-scale)',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--focus-ring-color) focus-visible:ring-offset-2 focus-visible:ring-offset-background',\n 'disabled:pointer-events-none disabled:opacity-50',\n ),\n {\n variants: {\n variant: {\n primary: 'bg-(--el-accent) text-(--el-accent-text) hover:opacity-90',\n secondary:\n 'bg-transparent text-(--el-text) border border-(--el-button-border) hover:bg-(--el-surface)',\n ghost: 'bg-transparent text-(--el-text) hover:bg-(--el-surface)',\n danger: 'bg-(--el-danger) text-(--el-danger-text) hover:opacity-90',\n },\n size: {\n sm: 'h-(--height-btn-sm) px-3 text-xs',\n md: 'h-(--height-btn-md) px-(--spacing-btn-x)',\n lg: 'h-(--height-btn-lg) px-6 text-base',\n },\n },\n defaultVariants: {\n variant: 'primary',\n size: 'md',\n },\n },\n);\n\nexport interface ButtonProps\n extends ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {\n /** Shows inline Spinner + disables interaction. */\n loading?: boolean;\n /** Icon rendered before the label. */\n leftIcon?: ReactNode;\n /** Icon rendered after the label. */\n rightIcon?: ReactNode;\n}\n\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(\n { variant, size, loading, leftIcon, rightIcon, disabled, className, children, ...rest },\n ref,\n) {\n return (\n <button\n ref={ref}\n type=\"button\"\n // `data-variant` is the hook the 3D / Immersive style uses to give FILLED\n // buttons physical thickness (a solid palette-derived base edge) + a\n // press-DOWN (7.3.39). Inert under every other style.\n data-variant={variant ?? 'primary'}\n className={cn(buttonVariants({ variant, size }), className)}\n disabled={disabled || loading}\n aria-busy={loading || undefined}\n {...rest}\n >\n {loading ? (\n <Spinner size={size === 'lg' ? 'md' : 'sm'} aria-hidden />\n ) : leftIcon ? (\n <span aria-hidden className=\"inline-flex\">\n {leftIcon}\n </span>\n ) : null}\n <span>{children}</span>\n {!loading && rightIcon ? (\n <span aria-hidden className=\"inline-flex\">\n {rightIcon}\n </span>\n ) : null}\n </button>\n );\n});\n","import { forwardRef, type HTMLAttributes, type ReactNode } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '../../utils/cn';\n\n/**\n * Card — content container with optional header/footer slots and tint variants.\n *\n * Compose with `header` / `footer` props for the common pattern, or just\n * pass children for a bare card. `clickable` makes the whole card a button\n * (use `onClick` + Enter/Space; pass `href` to render as `<a>` instead).\n *\n * Tint variants use the pastel feature tints from the palette\n * (peach/rose/mint/lavender/sky/yellow) — apply sparingly, never to\n * page-level surfaces.\n *\n * @example\n * <Card header={<h3>Title</h3>}>Body content</Card>\n * <Card tint=\"lavender\" clickable onClick={open}>Pastel card</Card>\n * <Card tint=\"mint\"><a href=\"/x\">Link wrapper</a></Card>\n */\nconst cardVariants = cva(\n cn(\n 'rounded-(--radius-card) border border-(--el-border)',\n 'p-(--spacing-card-padding)',\n 'transition-shadow duration-(--transition-duration)',\n ),\n {\n variants: {\n tint: {\n none: 'bg-(--el-card)',\n peach: 'bg-(--el-tint-peach) border-transparent',\n rose: 'bg-(--el-tint-rose) border-transparent',\n mint: 'bg-(--el-tint-mint) border-transparent',\n lavender: 'bg-(--el-tint-lavender) border-transparent',\n sky: 'bg-(--el-tint-sky) border-transparent',\n yellow: 'bg-(--el-tint-yellow) border-transparent',\n },\n clickable: {\n true: 'cursor-pointer hover:shadow-(--shadow-card) focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--focus-ring-color) focus-visible:ring-offset-2 focus-visible:ring-offset-background',\n false: '',\n },\n },\n defaultVariants: { tint: 'none', clickable: false },\n },\n);\n\nexport interface CardProps\n extends Omit<HTMLAttributes<HTMLDivElement>, 'children'>, VariantProps<typeof cardVariants> {\n header?: ReactNode;\n footer?: ReactNode;\n children?: ReactNode;\n}\n\nexport const Card = forwardRef<HTMLDivElement, CardProps>(function Card(\n { tint, clickable, header, footer, className, children, ...rest },\n ref,\n) {\n return (\n <div\n ref={ref}\n className={cn(cardVariants({ tint, clickable }), className)}\n // `data-tilt` is the hook the 3D / Immersive style's pointer-parallax\n // engine (ImmersiveTilt, 7.3.39) uses to tip this tile toward the cursor.\n // Inert for every other style + under reduced motion; harmless otherwise.\n data-tilt=\"\"\n // `data-surface` is the hook a surface-MATERIAL style (e.g. glassmorphism)\n // uses to frost this panel — see globals.css's material layer. Only the\n // default (untinted) card opts in; a pastel-tint feature card keeps its\n // opaque tint, never a frosted overlay that would wash the hue out.\n {...(tint && tint !== 'none' ? {} : { 'data-surface': 'card' })}\n {...(clickable ? { tabIndex: 0, role: 'button' } : {})}\n {...rest}\n >\n {/* `data-tilt-layer` lifts the slot onto a translateZ plane under the 3D /\n Immersive style (7.3.39) so it PARALLAXES above the card face as the\n card tilts — `front` (the header/title pops most), `back` (the footer\n sits nearer the face). Inert under every other style + reduced motion.\n The body stays on the face so arbitrary content never renders on a\n skewed Z-plane. */}\n {header ? (\n <div data-tilt-layer=\"front\" className=\"mb-(--spacing-md)\">\n {header}\n </div>\n ) : null}\n <div>{children}</div>\n {footer ? (\n <div\n data-tilt-layer=\"back\"\n className=\"border-(--el-border) mt-(--spacing-md) border-t pt-(--spacing-md)\"\n >\n {footer}\n </div>\n ) : null}\n </div>\n );\n});\n","import { forwardRef, type HTMLAttributes } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '../../utils/cn';\n\n/**\n * Pill — compact status or severity label. Pure span, no interaction.\n *\n * Variant axes (use exactly one):\n * - `status`: planned | in-progress | implemented | done — Motir's Subtask\n * lifecycle states. The first three are per-CATEGORY tones; `implemented` is\n * the one PER-STATUS tone (MOTIR-3103), because it shares a category with\n * In Progress / Planning / In Review and must not share their chip.\n * - `severity`: info | success | warning | danger — semantic UI states.\n * - `priority`: highest | high | medium | low | lowest — work-item priority, a\n * 5-step diverging ramp (`--el-priority-*` hue tinted over the surface, plus a\n * redundant direction icon). The single source of truth is `PRIORITY_META`.\n * - `memberRole`: admin | member | viewer | custom — a project membership role\n * (Story 6.4), one hued tint per role. (Named `memberRole`, not `role`, so\n * it can't collide with the DOM `role` ARIA attribute on the span.)\n * - `orgRole`: owner | admin | member — an ORGANIZATION role (Story 6.10),\n * sitting above the workspace `memberRole`. owner → lavender (the highest,\n * brand-purple tier), admin → sky, member → mint — per the org-admin design.\n * - `tone`: neutral — a non-semantic chip (counts, metadata). Dark text on a\n * neutral surface. `private` — the epic-level-privacy \"Not public\" badge\n * (Story 6.14 · design epic-privacy panels 1/2/6b): lavender tint + charcoal\n * text, the same AA-safe recipe as `status=\"planned\"` / `memberRole=\"admin\"`.\n *\n * All tones use adaptive dark/light text (`--color-charcoal`) on a hued tint,\n * so every variant clears WCAG AA contrast in both light and dark modes\n * (the hue lives in the background, not the text — PRODECT_FINDINGS #35).\n *\n * Always uses `--radius-badge` for fully rounded ends regardless of display style.\n *\n * @example\n * <Pill status=\"in-progress\">In progress</Pill>\n * <Pill severity=\"danger\">Validation failed</Pill>\n * <Pill tone=\"neutral\">3 members</Pill>\n */\nconst pillVariants = cva(\n cn(\n 'inline-flex items-center gap-1',\n 'rounded-(--radius-badge)',\n 'px-(--spacing-chip-x) py-(--spacing-chip-y)',\n 'font-sans text-xs font-medium',\n 'border',\n ),\n {\n variants: {\n // All colored tones carry the semantic hue in the TINT BACKGROUND and use\n // adaptive dark/light TEXT (`--color-charcoal`: #37352f light, #e5e5e5\n // dark) — the same pattern `planned` always used. The previous saturated\n // hue-on-tint TEXT failed WCAG AA (≈2.5–3.8:1 light, 3.2–4.0:1 dark);\n // charcoal-on-tint clears ~10:1 in both modes (PRODECT_FINDINGS #35).\n status: {\n planned: 'bg-(--el-tint-lavender) text-(--el-text-strong) border-transparent',\n 'in-progress': 'bg-(--el-tint-sky) text-(--el-text-strong) border-transparent',\n // BUILT, waiting on checks (MOTIR-3103). The first PER-STATUS tone on\n // this axis — the other three are per-CATEGORY, and `implemented` sits in\n // the same `in_progress` category as In Progress, Planning and In Review,\n // so without its own tone four statuses render one chip.\n //\n // It uses the recipe theme.css's own `--el-status-*` header prescribes —\n // \"a status CHIP bg = color-mix(var(--el-status-X) 14%, var(--el-surface))\n // with --el-text-strong\" — which is the same dilution the `priority`\n // variant below already renders. The hue is not spent by any other\n // status in any palette (measured at ΔE2000 >= 10 across all ten by\n // tests/theme/statusHueSeparation.test.ts), and the chip additionally\n // carries a GLYPH, because a 14% tint is a subtle mark by construction\n // and finding #35 forbids resting a state on colour alone.\n implemented:\n 'bg-[color-mix(in_srgb,var(--el-status-implemented)_14%,var(--el-surface))] text-(--el-text-strong) border-transparent',\n // A person's YES, and not yet shipped (MOTIR-5141). The SECOND\n // per-status tone, on the same documented recipe `implemented` uses\n // rather than a second one — `--el-status-approved` is the `done` green\n // one ink step deeper, measured at ΔE2000 >= 10 against every other\n // status hue in all ten palettes. Like `implemented` it carries a GLYPH\n // (`CircleCheck`, in `StatusPill`), because a 14% tint is a subtle mark\n // and finding #35 forbids resting a state on colour alone — and here it\n // does more work than usual: at 14% the whole ramp compresses, and this\n // chip's nearest neighbour is `done`'s.\n approved:\n 'bg-[color-mix(in_srgb,var(--el-status-approved)_14%,var(--el-surface))] text-(--el-text-strong) border-transparent',\n done: 'bg-(--el-tint-mint) text-(--el-text-strong) border-transparent',\n },\n severity: {\n info: 'bg-(--el-tint-sky) text-(--el-text-strong) border-transparent',\n success: 'bg-(--el-tint-mint) text-(--el-text-strong) border-transparent',\n warning: 'bg-(--el-tint-peach) text-(--el-text-strong) border-transparent',\n danger: 'bg-(--el-tint-rose) text-(--el-text-strong) border-transparent',\n },\n // Priority chips (MOTIR-1273 · 1266.2) — a 5-step diverging ramp keyed off\n // the `--el-priority-*` hues, un-collapsing `medium` and `lowest` (both\n // were `tone=\"neutral\"` grey). Each hue is diluted to a 14% tint over\n // `--el-surface` so charcoal `--el-text-strong` clears AA in both themes\n // (the hue lives in the background — finding #35); the redundant direction\n // icon (PRIORITY_META) is the non-colour cue. `medium` (slate) and\n // `lowest` (stone) resolve to two distinct greys in every palette.\n priority: {\n highest:\n 'bg-[color-mix(in_srgb,var(--el-priority-highest)_14%,var(--el-surface))] text-(--el-text-strong) border-transparent',\n high: 'bg-[color-mix(in_srgb,var(--el-priority-high)_14%,var(--el-surface))] text-(--el-text-strong) border-transparent',\n medium:\n 'bg-[color-mix(in_srgb,var(--el-priority-medium)_14%,var(--el-surface))] text-(--el-text-strong) border-transparent',\n low: 'bg-[color-mix(in_srgb,var(--el-priority-low)_14%,var(--el-surface))] text-(--el-text-strong) border-transparent',\n lowest:\n 'bg-[color-mix(in_srgb,var(--el-priority-lowest)_14%,var(--el-surface))] text-(--el-text-strong) border-transparent',\n },\n // Project membership roles (Story 6.4 · design/projects access-members):\n // admin → lavender, member → sky, viewer → mint — the hue in the tint,\n // charcoal text (AA-safe, finding #35), same recipe as `status`. Routed\n // through the DEDICATED `--el-role-*` tokens (MOTIR-1274 · 1266.3) so a\n // palette can tune roles apart from the other --el-tint-* meanings; each\n // defaults to its prior tint (zero visual change).\n // `custom` (MOTIR-2257 · MOTIR-2485) is a project's OWN role, which has no\n // enum value — it takes the next free tint slot (peach) because this\n // surface had already spent lavender / sky / mint on the built-ins, so a\n // custom chip in any of those would read as one of them. The KIND is\n // carried in WORDS by the chip's accessible name; nothing rests on the hue.\n memberRole: {\n admin: 'bg-(--el-role-admin) text-(--el-text-strong) border-transparent',\n member: 'bg-(--el-role-member) text-(--el-text-strong) border-transparent',\n viewer: 'bg-(--el-role-viewer) text-(--el-text-strong) border-transparent',\n custom: 'bg-(--el-role-custom) text-(--el-text-strong) border-transparent',\n },\n // Organization roles (Story 6.10 · design/org-admin): owner → lavender\n // (the highest, brand-purple tier), admin → sky, member → mint — the hue\n // in the tint, charcoal text (AA-safe, finding #35), same recipe as above.\n // Dedicated `--el-org-role-*` tokens (MOTIR-1274 · 1266.3); zero change.\n orgRole: {\n owner: 'bg-(--el-org-role-owner) text-(--el-text-strong) border-transparent',\n admin: 'bg-(--el-org-role-admin) text-(--el-text-strong) border-transparent',\n member: 'bg-(--el-org-role-member) text-(--el-text-strong) border-transparent',\n },\n tone: {\n neutral: 'bg-(--el-chip-bg) text-(--el-text-secondary) border-(--el-chip-border)',\n // Epic-level privacy \"Not public\" badge (Story 6.14 · design\n // epic-privacy panels 1/2/6b) — lavender tint, charcoal text, the same\n // AA-safe recipe as `status=\"planned\"` / `memberRole=\"admin\"` (≈9.7:1).\n // Dedicated `--el-privacy-private` token (MOTIR-1274 · 1266.3); the\n // matching `--el-privacy-public` is defined ahead of a \"public\" badge.\n private: 'bg-(--el-privacy-private) text-(--el-text-strong) border-transparent',\n // Inactive \"Archived\" badge (MOTIR-1276 · 1266.5) — a QUIET muted fill\n // with slate ink, not a hued tint: archived is an inactive state, not a\n // severity. Dedicated `--el-archived-pill-*` tokens so a palette can tune\n // the inactive badge apart from the neutral chip; AA-safe (slate on muted\n // clears AA both themes).\n archived: 'bg-(--el-archived-pill-bg) text-(--el-archived-pill-text) border-transparent',\n // A decision is WAITING on somebody (Story MOTIR-4778 · MOTIR-4792,\n // design `design/work-items/approval-control.mock.html` — the approval\n // frame's `pill-await`). Yellow, and the tint is chosen for DISTINCTNESS\n // rather than for mood: the frame renders four chips a reader must tell\n // apart at a glance — awaiting, recording (`severity=\"info\"`, sky),\n // approved (`severity=\"success\"`, mint) and changes-requested\n // (`severity=\"warning\"`, peach) — and `--el-tint-yellow` was the only\n // slot none of the other three had spent. `tone` rather than `severity`\n // because waiting is not a severity; it sits beside `private` and\n // `archived`, which are states rather than judgements.\n awaiting: 'bg-(--el-tint-yellow) text-(--el-text-strong) border-transparent',\n },\n },\n defaultVariants: {},\n },\n);\n\nexport interface PillProps\n extends HTMLAttributes<HTMLSpanElement>, VariantProps<typeof pillVariants> {}\n\nexport const Pill = forwardRef<HTMLSpanElement, PillProps>(function Pill(\n { status, severity, priority, memberRole, orgRole, tone, className, children, ...rest },\n ref,\n) {\n return (\n <span\n ref={ref}\n className={cn(\n pillVariants({ status, severity, priority, memberRole, orgRole, tone }),\n className,\n )}\n {...rest}\n >\n {children}\n </span>\n );\n});\n","import type { ReactNode } from 'react';\nimport { BookOpen, Search, SlidersHorizontal } from 'lucide-react';\nimport { cn } from '../../utils/cn';\nimport { buttonVariants } from '../ui/Button';\nimport { Card } from '../ui/Card';\nimport { Pill } from '../ui/Pill';\nimport type { StyleId } from '../../theme/styles';\nimport type { PaletteId } from '../../theme/palettes';\nimport type { TypeId } from '../../theme/typography';\n\n/**\n * StyleVignette — the LIVE preview specimen that makes a design axis's FEEL\n * legible (Subtask 7.3.37 / MOTIR-1050).\n *\n * The fix for \"I only see typography change\": instead of a token-swatch table,\n * this is a composed, realistic MINI-SURFACE — a nav rail, a work-item card, a\n * search input, a button row, and a floating modal — rendered LIVE under a\n * style's shape/feel tokens + component overrides (and, independently, a palette\n * and a type pairing). Switching any axis re-shapes / re-skins / re-types the\n * whole vignette, so the user sees the product's feel, not a colour chip.\n *\n * ── Two modes ────────────────────────────────────────────────────────────\n * • LIVE (no axis props) — the wrapper sets NO `data-*` axis attribute, so it\n * INHERITS the active theme from `<html>` (driven by `theme-context`). Use\n * this for the appearance-settings preview and the design-step showcase: the\n * vignette re-renders automatically as the global selection changes.\n * • SCOPED (any of `styleId` / `palette` / `type`) — the wrapper carries that\n * axis attribute, so the globals.css axis block recomputes the tokens for\n * this subtree ONLY. Use this for the onboarding Style gallery (one vignette\n * per style) and the Palette step (one style re-rendered under each palette).\n *\n * Built on the `app/tokens/page.tsx` specimen pattern: real `--el-*` colour\n * tokens + element-semantic shape tokens + the `data-surface` material hooks, so\n * surface-material styles (glassmorphism's frosted glass, cybercore's glow grid)\n * apply exactly as they do in the shipped app. NEVER a Tier-0 `--color-*` or a\n * raw `rounded-*`/`p-*`/`h-*` (the colour + shape token rules in CLAUDE.md).\n *\n * ⚠️ WHAT A SCOPED VIGNETTE IS AND IS NOT ISOLATED FROM (rewritten, MOTIR-3933).\n * This paragraph used to describe the nested-base leak and then discharge it\n * with \"in the real consumers this never bites\". That was a claim about the\n * consumer set at the time of writing, recorded as though it were a property of\n * the component — and it is what let a public page ship ten identical palette\n * previews. The consumer set grew. Measured, not assumed:\n *\n * • COLOUR (`palette`) — ISOLATED. The wrapper carries `data-appearance-scope`\n * alongside the axis attribute, which re-emits the Tier-3 `--el-*` layer\n * onto this element so it reads the `--color-*` this subtree overrides\n * rather than the ones `:root` declared. theme.css states that requirement\n * at the declaration site; this component did not honour it, and without it\n * a scoped palette changed nothing the vignette paints with. The base\n * `motir` entry now has its own block, and every per-palette dark block has\n * a descendant arm, so a scoped tile is correct in BOTH themes and for the\n * base entry too.\n * • TYPE (`type`) — ISOLATED, same mechanism plus a base `[data-type='motir']`\n * block. That axis ships no descendant rules, so the tokens are all of it.\n * • SHAPE (`styleId`, the token block) — ISOLATED. Every style, including the\n * Tier-0 `warm-editorial` base, has a `[data-style]` block that sets plain\n * custom properties on the wrapper; they inherit down only that subtree.\n * • MATERIAL (`styleId`, the descendant layer) — ISOLATED by the SCOPING\n * LIMIT, since MOTIR-3997. The layer used to ship as descendant rules\n * (`[data-style='X'] [data-surface=…]`, `[data-style='X'] body`, …), and a\n * descendant combinator does not stop at a nested `data-style`: under an\n * active `glassmorphism` only 1 of 11 scoped tiles wore its own material,\n * under `aurora` 2 of 11, 64 of 121 over the full matrix. All 69 rules now\n * ship as `@scope ([data-style='X']) to ([data-style]) { … }`, where\n * `to (…)` is a scoping LIMIT — THIS component's `data-style` wrapper is\n * what an ancestor style's scope stops at, so \"the nearest `[data-style]`\n * ancestor wins\" is structural rather than a per-rule opt-out. The full\n * matrix measures 121 / 121. See\n * `docs/decisions/scoped-preview-isolation.md` for the measurement, the two\n * rejected candidates and what the mechanism costs.\n * The mechanism is structural, not automatic: a material rule\n * authored as a plain descendant crosses the boundary exactly as these did,\n * which is what `tests/theme/styleRegistry.test.ts`'s 69-rule ratchet\n * exists to catch.\n */\n\nexport interface StyleVignetteProps {\n /** Scope the vignette to a specific style; omit to inherit the active style. */\n styleId?: StyleId;\n /** Scope the vignette to a specific palette; omit to inherit the active palette. */\n palette?: PaletteId;\n /** Scope the vignette to a specific type pairing; omit to inherit the active type. */\n type?: TypeId;\n /**\n * Accessible name for the preview region. The vignette is decorative chrome,\n * so it is announced as a single labelled image rather than a pile of\n * read-out controls.\n */\n label?: string;\n className?: string;\n}\n\n/** A muted progress bar; `accent` tints it with the active accent. */\nfunction Bar({ accent, widthPct }: { accent?: boolean; widthPct: number }) {\n return (\n <div className=\"h-[6px] w-full overflow-hidden rounded-(--radius-badge) bg-(--el-muted)\">\n <div\n className={cn(\n 'h-full rounded-(--radius-badge)',\n accent ? 'bg-(--el-accent)' : 'bg-(--el-border-strong)',\n )}\n style={{ width: `${widthPct}%` }}\n />\n </div>\n );\n}\n\n/** One row of the work-item card's mini list — a status dot + label + chip. */\nfunction ListRow({ dotVar, label, chip }: { dotVar: string; label: string; chip: string }) {\n return (\n <div className=\"flex items-center gap-2 text-[11px] text-(--el-text-secondary)\">\n <span\n className=\"size-2 shrink-0 rounded-full\"\n style={{ background: `var(${dotVar})` }}\n aria-hidden\n />\n <span className=\"truncate\">{label}</span>\n <span className=\"ml-auto shrink-0 rounded-(--radius-badge) bg-(--el-surface) px-(--spacing-chip-x) py-(--spacing-chip-y) text-[9px] font-medium text-(--el-text-secondary)\">\n {chip}\n </span>\n </div>\n );\n}\n\nexport function StyleVignette({ styleId, palette, type, label, className }: StyleVignetteProps) {\n // Only emit an axis attribute when the caller pins it; an omitted axis stays\n // inherited (LIVE mode), which is how the appearance preview follows the\n // global selection without re-implementing the theme context.\n const axisAttrs: Record<string, string> = {};\n if (styleId) axisAttrs['data-style'] = styleId;\n if (palette) axisAttrs['data-palette'] = palette;\n if (type) axisAttrs['data-type'] = type;\n // SCOPED mode only. `data-appearance-scope` re-emits the Tier-3 `--el-*`\n // layer onto this element, which is what makes the axis attributes above\n // actually change what the subtree paints with: `--el-*` reference\n // `--color-*` and `var()` resolves at the DECLARING element, so without it a\n // nested `data-palette` overrides Tier-0 that nothing here reads.\n //\n // ⚠️ NOT in LIVE mode. With no axis prop the vignette is meant to FOLLOW the\n // global selection, and re-emitting the layer locally would pin the subtree\n // to whatever `--color-*` resolved at mount — the exact opposite of what the\n // appearance preview mounts it for.\n if (Object.keys(axisAttrs).length > 0) axisAttrs['data-appearance-scope'] = '';\n\n return (\n <div\n {...axisAttrs}\n role=\"img\"\n aria-label={label ?? 'Design style preview'}\n className={cn(\n 'style-vignette relative isolate overflow-hidden',\n 'rounded-(--radius-card) border border-(--el-border) bg-(--el-surface)',\n 'shadow-(--shadow-subtle)',\n className,\n )}\n >\n {/* Material canvas: transparent for flat styles; the glassmorphism gradient\n / cybercore grid is painted here via the descendant rules in\n globals.css (a preview can't lean on the body-scoped canvas). */}\n <span className=\"sv-canvas pointer-events-none absolute inset-0 -z-10\" aria-hidden />\n\n <div className=\"flex flex-col gap-3 p-3\">\n {/* NAV — a compact rail; data-surface lets a material style frost it. */}\n <div\n data-surface=\"sidebar\"\n className=\"flex items-center gap-2 rounded-(--radius-control) bg-(--el-sidebar-bg) px-3 py-2\"\n >\n <span className=\"size-2.5 rounded-full bg-(--el-accent)\" aria-hidden />\n <span className=\"font-serif text-[12px] font-semibold text-(--el-text)\">Acme</span>\n <span className=\"ml-2 rounded-(--radius-control) bg-(--el-accent) px-2 py-[3px] text-[10px] font-medium text-(--el-accent-text)\">\n Board\n </span>\n <span className=\"text-[10px] text-(--el-text-secondary)\">Backlog</span>\n <span className=\"text-[10px] text-(--el-text-secondary)\">Reports</span>\n <span className=\"ml-auto size-5 rounded-full bg-(--el-muted)\" aria-hidden />\n </div>\n\n <div className=\"grid grid-cols-[1.4fr_1fr] gap-3\">\n {/* WORK-ITEM CARD — the centrepiece: type hue + serif title + status. */}\n <Card className=\"flex flex-col gap-2 !p-3\">\n <div className=\"flex items-center gap-2\">\n {/* A decorative story-type icon in its `--el-type-story` hue. The\n package can't depend on motir-core's domain `IssueTypeIcon`\n (which maps work-item kinds → glyphs), so the vignette inlines\n the same glyph + hue directly — pixel-identical, boundary-clean. */}\n <BookOpen className=\"size-4 text-(--el-type-story)\" aria-hidden />\n <span className=\"truncate font-serif text-[13px] font-semibold text-(--el-text)\">\n Ship the billing flow\n </span>\n <Pill status=\"in-progress\" className=\"ml-auto !text-[9px]\">\n In progress\n </Pill>\n </div>\n <div className=\"flex flex-col gap-1.5\">\n <ListRow dotVar=\"--el-success\" label=\"Invoice schema\" chip=\"Done\" />\n <ListRow dotVar=\"--el-warning\" label=\"Dunning emails\" chip=\"Review\" />\n <ListRow dotVar=\"--el-info\" label=\"Stripe webhook\" chip=\"To do\" />\n </div>\n <div className=\"mt-0.5 flex flex-col gap-1.5\">\n <Bar accent widthPct={66} />\n <Bar widthPct={38} />\n </div>\n </Card>\n\n {/* MODAL — a floating dialog panel; modal radius + elevation + scrim. */}\n <div className=\"relative\">\n <span\n className=\"pointer-events-none absolute -inset-1 rounded-(--radius-card) bg-black/10\"\n aria-hidden\n />\n <div\n data-surface=\"modal\"\n className=\"relative flex flex-col gap-2 rounded-(--radius-modal) border border-(--el-border) bg-(--el-page-bg) p-3 shadow-(--shadow-modal)\"\n >\n <span className=\"font-serif text-[12px] font-semibold text-(--el-text)\">\n New invoice\n </span>\n <Bar widthPct={100} />\n <Bar widthPct={72} />\n <div className=\"mt-1 flex gap-2\">\n <span\n className={cn(\n buttonVariants({ variant: 'primary', size: 'sm' }),\n '!h-7 !px-3 !text-[10px]',\n )}\n >\n Create\n </span>\n <span\n className={cn(\n buttonVariants({ variant: 'ghost', size: 'sm' }),\n '!h-7 !px-3 !text-[10px]',\n )}\n >\n Cancel\n </span>\n </div>\n </div>\n </div>\n </div>\n\n {/* INPUT — a search field; data-surface lets a material style fill it. */}\n <div\n data-surface=\"input\"\n className=\"flex h-(--height-input) items-center gap-2 rounded-(--radius-input) border border-(--el-border-strong) bg-(--el-page-bg) px-(--spacing-input-x) text-[11px] text-(--el-text-secondary)\"\n >\n <Search className=\"size-3.5\" aria-hidden />\n <span>Search work items…</span>\n </div>\n\n {/* BUTTON ROW — primary + ghost, exact Button silhouettes (non-interactive). */}\n <div className=\"flex gap-2\">\n <span className={cn(buttonVariants({ variant: 'primary', size: 'sm' }), '!text-[11px]')}>\n New work item\n </span>\n <span className={cn(buttonVariants({ variant: 'ghost', size: 'sm' }), '!text-[11px]')}>\n <SlidersHorizontal className=\"size-3.5\" aria-hidden />\n Filter\n </span>\n </div>\n </div>\n </div>\n );\n}\n\nexport default StyleVignette as (props: StyleVignetteProps) => ReactNode;\n"]}
@@ -37,8 +37,11 @@ var buttonVariants = cva(
37
37
  cn(
38
38
  // Base layout
39
39
  "inline-flex items-center justify-center gap-2",
40
- // Typography
41
- "font-sans text-sm font-medium leading-none",
40
+ // Typography. `whitespace-nowrap`: a label longer than its English draft
41
+ // (German and Polish run 30–40% longer) WIDENS the button instead of
42
+ // wrapping to a second line inside a box one line tall, where the text
43
+ // spills out of the control (MOTIR-7759).
44
+ "font-sans text-sm font-medium leading-none whitespace-nowrap",
42
45
  // Shape — semantic tokens that flip with the active style
43
46
  "rounded-(--radius-btn)",
44
47
  // Interaction
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/utils/cn.ts","../../../src/components/ui/Spinner.tsx","../../../src/components/ui/Button.tsx"],"names":["Spinner","cva","forwardRef","Button","jsx"],"mappings":"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","file":"Button.js","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\n/**\n * Merge Tailwind class names without conflicts.\n *\n * `clsx` handles conditional inclusion (booleans, undefineds, arrays);\n * `twMerge` resolves conflicting Tailwind classes (later wins, e.g.\n * `px-2 px-4` → `px-4`). Together they are the de facto standard for\n * composing Tailwind classes in React components.\n *\n * @example\n * cn('px-2 py-1', isActive && 'bg-primary', className)\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","import { forwardRef, type HTMLAttributes } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '../../utils/cn';\n\n/**\n * Spinner — pure CSS rotation indicator.\n *\n * Three sizes; works standalone or inside a Button's loading state.\n *\n * @example\n * <Spinner size=\"md\" />\n * <Spinner size=\"sm\" aria-label=\"Loading\" />\n */\nconst spinnerVariants = cva(\n 'inline-block animate-spin rounded-full border-current border-t-transparent',\n {\n variants: {\n size: {\n sm: 'h-4 w-4 border-[1.5px]',\n md: 'h-5 w-5 border-2',\n lg: 'h-8 w-8 border-[3px]',\n },\n },\n defaultVariants: { size: 'md' },\n },\n);\n\nexport interface SpinnerProps\n extends Omit<HTMLAttributes<HTMLSpanElement>, 'role'>, VariantProps<typeof spinnerVariants> {}\n\nexport const Spinner = forwardRef<HTMLSpanElement, SpinnerProps>(function Spinner(\n { className, size, ...rest },\n ref,\n) {\n return (\n <span\n ref={ref}\n role=\"status\"\n aria-label={rest['aria-label'] ?? 'Loading'}\n className={cn(spinnerVariants({ size }), className)}\n {...rest}\n />\n );\n});\n","import { forwardRef, type ButtonHTMLAttributes, type ReactNode } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { Spinner } from './Spinner';\nimport { cn } from '../../utils/cn';\n\n/**\n * Button — primary interactive primitive.\n *\n * Variants × sizes × states. The `loading` state shows an inline Spinner\n * and disables the button. Use semantic Tailwind token classes; never\n * hardcode colors. Shape responds to `data-style` via\n * `--radius-btn` (rectangles in default, pills in `soft`).\n *\n * @example\n * <Button variant=\"primary\" size=\"md\">Save</Button>\n * <Button variant=\"ghost\" leftIcon={<Plus />} loading>Saving…</Button>\n */\n// Exported so a Next `<Link>` can render AS a styled button (an anchor styled\n// with the button variants — keeps real-link semantics for navigation) without\n// nesting a <button> inside an <a>. Used by the /ready empty state's \"View all\n// issues\" link (Subtask 7.0.6).\nexport const buttonVariants = cva(\n cn(\n // Base layout\n 'inline-flex items-center justify-center gap-2',\n // Typography\n 'font-sans text-sm font-medium leading-none',\n // Shape — semantic tokens that flip with the active style\n 'rounded-(--radius-btn)',\n // Interaction\n 'transition-[transform,background-color,border-color,color] duration-(--transition-duration)',\n 'active:scale-(--active-scale)',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--focus-ring-color) focus-visible:ring-offset-2 focus-visible:ring-offset-background',\n 'disabled:pointer-events-none disabled:opacity-50',\n ),\n {\n variants: {\n variant: {\n primary: 'bg-(--el-accent) text-(--el-accent-text) hover:opacity-90',\n secondary:\n 'bg-transparent text-(--el-text) border border-(--el-button-border) hover:bg-(--el-surface)',\n ghost: 'bg-transparent text-(--el-text) hover:bg-(--el-surface)',\n danger: 'bg-(--el-danger) text-(--el-danger-text) hover:opacity-90',\n },\n size: {\n sm: 'h-(--height-btn-sm) px-3 text-xs',\n md: 'h-(--height-btn-md) px-(--spacing-btn-x)',\n lg: 'h-(--height-btn-lg) px-6 text-base',\n },\n },\n defaultVariants: {\n variant: 'primary',\n size: 'md',\n },\n },\n);\n\nexport interface ButtonProps\n extends ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {\n /** Shows inline Spinner + disables interaction. */\n loading?: boolean;\n /** Icon rendered before the label. */\n leftIcon?: ReactNode;\n /** Icon rendered after the label. */\n rightIcon?: ReactNode;\n}\n\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(\n { variant, size, loading, leftIcon, rightIcon, disabled, className, children, ...rest },\n ref,\n) {\n return (\n <button\n ref={ref}\n type=\"button\"\n // `data-variant` is the hook the 3D / Immersive style uses to give FILLED\n // buttons physical thickness (a solid palette-derived base edge) + a\n // press-DOWN (7.3.39). Inert under every other style.\n data-variant={variant ?? 'primary'}\n className={cn(buttonVariants({ variant, size }), className)}\n disabled={disabled || loading}\n aria-busy={loading || undefined}\n {...rest}\n >\n {loading ? (\n <Spinner size={size === 'lg' ? 'md' : 'sm'} aria-hidden />\n ) : leftIcon ? (\n <span aria-hidden className=\"inline-flex\">\n {leftIcon}\n </span>\n ) : null}\n <span>{children}</span>\n {!loading && rightIcon ? (\n <span aria-hidden className=\"inline-flex\">\n {rightIcon}\n </span>\n ) : null}\n </button>\n );\n});\n"]}
1
+ {"version":3,"sources":["../../../src/utils/cn.ts","../../../src/components/ui/Spinner.tsx","../../../src/components/ui/Button.tsx"],"names":["Spinner","cva","forwardRef","Button","jsx"],"mappings":"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","file":"Button.js","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\n/**\n * Merge Tailwind class names without conflicts.\n *\n * `clsx` handles conditional inclusion (booleans, undefineds, arrays);\n * `twMerge` resolves conflicting Tailwind classes (later wins, e.g.\n * `px-2 px-4` → `px-4`). Together they are the de facto standard for\n * composing Tailwind classes in React components.\n *\n * @example\n * cn('px-2 py-1', isActive && 'bg-primary', className)\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","import { forwardRef, type HTMLAttributes } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '../../utils/cn';\n\n/**\n * Spinner — pure CSS rotation indicator.\n *\n * Three sizes; works standalone or inside a Button's loading state.\n *\n * @example\n * <Spinner size=\"md\" />\n * <Spinner size=\"sm\" aria-label=\"Loading\" />\n */\nconst spinnerVariants = cva(\n 'inline-block animate-spin rounded-full border-current border-t-transparent',\n {\n variants: {\n size: {\n sm: 'h-4 w-4 border-[1.5px]',\n md: 'h-5 w-5 border-2',\n lg: 'h-8 w-8 border-[3px]',\n },\n },\n defaultVariants: { size: 'md' },\n },\n);\n\nexport interface SpinnerProps\n extends Omit<HTMLAttributes<HTMLSpanElement>, 'role'>, VariantProps<typeof spinnerVariants> {}\n\nexport const Spinner = forwardRef<HTMLSpanElement, SpinnerProps>(function Spinner(\n { className, size, ...rest },\n ref,\n) {\n return (\n <span\n ref={ref}\n role=\"status\"\n aria-label={rest['aria-label'] ?? 'Loading'}\n className={cn(spinnerVariants({ size }), className)}\n {...rest}\n />\n );\n});\n","import { forwardRef, type ButtonHTMLAttributes, type ReactNode } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { Spinner } from './Spinner';\nimport { cn } from '../../utils/cn';\n\n/**\n * Button — primary interactive primitive.\n *\n * Variants × sizes × states. The `loading` state shows an inline Spinner\n * and disables the button. Use semantic Tailwind token classes; never\n * hardcode colors. Shape responds to `data-style` via\n * `--radius-btn` (rectangles in default, pills in `soft`).\n *\n * @example\n * <Button variant=\"primary\" size=\"md\">Save</Button>\n * <Button variant=\"ghost\" leftIcon={<Plus />} loading>Saving…</Button>\n */\n// Exported so a Next `<Link>` can render AS a styled button (an anchor styled\n// with the button variants — keeps real-link semantics for navigation) without\n// nesting a <button> inside an <a>. Used by the /ready empty state's \"View all\n// issues\" link (Subtask 7.0.6).\nexport const buttonVariants = cva(\n cn(\n // Base layout\n 'inline-flex items-center justify-center gap-2',\n // Typography. `whitespace-nowrap`: a label longer than its English draft\n // (German and Polish run 30–40% longer) WIDENS the button instead of\n // wrapping to a second line inside a box one line tall, where the text\n // spills out of the control (MOTIR-7759).\n 'font-sans text-sm font-medium leading-none whitespace-nowrap',\n // Shape — semantic tokens that flip with the active style\n 'rounded-(--radius-btn)',\n // Interaction\n 'transition-[transform,background-color,border-color,color] duration-(--transition-duration)',\n 'active:scale-(--active-scale)',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--focus-ring-color) focus-visible:ring-offset-2 focus-visible:ring-offset-background',\n 'disabled:pointer-events-none disabled:opacity-50',\n ),\n {\n variants: {\n variant: {\n primary: 'bg-(--el-accent) text-(--el-accent-text) hover:opacity-90',\n secondary:\n 'bg-transparent text-(--el-text) border border-(--el-button-border) hover:bg-(--el-surface)',\n ghost: 'bg-transparent text-(--el-text) hover:bg-(--el-surface)',\n danger: 'bg-(--el-danger) text-(--el-danger-text) hover:opacity-90',\n },\n size: {\n sm: 'h-(--height-btn-sm) px-3 text-xs',\n md: 'h-(--height-btn-md) px-(--spacing-btn-x)',\n lg: 'h-(--height-btn-lg) px-6 text-base',\n },\n },\n defaultVariants: {\n variant: 'primary',\n size: 'md',\n },\n },\n);\n\nexport interface ButtonProps\n extends ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {\n /** Shows inline Spinner + disables interaction. */\n loading?: boolean;\n /** Icon rendered before the label. */\n leftIcon?: ReactNode;\n /** Icon rendered after the label. */\n rightIcon?: ReactNode;\n}\n\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(\n { variant, size, loading, leftIcon, rightIcon, disabled, className, children, ...rest },\n ref,\n) {\n return (\n <button\n ref={ref}\n type=\"button\"\n // `data-variant` is the hook the 3D / Immersive style uses to give FILLED\n // buttons physical thickness (a solid palette-derived base edge) + a\n // press-DOWN (7.3.39). Inert under every other style.\n data-variant={variant ?? 'primary'}\n className={cn(buttonVariants({ variant, size }), className)}\n disabled={disabled || loading}\n aria-busy={loading || undefined}\n {...rest}\n >\n {loading ? (\n <Spinner size={size === 'lg' ? 'md' : 'sm'} aria-hidden />\n ) : leftIcon ? (\n <span aria-hidden className=\"inline-flex\">\n {leftIcon}\n </span>\n ) : null}\n <span>{children}</span>\n {!loading && rightIcon ? (\n <span aria-hidden className=\"inline-flex\">\n {rightIcon}\n </span>\n ) : null}\n </button>\n );\n});\n"]}
@@ -56,6 +56,14 @@ interface ComboboxOption<T extends string> {
56
56
  * reads in `--el-text-secondary`, never an opacity dim, which keeps AA.
57
57
  */
58
58
  disabled?: boolean;
59
+ /**
60
+ * The language the LABEL is written in (a BCP 47 code), rendered as the `lang`
61
+ * attribute on this option's row and, when it is selected, on the trigger's
62
+ * label (MOTIR-7758 — a language picker names each language in its own script,
63
+ * so a reader announces 日本語 in Japanese inside an English page). Omit it and
64
+ * the label inherits the page's language, as before.
65
+ */
66
+ lang?: string;
59
67
  }
60
68
  interface ComboboxProps<T extends string> {
61
69
  options: ComboboxOption<T>[];
@@ -107,7 +115,30 @@ interface ComboboxProps<T extends string> {
107
115
  * unaffected, and it is announced as ordinary text. Omit it for no note.
108
116
  */
109
117
  footer?: ReactNode;
118
+ /**
119
+ * A leading glyph drawn on the TRIGGER only, before the selected label (or the
120
+ * placeholder), and never in the option rows — unlike an option's `icon`,
121
+ * which is drawn in both (MOTIR-7758: the signed-out language control's
122
+ * `Languages` glyph). Decorative: it is rendered `aria-hidden`, and the
123
+ * trigger's accessible name stays `label`.
124
+ */
125
+ triggerIcon?: ReactNode;
126
+ /**
127
+ * The value the trigger shows is being applied (MOTIR-7758). The trigger is
128
+ * marked `aria-busy` and a spinner takes the chevron's 16px slot, so the
129
+ * trigger keeps its size. It does NOT disable the control — a caller that
130
+ * wants that passes `disabled` as well. Pair it with a polite status that says
131
+ * what is happening; this prop draws nothing a reader hears.
132
+ */
133
+ busy?: boolean;
134
+ /**
135
+ * Which edge of the trigger the menu lines up with (MOTIR-7758). `start`
136
+ * (the default) aligns the menu's left edge with the trigger's, as before;
137
+ * `end` aligns the RIGHT edges, so a menu wider than a trigger that sits in a
138
+ * right-hand corner grows leftwards instead of running past the viewport.
139
+ */
140
+ align?: 'start' | 'end';
110
141
  }
111
- declare function Combobox<T extends string>({ options, value, onChange, label, placeholder, searchable, searchPlaceholder, emptyText, loading, loadingText, disabled, id, className, autoOpen, onClose, query: controlledQuery, onQueryChange, footer, }: ComboboxProps<T>): react_jsx_runtime.JSX.Element;
142
+ declare function Combobox<T extends string>({ options, value, onChange, label, placeholder, searchable, searchPlaceholder, emptyText, loading, loadingText, disabled, id, className, autoOpen, onClose, query: controlledQuery, onQueryChange, footer, triggerIcon, busy, align, }: ComboboxProps<T>): react_jsx_runtime.JSX.Element;
112
143
 
113
144
  export { Combobox, type ComboboxOption, type ComboboxProps };
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { useState, useId, useRef, useMemo, useCallback, useEffect, Fragment as Fragment$1, useSyncExternalStore, useLayoutEffect } from 'react';
3
3
  import { createPortal } from 'react-dom';
4
- import { Check, ChevronsUpDown } from 'lucide-react';
4
+ import { Check, LoaderCircle, ChevronsUpDown } from 'lucide-react';
5
5
  import { clsx } from 'clsx';
6
6
  import { twMerge } from 'tailwind-merge';
7
7
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
@@ -58,8 +58,13 @@ function fixedContainingBlock(el) {
58
58
  function paddingBox(el) {
59
59
  const r = el.getBoundingClientRect();
60
60
  const top = r.top + el.clientTop;
61
+ const left = r.left + el.clientLeft;
61
62
  const height = el.clientHeight || r.height;
62
- return { top, left: r.left + el.clientLeft, bottom: top + height };
63
+ const width = el.clientWidth || r.width;
64
+ return { top, left, bottom: top + height, right: left + width };
65
+ }
66
+ function viewportRight() {
67
+ return document.documentElement.clientWidth || window.innerWidth;
63
68
  }
64
69
  function Combobox({
65
70
  options,
@@ -79,7 +84,10 @@ function Combobox({
79
84
  onClose,
80
85
  query: controlledQuery,
81
86
  onQueryChange,
82
- footer
87
+ footer,
88
+ triggerIcon,
89
+ busy = false,
90
+ align = "start"
83
91
  }) {
84
92
  const [open, setOpen] = useState(autoOpen && !disabled);
85
93
  const serverFiltered = onQueryChange !== void 0;
@@ -123,13 +131,14 @@ function Combobox({
123
131
  setListMaxHeight(Math.max(80, Math.min(256, avail - (searchable ? 52 : 12))));
124
132
  const style = {
125
133
  position: "fixed",
126
- left: Math.round(rect.left),
127
134
  minWidth: Math.round(rect.width)
128
135
  };
136
+ if (align === "end") style.right = Math.round(viewportRight() - rect.right);
137
+ else style.left = Math.round(rect.left);
129
138
  if (placeBelow) style.top = Math.round(rect.bottom + gap);
130
139
  else style.bottom = Math.round(viewportH - rect.top + gap);
131
140
  setMenuStyle(style);
132
- }, [searchable]);
141
+ }, [searchable, align]);
133
142
  const updateInlinePosition = useCallback(() => {
134
143
  const trigger = triggerRef.current;
135
144
  if (!trigger) return;
@@ -166,12 +175,13 @@ function Combobox({
166
175
  const measured = menu2 && list ? menu2.offsetHeight - list.offsetHeight : 0;
167
176
  const chrome = measured > 0 ? measured : searchable ? 52 : 12;
168
177
  setListMaxHeight(Math.max(80, Math.min(256, avail2 - chrome)));
169
- const origin = cb ? paddingBox(cb) : { top: 0, left: 0, bottom: window.innerHeight };
178
+ const origin = cb ? paddingBox(cb) : { top: 0, left: 0, bottom: window.innerHeight, right: viewportRight() };
170
179
  const style = {
171
180
  position: "fixed",
172
- left: Math.round(rect2.left - origin.left),
173
181
  minWidth: Math.round(rect2.width)
174
182
  };
183
+ if (align === "end") style.right = Math.round(origin.right - rect2.right);
184
+ else style.left = Math.round(rect2.left - origin.left);
175
185
  if (placeAbove2) style.bottom = Math.round(origin.bottom - (rect2.top - gap2));
176
186
  else style.top = Math.round(rect2.bottom + gap2 - origin.top);
177
187
  setInlineAbove(placeAbove2);
@@ -189,7 +199,7 @@ function Combobox({
189
199
  setInlineAbove(placeAbove);
190
200
  const avail = Math.max(80, placeAbove ? spaceAbove : spaceBelow);
191
201
  setListMaxHeight(Math.max(80, Math.min(256, avail - (searchable ? 52 : 12))));
192
- }, [searchable]);
202
+ }, [searchable, align]);
193
203
  useEffect(() => {
194
204
  if (!(open && mounted)) return;
195
205
  const t = setTimeout(() => {
@@ -347,6 +357,7 @@ function Combobox({
347
357
  role: "option",
348
358
  "aria-selected": isSelected,
349
359
  "aria-disabled": opt.disabled ? true : void 0,
360
+ lang: opt.lang,
350
361
  onMouseEnter: () => setActiveIndex(i),
351
362
  onClick: () => commit(i),
352
363
  className: cn(
@@ -416,7 +427,11 @@ function Combobox({
416
427
  // `inlineFixed`. Otherwise an absolute child of the trigger that flips
417
428
  // above it when the dialog has more room there, so a tall list near the
418
429
  // modal's bottom edge isn't clipped (clamped + scrolled).
419
- inlineFixed ? null : cn("absolute left-0 min-w-full", inlineAbove ? "bottom-full mb-1" : "top-full mt-1")
430
+ inlineFixed ? null : cn(
431
+ "absolute min-w-full",
432
+ align === "end" ? "right-0" : "left-0",
433
+ inlineAbove ? "bottom-full mb-1" : "top-full mt-1"
434
+ )
420
435
  ),
421
436
  children: menuInner
422
437
  }
@@ -449,6 +464,7 @@ function Combobox({
449
464
  "aria-expanded": open,
450
465
  "aria-controls": open ? listId : void 0,
451
466
  "aria-label": label,
467
+ "aria-busy": busy || void 0,
452
468
  disabled,
453
469
  onClick: () => open ? closeMenu() : openMenu(),
454
470
  onKeyDown: onTriggerKeyDown,
@@ -459,15 +475,26 @@ function Combobox({
459
475
  className
460
476
  ),
461
477
  children: [
478
+ triggerIcon ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: ICON_SLOT, children: triggerIcon }) : null,
462
479
  selected ? /* @__PURE__ */ jsxs(Fragment, { children: [
463
480
  selected.icon ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: ICON_SLOT, children: selected.icon }) : null,
464
- /* @__PURE__ */ jsx("span", { className: "text-(--el-text) truncate", children: selected.label }),
481
+ /* @__PURE__ */ jsx("span", { lang: selected.lang, className: "text-(--el-text) truncate", children: selected.label }),
465
482
  selected.secondary ? (
466
483
  // identifier (= slate, the -secondary weight): AA on the trigger surface at 12px (as above).
467
484
  /* @__PURE__ */ jsx("span", { className: "text-(--el-text-identifier) ml-auto truncate text-xs", children: selected.secondary })
468
485
  ) : null
469
486
  ] }) : /* @__PURE__ */ jsx("span", { className: "text-(--el-text-muted) truncate", children: placeholder }),
470
- /* @__PURE__ */ jsx(ChevronsUpDown, { className: "text-(--el-icon-muted) ml-auto h-4 w-4 shrink-0", "aria-hidden": true })
487
+ busy ? (
488
+ // The spinner takes the chevron's 16px slot, so the trigger keeps its size.
489
+ /* @__PURE__ */ jsx(
490
+ LoaderCircle,
491
+ {
492
+ "data-combobox-busy": "",
493
+ className: "text-(--el-icon-muted) ml-auto h-4 w-4 shrink-0 animate-spin",
494
+ "aria-hidden": true
495
+ }
496
+ )
497
+ ) : /* @__PURE__ */ jsx(ChevronsUpDown, { className: "text-(--el-icon-muted) ml-auto h-4 w-4 shrink-0", "aria-hidden": true })
471
498
  ]
472
499
  }
473
500
  ),