Skip to content

Releases: haruhimemoe/ui

v0.21.0

Choose a tag to compare

@dvhsh dvhsh released this 07 Oct 21:18
af723de

Added

  • harumin joins HARUHIME_TOOLS ("osu! Discord bot"), so every tool's footer and command palette link it, and HaruhimeToolId takes "harumin".

v0.20.0

Choose a tag to compare

@dvhsh dvhsh released this 07 Oct 05:28
4260e08

Added

  • @haruhimemoe/ui/theme-light.css, a light scheme: import it after theme.css. Light surfaces, tinted ink, h1 as the dark accent and h2 as the pale one, dark code tokens, and a negative contrast lift. Every ink step keeps 4.5:1 on every surface at every hue; the README gives the --h1-l for hues 30 to 200. For harumin.haruhime.moe's white site.
  • Lightness variables for every background and content step: --b1-l to --b6-l and --c1-l (--c2-l to --c4-l already existed). Unset, the dark palette is unchanged.
  • --wordmark-ink: HaruhimeWordmark's white parts read it (default #ffffff), so the light scheme can ink them.

v0.19.0

Choose a tag to compare

@dvhsh dvhsh released this 06 Oct 08:05
1c1d273

Fixed

  • CodeBlock (and so MdxPre's fenced blocks) no longer shows a blank line between every line. Each line was a display:block span with a "\n" text node between them, and inside the <pre> that text node rendered as an extra empty line. The text node is gone; the copy button still copies the source text with its real newlines. The <code> element's textContent no longer contains newlines.
  • ContentPage's header has a bottom margin (mb-8), so the meta row and "Copy as Markdown" button no longer touch the body.
  • SiteHeader's actions slot is a centered flex row (flex items-center gap-3), so CommandPaletteButton and an account link like "Sign in" share a center line instead of sitting about 4px apart on their text baselines.

Changed

  • CopyMarkdownButton is a compact 28px pill (h-7 px-3, still 44px on a coarse pointer) to sit level with the byline's small text. Button size="sm" is a 28px icon square, too narrow for a label, so this is the secondary button with a smaller height and padding.
  • SiteHeader under sm, when it has actions: the nav moves to its own full-width row below, so the actions stay on the brand's row instead of wrapping under the nav. The DOM order (brand, nav, actions) and so the tab order is unchanged.
  • CommandPaletteButton hides its Ctrl K/⌘K hint under sm, leaving the magnifier (and any children).
  • ContentPage with a toc: the toc/body grid gap is gap-6, xl:gap-10 (was gap-8 at every width), matching ContentLayout's nav/body gap (gap-6, lg:gap-10). On phones the "On this page" disclosure now sits the same 24px above the body as the "Contents" disclosure.
  • README and SECURITY.md: the Discord invite is now https://haruhime.moe/discord and the security contact email is haruhime@haruhime.moe.

v0.18.0

Choose a tag to compare

@dvhsh dvhsh released this 06 Oct 02:01
aca0579

Added

  • Button new size="sm": a 28px icon-only square (still over the 24px WCAG 2.5.8 target); md and lg unchanged.
  • ChevronUpIcon and ChevronDownIcon: plain chevrons as static inline SVGs, matching DiscordIcon/GitHubIcon's shape (default size-5, aria-hidden, a className replaces the size).

Changed

  • SortableMoveButtons default content is now the two chevron icons (aria-hidden; the accessible name stays on the button), sized "sm"; upText/downText still swap in words and size back to "md" by default. New size prop overrides either default. New orientation prop ("horizontal" default, "vertical" stacks the buttons in a column). Each button's title now mirrors its aria-label. Accessible names ("Move {label} up"/"down") are unchanged.

v0.17.0

Choose a tag to compare

@dvhsh dvhsh released this 05 Oct 22:03
39ac153

Added

  • @haruhimemoe/ui/remark: remarkFigures (a paragraph holding only an image becomes a <figure>, its title moving into a trailing <figcaption>), remarkEmbeds (a paragraph holding only a bare YouTube/Twitch URL becomes a click-to-load player), parseEmbedUrl and type EmbedTarget (every YouTube URL form with start times, Twitch videos, clips and channels). articleData, type ArticleData and type TocItem: a flat table of contents (h2 to h4), a word count (Intl.Segmenter, Japanese-safe) and a reading time from a document's heading ids. remarkHaruhime options figures, embeds (default true), wordsPerMinute (default 200), mdxExports (default false: adds export const toc/readingMinutes/words to a compiled MDX module, an author's own export of the same name kept) and collect (hands the same data to a function, for react-markdown, whose plugin options don't have to cross Turbopack's serializable boundary). haruhimeSanitizeSchema and type SanitizeSchema: extends an hast-util-sanitize schema (usually rehype-sanitize's defaultSchema) with the tags and attributes this kit's plugins write; trusted: true drops the user-content- id prefix for an app's own READMEs and changelogs. rehypeLocalHrefs and type HastLike: rewrites an in-page #x link to #user-content-x when the sanitizer prefixed the id but not the href. mdxMarkdownTransforms (plus figureMarkdown, embedMarkdown, linkCardMarkdown): turns <Figure>, <Embed> and <MdxLinkCard> into their Markdown line for an app's .md mirror, skipping fenced code. remarkHeadingIds now ids h2 through h4 (was h2/h3).
  • @haruhimemoe/ui/mdx new element overrides, through mdxComponents: h4 (same anchor treatment as h2/h3), img (a plain, lazy, async-decoded <img>), input (a GFM task-list checkbox becomes a named, disabled checkbox), details (a native <details>/<summary> pair styled like Disclosure's toggle, works with no JS), kbd (Kbd's look) and div (a remarkEmbeds data-embed div renders Embed). The GFM footnote label h2 now renders bare, sr-only, with no anchor.
  • @haruhimemoe/ui/mdx new article components: Figure (a sized MdxImg with an optional caption, credit and eager/priority loading), Steps (a numbered-rail wrapper for an ordered list), Embed (a click-to-load YouTube/Twitch player in a 16:9 box, with a poster image and a plain-link fallback for an unrecognized URL; poster={false} or a local poster replaces YouTube's own thumbnail), MdxLinkCard (one link named by its title, a description and a source line), Schedule (an ordered list of when/label/note rows, when a <time dateTime> or plain text), Glossary/Term (a <dl> of entries anchored at #term-<slug>, with alias anchors, and a dotted-underline link to one), Kbd/kbdClasses (re-exported from the root), and type MdxArticleModule (the structural shape of a compiled MDX module with mdxExports: true, for await import(...)), type ArticleData and type TocItem (re-exported from ./remark).
  • Root @haruhimemoe/ui: Toc, type TocProps, type TocItem: an article's table of contents, a sticky column from xl up plus a phone disclosure, both labelled "On this page", nesting a flat TOC by heading depth. Kbd, type KbdProps, kbdClasses: a keyboard key, shared with the command palette's own shortcut hints. ContentPage grows authors (type ContentAuthor, through the new ContentByline), published, lastUpdated now formatted "Oct 4, 2026" (see Changed), readingMinutes, toc (rendered beside the body from xl up) and footer (rendered after the body, outside Prose), plus proseSize passed straight to Prose.
  • Prose new size prop ("base" | "sm", same as ContentPage's proseSize) and new element styles: h4, dl, figures, task lists, footnotes and details.

Changed

  • h4 headings in existing Markdown and MDX now get an id and a hover-revealed # anchor, through remarkHeadingIds' new depth-4 support.
  • A paragraph holding only an image now renders as a <figure>; one holding only a bare YouTube/Twitch URL now renders a click-to-load player. Turn either off per document with { figures: false } / { embeds: false } passed to remarkHaruhime.
  • Prose now styles h4, dl, figures, task lists, footnotes and details wherever they appear in its content.
  • mdxComponents has six more keys (h4, img, input, details, kbd, div). An app's own key of the same name still wins.
  • The GFM footnotes label h2 loses its # anchor (it was a visible anchor beside invisible text).
  • ContentPage prints ISO dates as "Oct 4, 2026" instead of "2026-10-04" on every docs, guides and legal page. ContentNav's classes move to contentNavStyles.ts, shared with Toc, with no visual change.

v0.16.0

Choose a tag to compare

@dvhsh dvhsh released this 05 Oct 21:27
26d3235

Added

  • MapCard, MapSetCard, MapGroup, MapCover, MapPreviewButton and MapCopyScope: map display from plain props. MapCard draws one map as a row (packs' slot row) or a card (osu-web's beatmapset panel), with no background, the set's cover or a blurred cover behind a b5 overlay, comfortable or compact density, loading/missing/error states, an optional slot pill, status, stars and stats (overridable per key), an optional Copy ID, and leading, preview, badges, details and actions slots. Its map prop takes @haruhimemoe/osu's BeatmapMeta as-is. MapSetCard is a set with its difficulties, MapGroup a bucket with its heading, count, empty slots and one shared Copy ID scope. MapPreviewButton plays a set's preview clip from b.ppy.sh (one clip per page), stopMapPreview() stops it. Also mapCoverUrl, MAP_STATUS_LABELS and the types MapCardProps, MapCardLabels, MapData, MapSetCardProps, MapSetDifficulty, MapGroupProps, MapCoverProps, MapCoverSize, MapPreviewButtonProps.
  • CopyButton statusPosition ("end" by default, "start" puts the status before the button) and reserveStatus (keeps the status's width so a press never moves the button).

v0.15.0

Choose a tag to compare

@dvhsh dvhsh released this 05 Oct 21:21
5257790

Added

  • Sortable lists: useSortable, SortableList, SortableHandle, SortableMoveButtons, SortableLayer, moveItem, SORTABLE_ITEM and SORTABLE_CONTAINER, with their types. Drag by mouse, touch or pen (pointer events only, no HTML5 drag and drop) or by keyboard (Space or Enter to pick up, the arrows, Home, End, PageUp and PageDown to move, Space or Enter to drop, Escape to cancel); a click with detail 0 lifts and drops for screen readers in browse mode. Up and Down buttons run on the same path. "between" and "onto" containers, nested containers, canDrop refusals with a reason, async onMove with a pending state, auto-scroll near edges, an assertive live region with overridable text, a pointer chip that shows the label and why a drop won't take, and focus that stays on the moved item's handle or button. The app owns the data. No new dependencies.

v0.14.0

Choose a tag to compare

@dvhsh dvhsh released this 05 Oct 19:55
7d679b5

Added

  • Dialog (client): the modal base, a native <dialog> that follows a controlled open. It focuses initialFocus, locks the page's scroll (counted, restoring the page's own inline overflow), and hands focus back on close or on unmount to the opener, else to returnFocus(). Escape and a backdrop press call onDismiss instead of closing; a press only counts as a backdrop press when it starts and ends there; a close the browser makes is reported as "browser". A dialog opened from inside one that closes at the same time returns focus to that one's opener. Fades in on the motion tokens (motion), closes at once. New types: DialogProps, DialogDismissReason.
  • ConfirmDialog (client): a confirm in a modal alertdialog on Dialog, opened from its own trigger or a controlled open/onOpenChange. Focus starts on Cancel; Enter submits; one run at a time with pendingLabel, busy buttons that keep focus, and Escape and backdrop ignored while pending; a throw keeps it open with failedMessage in an alert; tone="destructive" uses the danger button; typeToConfirm adds a field (no autocomplete, autocapitalize or spellcheck) and keeps Confirm aria-disabled until the text matches; returnFocus for a confirm that removed its own row. New types: ConfirmDialogProps, ConfirmTone.
  • danger Button variant: white on rose-700, rose-800 on hover. InlineConfirm's confirmVariant and TypeToConfirm's variant take it too.
  • README: "Which confirm", the rule for no confirm, InlineConfirm, ConfirmDialog, ConfirmDialog with typeToConfirm, and TypeToConfirm.

Fixed

  • CommandPalette is built on Dialog: closing it puts back the page's own inline overflow instead of clearing it, a text selection dragged from the input onto the backdrop no longer closes it, and a command that opens another dialog sends focus back to whatever opened the palette once that dialog closes.

v0.13.0

Choose a tag to compare

@dvhsh dvhsh released this 05 Oct 17:41
598f09b

Added

  • Surface and surfaceClasses: the list-item box (Card's b4 and 10px radius at p-3, md p-4, lg p-5, as div, li, section, article, form or p), and its classes for other elements.
  • LinkCard and CardLink: a card that is one link. CardLink's cover fills the card, its name stays its own text, and LinkCard lifts every other control above the cover. media for a full-bleed banner. No landmark.
  • CardGrid: a list of cards, one column on phones, columns 2 or 3 from sm/lg, gap sm or md, wrapping each child in its own flex <li> so cards in a row match height.
  • StatList: label/value pairs as a <dl>, inline, tiles or grid.
  • EmptyState: the "nothing here yet" box, dashed or filled, sm or md, with an optional title and action.
  • Progress: a native <progress> named by its label, described by an always-mounted <output> status line.
  • LinkRow: a wrapping row of text links, plain or in a named nav, accent or quiet, with a current item marked by weight and an underline.
  • SectionHeading: the h2 under a page's h1, with an optional detail, actions and a # anchor.
  • PrevNext: previous/next links at the end of a page in a series, each named "Previous: <title>" or "Next: <title>".
  • CodeChip: inline code with a copy button, its name Copy <code> unless copyLabel.
  • CopyField (client): a read-only field in mono with focus-selects-all and a Copy row, described by the field's label.
  • SegmentedControl (client): a two to four way view switch on native radios, drawn like a pill track.

Changed

  • Card takes its classes from surfaceClasses. No visual change.
  • ContentIndex lays its cards out on CardGrid and surfaceClasses: the tiles get Card's 10px radius (from 6px) and a 10px gap (from 8px).

v0.12.0

Choose a tag to compare

@dvhsh dvhsh released this 04 Oct 14:53
031c067

Added

  • Motion tokens in theme.css: duration-short (150ms), duration-medium (250ms), duration-long (400ms), ease-standard, ease-enter and ease-exit. Every transition-* now reads duration-short and ease-standard, the values it had.
  • One reduced-motion rule: under prefers-reduced-motion: reduce every animation and transition finishes in 0.01ms and smooth scrolling turns off, in the kit and in the app's own markup. data-motion="essential" keeps an element's motion.
  • High contrast: under prefers-contrast: more, c2, c3, c4 and h1 get 8 points lighter and h2 4 points darker, through --contrast-lift (0% opts out).
  • The coarse: variant (@media (pointer: coarse)), for a touchscreen as the main pointer.
  • useMotionAllowed() (client): true when the visitor allows motion, false on the server, during hydration and under reduced motion, live. Lifted from haruhime.moe's homepage banner.
  • Text and textClasses: a line of text in six tones (default c2, muted c3, subtle c4, error, warning amber-300, success) and three sizes, for the error, warning and muted text apps wrote by hand.
  • download on ButtonLink and TextLink renders a plain <a download> (no prefetch, no client routing), for any value but false.
  • hideLabel on TextInput, Select, Textarea, Checkbox and RadioGroup: the label (legend for RadioGroup) is hidden visually but still names the control, with no gap left above it.
  • ModBadge color (ModBadgeColor): any of the six bucket colors, the ten @haruhimemoe/pool palette colors or neutral, overriding the color the mod's letters pick. A value outside the list falls back to the bucket.
  • 44px touch targets on a coarse pointer: chips and choice chips, checkbox and radio rows, Disclosure and HeaderMenu buttons and menu items, Tabs and LinkTabs, palette rows, FilterPanel's toggle, code copy buttons and ContentNav links.
  • Under more contrast, cards, chips, neutral badges and the tabs track get a c4 edge, and code blocks, menus, the palette and its key hints a c4 border. In forced colors, cards, chips, badges, mod badges and the tabs track keep a 1px border; an unavailable chip is GrayText.

Changed

  • c2, c3, c4, h1 and h2 are defined through lightness variables (--c2-l, --c3-l and --c4-l join --h1-l and --h2-l) plus the contrast lift. The default colors are unchanged.
  • Notice, field errors, CharCounter, AsyncButton's failure text and the palette's input error use Text's tones, so they get one step lighter under more contrast.
  • Button no longer stretches in a flex column or a grid cell: buttonClasses() adds w-fit (Button, ButtonLink, AsyncButton, CopyButton, Pagination). Pass className="w-full" where a full-width button is wanted. self-start on buttons is now a no-op.
  • Buttons at md are 44px tall on touch screens (coarse:h-11). Secondary and ghost buttons get a c4 edge under more contrast; every button keeps a 1px border in forced colors, and a disabled one uses the system's GrayText there.
  • CopyMarkdownButton follows Button's classes again (a test now pins its copy).
  • BrandPage's file links are TextLink download.
  • Fields are 44px tall with 16px text on touch screens (no iOS zoom), checkbox and radio rows 44px tall, and a field's border is c4 under more contrast.
  • RangeSlider's value boxes also become 44px tall with 16px text on touch screens: they use fieldClasses, the same field look.