Skip to content

Releases: code-sherpas/pharos-react

v0.14.3

Choose a tag to compare

@code-sherpas-releases code-sherpas-releases released this 18 Jun 02:47
Immutable release. Only release title and notes can be modified.

Patch Changes

  • d67e70c: fix(Combobox): anchor the multi-select popup to the chips control box, not the input

    In a Combobox multiple, Base UI resolves the popup anchor as
    inputGroupElement ?? inputElement. Single-select wraps Base UI's InputGroup
    (ComboboxControl), so the popup anchored to the full-width control. But
    multi-select uses Combobox.Chips (ComboboxChips) as the bordered box, and
    Chips registers no inputGroupElement — so the anchor fell back to the
    Input, which floats at the end of the chip row. Once several chips pushed the
    input toward the right edge, the popup opened narrow and shifted right
    (anchored at the typing caret) instead of keeping the control width.

    ComboboxChips now publishes its DOM node through an internal context that
    ComboboxContent reads and passes as the Positioner's anchor, so the popup
    keeps a constant width = the control (width: var(--anchor-width)),
    left-aligned, regardless of where the input sits. Single-select is unchanged:
    no chips box means no anchor override, so Base UI keeps anchoring to the
    ComboboxControl as before.

    No public TypeScript API change. Surfaced in Alexandria's AddUsers /
    AddSkills multi-select pickers.

v0.14.2

Choose a tag to compare

@code-sherpas-releases code-sherpas-releases released this 18 Jun 00:26
Immutable release. Only release title and notes can be modified.

Patch Changes

  • d70eb1a: fix(overlays): give the anchored popups a configurable z-index so they clear host-app overlays

    Combobox, Select, DropdownMenu and Popover portal their popup to the end
    of <body>, which floats it above in-flow content — but not above a
    host-app overlay that carries its own z-index (a drawer, a modal). Worse, a
    z-index set on the popup surface (*Content) is inert: Base UI's Positioner
    anchors with transform, which creates a stacking context that traps any
    z-index on its descendants. So a consumer could not lift the popup from the
    outside at all.

    The z-index now lives on the Positioner (where it is not trapped), sourced
    from var(--pharos-z-index-popover, 1000). Apps with no high z-index scale get
    a sensible default; an app whose own overlays sit higher (e.g. a drawer at
    z-index: 10000) raises every Pharos popup at once by overriding
    --pharos-z-index-popover in :root — no per-call-site className. This closes
    the long-standing "shared --pharos-z-index-* scale" follow-up the four
    overlay modules carried.

    No public TypeScript API change. Surfaced while adopting the Combobox-multiple
    pickers in Alexandria, whose AddSkills picker lives inside a z-index: 9999
    drawer and had its listbox rendering behind the drawer.

v0.14.1

Choose a tag to compare

@code-sherpas-releases code-sherpas-releases released this 17 Jun 02:57
Immutable release. Only release title and notes can be modified.

Patch Changes

  • a9605d8: fix(Select): anchor the listbox below the trigger instead of overlapping it

    SelectContent now forces Base UI's alignItemWithTrigger to false. Base UI's
    Select.Positioner defaults it to true — the native-<select> / macOS
    behaviour that floats the popup over the trigger and, as a side effect,
    silently ignores the side / align / sideOffset props (it forces
    renderedSide = 'none'). The listbox therefore covered the trigger box, and the
    documented "opens below the trigger, 8px away" defaults never applied.

    With alignItemWithTrigger={false} the listbox opens below the trigger with
    the 8px sideOffset, matching the dominant web convention (shadcn
    position="popper", Ant, Mantine) and — decisively — making Select consistent
    with Combobox, whose Base UI primitive has no item-alignment mode and always
    anchors adjacent. No public API change. D17 refinement; see NAMING-decisions § Select + Combobox.

    Also fixes a pre-existing critical a11y issue surfaced by the same change:
    SelectSeparator re-exported Base UI's generic Separator (role="separator"),
    which is not a permitted child of the listbox role and failed axe's
    aria-required-children on a grouped Select. It now renders decoratively
    (aria-hidden, role="none") — the grouping semantics already live on
    SelectGroup / SelectLabel, matching Radix / shadcn.

v0.14.0

Choose a tag to compare

@code-sherpas-releases code-sherpas-releases released this 13 Jun 07:19
Immutable release. Only release title and notes can be modified.

