Commit graph

37 commits

Author SHA1 Message Date
a49f216c43 fix: fill the RadioGroup indicator's own SVG path instead of faking it with a background
The checked-state dot used bg-primary-foreground behind an unfilled,
stroke-only CircleIcon (fill="none" by default) -- relying on the
stroke and background happening to share the same color to look like
a solid dot. That's fragile and, combined with a size-2 box (50% of
the outer ring), reads as an oversized hole rather than a normal radio
dot. Fill the icon's own path directly (fill-primary-foreground) and
size it down to a more typical proportion (size-1.5, ~40% of the
ring). Verified centered and correctly proportioned in both themes via
headless Chromium (pixel-precise marker overlay + zoomed screenshots).
2026-07-14 10:39:18 -04:00
8cee97614d fix: use text-foreground for the Dialog close icon
The close icon used the Button "secondary" variant's
text-secondary-foreground, a near-white token fixed across both
themes. That reads fine against the dark Hades popover but is almost
invisible against the light Apollo popover since bg-transparent
removes the variant's own background. text-foreground already adapts
per-theme and is what a plain icon-only dismiss button should use.
Pre-existing bug, unrelated to the reka-ui removal (same classes
existed before) -- confirmed via computed-style + screenshot checks in
both themes in headless Chromium.
2026-07-14 09:40:45 -04:00
a2685c6920 fix: stop Select/Dialog enter animations from transitioning position
Root cause: Tailwind's `duration-100` utility sets the literal CSS
`transition-duration` property (it's meant for transitions, not
animations). Since `transition-property`'s initial value is `all` and
nothing overrode it, every element using `duration-100` in its
Transition enter/leave-active-class picked up an unintended
`transition: all 100ms`. For SelectContent, whose `top`/`left` are set
via inline style from usePopoverPosition, this meant the popover's
real computed position (set synchronously on mount) got smoothly
interpolated from the reactive object's initial {top:0,left:0} default
-- visibly flying in from the top-left corner of the viewport before
landing in place.

Fix: use tw-animate-css's dedicated `animation-duration-*` utility
instead, which sets `animation-duration`/`--tw-animation-duration`
without ever touching `transition-property`/`transition-duration`.
Confirmed via computed-style inspection in headless Chromium that
transitionDuration is now 0s while the intended enter/exit keyframe
animation still runs for 100ms. Applied everywhere `duration-100` was
used for this pattern (DialogContent, DialogOverlay,
DialogScrollContent, SelectContent).
2026-07-14 09:39:12 -04:00
b8fb6769c7 fix: stop unregistering Select item labels on unmount
SelectItem removed its entry from the shared itemLabels map on
unmount, which fires for every item when the dropdown closes --
including the item that was just selected. That wiped the trigger's
display label right after selecting (and after Escape/close in
general). Labels only need to be learned once and can safely persist,
matching the documented behavior in tests/Select.spec.ts. Found via
manual Storybook verification.
2026-07-13 18:39:31 -04:00
8c830438d5 fix: unwrap Fragment vnodes in Slot before cloning
<slot /> used as a passthrough outlet inside an SFC template (e.g.
DialogTrigger.vue's <Primitive as-child><slot /></Primitive>) resolves
through Vue's renderSlot() helper, which always wraps the forwarded
content in a Fragment vnode -- even when there's exactly one real
child. Slot was cloning that inert Fragment wrapper instead of the
real child, so attrs merged onto asChild-forwarded elements (data-slot,
aria-*, the click handler) silently vanished. Found via manual
Storybook verification: the Dialog trigger rendered but never opened
the dialog on click.

Unwrap single-child Fragments before merging/cloning, and add a
regression test through a real SFC fixture (a raw h()-based test
doesn't reproduce this, since it never goes through renderSlot()).
2026-07-13 18:37:50 -04:00
8497ddeed4 chore: drop the reka-ui dependency
Every component that used to depend on it has been rewritten on small
local primitives (previous commits). reka-ui is no longer a
peerDependency of @nychthemeron/library, so consumers no longer need
to install it.
2026-07-13 18:18:55 -04:00
81f7e0c2cc refactor: rewrite SelectContent and scroll buttons without reka-ui
Positioning via usePopoverPosition, focus/dismiss via useFocusTrap +
useDismissableLayer, arrow/Home/End navigation via useRovingFocus, plus
inline typeahead. This is the last reka-ui import in the library.
2026-07-13 18:17:53 -04:00
1cb1486fbd refactor: rewrite SelectTrigger/SelectItem without reka-ui
role="combobox" trigger and role="option" items driven by the shared
Select context. SelectContent (next commit) is needed before the
Select family works end-to-end again.
2026-07-13 18:16:55 -04:00
f69a3b0c65 refactor: rewrite the Select root family without reka-ui
Root/Value/Label/Group/Separator/ItemText now share a small
provide/inject context. SelectValue's label lookup is backed by an
itemLabels map that SelectItem populates on mount (next commit) --
matching the existing behavior where the trigger's displayed label
only resolves once the dropdown has opened at least once.
2026-07-13 18:16:17 -04:00
e7d96cc3ba refactor: rewrite DialogContent/DialogScrollContent without reka-ui
Uses Teleport + Transition + useFocusTrap + useDismissableLayer +
useScrollLock. The Dialog family no longer imports reka-ui at all.
2026-07-13 18:15:30 -04:00
3d8bdaa903 refactor: rewrite the Dialog root family without reka-ui
Root/Trigger/Overlay/Close/Title/Description now share a small
provide/inject context instead of reka-ui's Dialog primitives.
DialogContent/DialogScrollContent follow in the next commit since they
need the focus-trap and dismissable-layer composables.
2026-07-13 18:14:52 -04:00
07b44f6de4 refactor: rewrite RadioGroup without reka-ui
Uses a small provide/inject context plus useRovingFocus for arrow-key
selection. data-state/aria-checked contract is unchanged.
2026-07-13 18:13:23 -04:00
8175db2cde refactor: rewrite Checkbox and Switch without reka-ui
Both become plain role="checkbox"/role="switch" buttons driven by
useVModel, matching the existing Input.vue controlled-value pattern.
Classes and data-state/aria-checked contract are unchanged.
2026-07-13 18:12:38 -04:00
2341ceebe0 refactor: rewrite Button on the local Primitive utility
Drops the reka-ui Primitive import; behavior and classes are unchanged.
2026-07-13 18:11:58 -04:00
7fe65bd3ed feat: add usePopoverPosition composable
Replaces reka-ui's floating-position logic for the Select listbox with
a simple flip-above/below fixed-position calculation.
2026-07-13 18:11:28 -04:00
0abc7c124d feat: add useRovingFocus composable
Replaces reka-ui's roving-tabindex/arrow-key navigation for RadioGroup
and Select.
2026-07-13 18:10:46 -04:00
57c94e1207 feat: add useDismissableLayer composable
Replaces reka-ui's Escape/click-outside dismissal behavior for Dialog
and Select.
2026-07-13 18:10:06 -04:00
cbe9e45af5 feat: add useFocusTrap composable
Replaces reka-ui's internal focus-trap behavior for Dialog and Select.
2026-07-13 18:09:18 -04:00
dc3aa2c816 feat: add Primitive/Slot polymorphic-rendering utility
First piece of the reka-ui removal: a small asChild/Slot implementation
to replace reka-ui's Primitive component.
2026-07-13 18:07:45 -04:00
23cb57420d fix: alert title contrast, and make Storybook dev serve live library source
Two independent fixes:

1. Alert titles used text-<severity> directly on a bg-<severity>/15 tinted
   background. For danger (a very dark base hue) this reads as near-invisible
   text-on-tinted-background. The original PrimeVue-era Message design kept
   body/title text neutral and only accented the icon/left-border with the
   severity color -- restore that: alert text stays text-foreground, severity
   shows via a border-l-4 accent stripe instead of tinted text.

2. Root-caused why none of the last several fixes were showing up without a
   full `bun run build`: @nychthemeron/library's package.json exports its "."
   entry as ./dist/index.js unconditionally, and every story imports from the
   bare package specifier. So Storybook's dev server was always resolving to
   whatever the dist bundle looked like at last build time, never live
   source -- editing packages/library/src and restarting `bun run dev` (even
   with caches cleared) never picked up changes. Vite auto-injects a
   "development" resolution condition during `vite dev`/serve (and
   "production" during build), which is the standard fix for exactly this
   monorepo dev-vs-published split: added a "development" condition to the
   "." export pointing at src/index.ts, matching the pattern the "./components"
   sub-export already used. Also added the matching "@" alias to
   packages/playground/vite.config.ts, since resolving straight to library
   source pulls in its "@/lib/utils"-style internal imports, which playground's
   own Vite instance didn't previously know how to resolve.

   Verified both paths: dev server now reflects source edits without a
   library rebuild, and `bun run build` still produces a correct dist bundle
   with the same fix baked in.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-13 15:23:13 -04:00
e1ee3c739d fix: restore the loading-icon spin animation
The @keyframes nych-spin rule and NychLoadingIcon's wreath fill/stroke
colors lived in the old nychthemeron.css, which got deleted in the Task 13
cleanup as "fully superseded" -- but this component-specific animation was
never actually ported over to the new tailwind.css, so the icon rendered
static instead of spinning. Restored, verified live via computed
animationPlayState/animationName on the wreath element (previously
untestable via the compiled-CSS-only checks used for the earlier fixes).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-13 11:46:21 -04:00
830ac3094b fix: self-host fonts instead of depending on Google Fonts CDN
Cinzel/IBM Plex Sans/IBM Plex Mono were loaded via a Google Fonts CDN
@import, unchanged from the original PrimeVue-era CSS. Any environment
without outbound network access (or that can't reach fonts.googleapis.com)
silently fell back to a generic system sans-serif, which is what surfaced
as "text still off" after the border/heading-font fixes. Switch to
@fontsource's self-hosted packages so the font files ship as real build
assets (26 woff2 files bundled) instead of a runtime network dependency.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-13 09:15:45 -04:00
5d6d647373 fix: restore visible borders and heading font on Card/Dialog/Select
The CLI's default style uses a very subtle ring-foreground/10 (10%-opacity
ring) instead of a real border for Card, DialogContent, and SelectContent,
which reads as invisible against our dark Hades theme. Swap to a real
border-border + shadow, matching the original PrimeVue-era design's
visible borders.

Also: CardTitle/DialogTitle/AlertTitle referenced a `cn-font-heading`
class that is never defined anywhere (dead weight from the CLI's default
style template), so titles silently fell back to the body font instead of
our --font-serif (Cinzel) heading font. Replaced with the real `font-serif`
utility. Also drops another dead class, `cn-menu-translucent`, from
SelectContent.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 15:15:28 -04:00
ff02f7ec72 chore: remove PrimeVue and the ThemeEngine, migration complete
- Delete packages/library/src/engine/ and engine.spec.ts; LoadingIcon is
  now exported directly instead of routed through a generic theming engine.
- Remove primevue from every package.json (root devDependencies, library
  peerDependencies, playground dependencies) and from vite.config.ts's
  externalized/global Rollup config; add reka-ui as the library's peer dep.
- Delete the hand-written nychthemeron.css/theme.css, now fully superseded
  by the Tailwind entry CSS; both library exports (./style, ./theme) point
  at it.
- Drop the PrimeVue plugin install from Storybook's preview.ts.
- Add a `@source "../../";` directive to tailwind.css: Tailwind's automatic
  content detection is scoped to whichever Vite project runs it, so when
  the playground consumes this CSS file it never scanned the sibling
  packages/library/src tree for class usage -- every component rendered
  with zero utility classes applied until this was added (verified via the
  compiled Storybook CSS output going from ~2KB/no .bg-primary rule to
  ~40KB with the full utility set, and the pre-fix screenshot that
  surfaced it: buttons and other components with a real background color
  showing as bare, near-invisible text on white).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 15:07:59 -04:00
7bb554aeb0 feat: migrate Dialog to shadcn-vue compound API
Also: fixes DialogContent/DialogFooter's close/action buttons to use our
actual Button variant set (secondary) instead of the CLI's default
ghost/outline variants, which don't exist on our re-themed Button.
Adds an eslint override so shadcn-vue's single-word ui/ component names
(Button.vue, Dialog.vue, ...) aren't flagged by vue/multi-word-component-names.
This was the last PrimeVue-wrapped component, so lib/index.ts no longer
imports anything from primevue or uses the ptThemeable helper.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 14:57:06 -04:00
4755f9ab6e feat: migrate Select to shadcn-vue compound API
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 14:50:36 -04:00
5d840fcc16 feat: migrate Message to shadcn-vue Alert
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 14:45:45 -04:00
fed19667f7 feat: migrate Card to shadcn-vue compound API
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 14:41:58 -04:00
d999cd95f6 feat: migrate RadioButton/RadioButtonGroup to shadcn-vue RadioGroup
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 14:39:22 -04:00
2eb438932b feat: migrate Checkbox and CheckboxGroup to shadcn-vue
CheckboxGroup is hand-built (no shadcn-vue stock equivalent) as a simple
name/layout provider via provide/inject, matching RadioGroup's pattern --
it does not coordinate an array modelValue across children the way
PrimeVue's did, since each Checkbox now owns its own boolean v-model.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 14:35:36 -04:00
5238d61f24 feat: migrate Tag to shadcn-vue Badge
Also fixes Card.stories.ts, which imported the now-removed Tag export.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 14:32:18 -04:00
7a4ae9ff6a feat: migrate ToggleSwitch to shadcn-vue Switch
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 14:28:51 -04:00
382d76c2d7 feat: migrate Textarea to shadcn-vue
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 14:24:29 -04:00
f3002c7eb1 feat: migrate InputText to shadcn-vue Input
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 14:22:14 -04:00
3a8dd109bb feat: migrate Button from PrimeVue to shadcn-vue
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 14:19:39 -04:00
106c214b16 feat: wire up Tailwind v4 and shadcn-vue token mapping alongside PrimeVue
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 14:15:37 -04:00
8751c150cd Inital Commit
All checks were successful
ci / build (push) Successful in 19s
ci / publish (push) Successful in 11s
ci / publish-docs (push) Successful in 41s
2026-06-14 14:50:44 -04:00