Minor Changes

  • 6345498: Add the Select and Combobox atoms — the selection family (Decision D17).

    Two atoms split by interaction contract, matching the shadcn / Base UI / ARIA
    APG consensus:

    • Select — pick from a known set of options (the ARIA listbox pattern,
      no text input). Wraps Base UI's Select.*. Parts: Select, SelectTrigger,
      SelectValue, SelectContent, SelectItem, SelectGroup, SelectLabel,
      SelectSeparator.
    • Combobox — a text input that filters a known set (the APG combobox
      pattern). Wraps Base UI's Combobox.*. Parts: Combobox, ComboboxControl,
      ComboboxChips, ComboboxInput, ComboboxTrigger, ComboboxClear,
      ComboboxChip, ComboboxChipRemove, ComboboxContent, ComboboxList,
      ComboboxItem, ComboboxEmpty, ComboboxGroup, ComboboxGroupLabel,
      ComboboxSeparator.

    Multi-select is the multiple axis on each root (not a separate atom);
    single-select closes on pick, multi-select keeps the popup open. Both popups
    take min-width: var(--anchor-width) so they are never narrower than their
    control. The trigger/control share the Input chrome (WCAG 1.4.11 border tone,
    brand focus ring, sm/md/lg size grid). Following Escuela 1 (D11) neither
    atom owns label/helper/error — compose those and convey error via
    aria-invalid.

v0.13.0

Choose a tag to compare

@code-sherpas-releases code-sherpas-releases released this 13 Jun 03:17
Immutable release. Only release title and notes can be modified.

Minor Changes

  • 0cb3869: Add the Popover compound atom (Decision D16).

    Free-form content anchored to a trigger, wrapping Base UI's Popover.*
    parts, which implement the ARIA dialog pattern as a non-modal
    disclosure: role="dialog" on the popup, focus moves into it on open and
    returns to the trigger on close, Escape and outside-click dismiss it, and
    focus is NOT trapped (the rest of the page stays interactive).

    Naming follows shadcn rather than Base UI's Popover parts. The surface
    collapses Base UI's Portal + Positioner + Popup into a single
    PopoverContent exposing side / align / sideOffset / alignOffset
    (align defaults to center, unlike DropdownMenu's start). Parts shipped
    in v1: Popover, PopoverTrigger, PopoverContent, PopoverTitle,
    PopoverDescription, PopoverClose.

    Deliberately distinct from the DropdownMenu atom (D15): a DropdownMenu is
    for commands (menu-button contract — role="menu"/menuitem, roving
    focus); a Popover holds arbitrary content (forms, navigation, panels) under
    the disclosure/dialog contract. The two wrap different Base UI primitives
    (Menu vs Popover) and share no machinery — 7 of 8 surveyed top-tier
    design systems separate them. PopoverArrow, a non-trigger PopoverAnchor
    (the future Combobox will want it) and a modal Backdrop are deferred — no
    consumer exercises them yet and the compound API makes them additive without
    a breaking change.

v0.12.0

Choose a tag to compare

@code-sherpas-releases code-sherpas-releases released this 12 Jun 22:34
Immutable release. Only release title and notes can be modified.

Minor Changes

  • 270aec0: Add the DropdownMenu compound atom (Decision D15).

    An action menu anchored to a trigger, wrapping Base UI's Menu.* parts,
    which implement the ARIA APG menu-button pattern (role="menu" /
    menuitem, roving focus, arrow-key navigation, typeahead, and
    Escape-to-close with focus return to the trigger).

    Naming follows shadcn rather than Base UI's Menu. The surface collapses
    Base UI's Portal + Positioner + Popup into a single
    DropdownMenuContent exposing side / align / sideOffset. Parts
    shipped in v1: DropdownMenu, DropdownMenuTrigger,
    DropdownMenuContent, DropdownMenuItem (with a destructive variant),
    DropdownMenuSeparator, DropdownMenuLabel, DropdownMenuGroup.

    Deliberately distinct from a future Popover atom: a DropdownMenu is for
    commands (menu-button contract); free-form anchored content belongs in a
    Popover (disclosure/dialog contract). 7 of 8 surveyed top-tier design
    systems separate the two. CheckboxItem, RadioItem, submenus and
    LinkItem are deferred — no consumer exercises them yet and the compound
    API makes them additive without a breaking change.

v0.11.1

Choose a tag to compare

@code-sherpas-releases code-sherpas-releases released this 12 May 01:32
Immutable release. Only release title and notes can be modified.

Patch Changes

  • f052ca8: Mark the published ESM bundle as client-only via a "use client" banner.

    Avatar (D14) calls createContext(...) three times at module top level
    (shape / loading / group). Without the banner, Next.js evaluates the
    Pharos module during its RSC build-time analysis pass and fails with
    TypeError: (0 , c.createContext) is not a function while collecting
    page data for any route whose layout transitively imports an Avatar.

    The banner is the same convention every React DS ships with
    (MUI / Chakra / Radix / shadcn). The atoms that have no hooks
    (Card / Separator) lose server-render-ability as a side effect; this is
    acceptable since they were never advertised as server-safe and React DS
    libraries are universally client-only today.

    The Avatar source file also carries its own "use client" directive so
    the intent is visible to anyone reading the source.

v0.11.0

Choose a tag to compare

@code-sherpas-releases code-sherpas-releases released this 11 May 23:50
Immutable release. Only release title and notes can be modified.

Minor Changes

  • 584821e: Add the Avatar compound atom (Decision D14). Ships Avatar,
    AvatarImage, AvatarFallback, and AvatarGroup as named exports.
    • Three named sizes (sm / md / lg = 32 / 40 / 48 px) align with
      the IconButton height grid; a numeric size={number} writes
      width / height inline for one-off cases (profile pictures,
      compact stacks).
    • Two shapes (circle default, square via the radius-md token).
    • Compound API (AvatarImage + AvatarFallback) follows the
      shadcn / Radix / Base UI / Chakra contract — the fallback content is
      composed explicitly by the consumer (Escuela 1, D11). No magic
      name prop, no built-in initials computation.
    • AvatarGroup stacks Avatar children with proportional negative
      margin and a box-shadow ring that defaults to the page surface
      (--pharos-avatar-group-ring). The max prop caps visible avatars
      and collapses the surplus into a final +N Avatar with accessible
      name {N} more.
    • render prop on the root composes the Avatar as a different element
      while keeping its styles (e.g. render={<a href="/profile" />}).
    • No status-badge primitive in v1 — deferred to composition with a
      future Badge positioning helper.

v0.10.0

Choose a tag to compare

@code-sherpas-releases code-sherpas-releases released this 30 Apr 22:28
Immutable release. Only release title and notes can be modified.

Minor Changes

  • a3f5ef3: Add IconButton atom — icon-only pressable control. Dedicated atom rather than a <Button size="icon"> variant (Decision D13, 2026-04-30): six of eight top-tier DSes ship a dedicated IconButton (Material 3, MUI, Chakra, Radix Themes, IBM Carbon, plus Mantine's ActionIcon), and a dedicated atom enforces aria-label (or aria-labelledby) at the type level instead of leaving WCAG 4.1.2 compliance as a runtime warning the way Chakra / Mantine do.

    Public API mirrors Button: intent="primary | secondary | ghost | destructive" × size="sm | md | lg" × native <button> props × Base UI render prop for composition (<IconButton render={<Link to="/next" />} aria-label="Next">). Defaults: intent="ghost" (the dominant icon-only case is low-emphasis — close, dismiss, toolbar action), size="md". Square dimensions matching the Button height grid (32 / 40 / 48 px); border-radius: var(--pharos-radius-full) produces a perfect circle. Direct-child > svg selector sizes Lucide icons (which ship with hardcoded width="24" height="24") to 16 / 20 / 24 px to match the icon slot.

    isLoading prop wired into the atom because the icon-only case is materially worse without it: with a Button the consumer can compose <Button disabled><Spinner /><span>Saving</span></Button>, but with an IconButton the slot has to be swapped (icon while idle, Spinner while loading), forcing two render branches at every async call-site. Setting isLoading swaps the slot for <Spinner size={size}/>, sets disabled, and exposes aria-busy="true".

    No integrated Tooltip (diverges from IBM Carbon's mandatory tooltip): aria-label already satisfies WCAG 4.1.2; tooltip is a separate composition concern and a separate atom (not yet shipped). No selected toggle state in v1: no Alexandria call-site exercises the toggle pattern today; additive when needed.

    Adoption contract documented in NAMING-decisions.md — the release pairs with an Alexandria PR that replaces CloseButton, CloseButtonCircle, NextCircleButton, PreviousCircleButton with the corresponding <IconButton> mappings, and folds the icon-only call-sites of AsyncLoadingButton / PublishButton / UnpublishButton / AddButton into the atom's built-in isLoading.

v0.9.0

Choose a tag to compare

@code-sherpas-releases code-sherpas-releases released this 29 Apr 02:28
Immutable release. Only release title and notes can be modified.

Minor Changes

  • 92d84b0: Add Spinner atom — visual loading indicator with role="status" for assistive tech, three sizes (sm / md / lg) aligned with the form-control grid (16 / 20 / 24 px), and currentColor inheritance so it picks up the parent's text colour automatically (composition cases like <Button intent="primary"><Spinner /></Button> work out of the box, no Button isLoading prop required at the atom level).

    Inline SVG with CSS @keyframes rotation — zero icon-library dependencies (no lucide-react, no framer-motion). Honours prefers-reduced-motion by slowing the rotation to 4 s instead of removing it (assistive UX still benefits from the in-progress cue).

    srLabel prop (default "Loading…") is rendered as visually-hidden text inside the status node; pass an action-specific label ("Saving template…", "Deleting…") when relevant. No tone axis on the atom; consumers set the colour by setting color on the parent or via className — same pattern Radix Themes / shadcn / Mantine use.