Repository navigation
Releases: haruhimemoe/ui
Releases · haruhimemoe/ui
Release list
v0.21.0
v0.20.0
Added
@haruhimemoe/ui/theme-light.css, a light scheme: import it aftertheme.css. Light surfaces, tinted ink,h1as the dark accent andh2as 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-lfor hues 30 to 200. For harumin.haruhime.moe's white site.- Lightness variables for every background and content step:
--b1-lto--b6-land--c1-l(--c2-lto--c4-lalready 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
Fixed
CodeBlock(and soMdxPre's fenced blocks) no longer shows a blank line between every line. Each line was adisplay:blockspan 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'stextContentno 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), soCommandPaletteButtonand an account link like "Sign in" share a center line instead of sitting about 4px apart on their text baselines.
Changed
CopyMarkdownButtonis a compact 28px pill (h-7 px-3, still 44px on a coarse pointer) to sit level with the byline's small text. Buttonsize="sm"is a 28px icon square, too narrow for a label, so this is the secondary button with a smaller height and padding.SiteHeaderundersm, 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.CommandPaletteButtonhides itsCtrl K/⌘Khint undersm, leaving the magnifier (and anychildren).ContentPagewith atoc: the toc/body grid gap isgap-6,xl:gap-10(wasgap-8at every width), matchingContentLayout'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/discordand the security contact email isharuhime@haruhime.moe.
v0.18.0
Added
- Button new
size="sm": a 28px icon-only square (still over the 24px WCAG 2.5.8 target);mdandlgunchanged. ChevronUpIconandChevronDownIcon: plain chevrons as static inline SVGs, matchingDiscordIcon/GitHubIcon's shape (defaultsize-5,aria-hidden, aclassNamereplaces the size).
Changed
SortableMoveButtonsdefault content is now the two chevron icons (aria-hidden; the accessible name stays on the button), sized"sm";upText/downTextstill swap in words and size back to"md"by default. Newsizeprop overrides either default. Neworientationprop ("horizontal"default,"vertical"stacks the buttons in a column). Each button'stitlenow mirrors itsaria-label. Accessible names ("Move {label} up"/"down") are unchanged.
v0.17.0
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),parseEmbedUrlandtype EmbedTarget(every YouTube URL form with start times, Twitch videos, clips and channels).articleData,type ArticleDataandtype 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.remarkHaruhimeoptionsfigures,embeds(defaulttrue),wordsPerMinute(default 200),mdxExports(defaultfalse: addsexport const toc/readingMinutes/wordsto a compiled MDX module, an author's own export of the same name kept) andcollect(hands the same data to a function, for react-markdown, whose plugin options don't have to cross Turbopack's serializable boundary).haruhimeSanitizeSchemaandtype SanitizeSchema: extends anhast-util-sanitizeschema (usually rehype-sanitize'sdefaultSchema) with the tags and attributes this kit's plugins write;trusted: truedrops theuser-content-id prefix for an app's own READMEs and changelogs.rehypeLocalHrefsandtype HastLike: rewrites an in-page#xlink to#user-content-xwhen the sanitizer prefixed the id but not the href.mdxMarkdownTransforms(plusfigureMarkdown,embedMarkdown,linkCardMarkdown): turns<Figure>,<Embed>and<MdxLinkCard>into their Markdown line for an app's.mdmirror, skipping fenced code.remarkHeadingIdsnow idsh2throughh4(wash2/h3).@haruhimemoe/ui/mdxnew element overrides, throughmdxComponents:h4(same anchor treatment ash2/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 likeDisclosure's toggle, works with no JS),kbd(Kbd's look) anddiv(aremarkEmbedsdata-embeddiv rendersEmbed). The GFM footnote labelh2now renders bare,sr-only, with no anchor.@haruhimemoe/ui/mdxnew article components:Figure(a sizedMdxImgwith 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 ofwhen/label/note rows,whena<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), andtype MdxArticleModule(the structural shape of a compiled MDX module withmdxExports: true, forawait import(...)),type ArticleDataandtype TocItem(re-exported from./remark).- Root
@haruhimemoe/ui:Toc,type TocProps,type TocItem: an article's table of contents, a sticky column fromxlup 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.ContentPagegrowsauthors(type ContentAuthor, through the newContentByline),published,lastUpdatednow formatted "Oct 4, 2026" (see Changed),readingMinutes,toc(rendered beside the body fromxlup) andfooter(rendered after the body, outsideProse), plusproseSizepassed straight toProse. Prosenewsizeprop ("base" | "sm", same asContentPage'sproseSize) and new element styles:h4,dl, figures, task lists, footnotes anddetails.
Changed
- h4 headings in existing Markdown and MDX now get an id and a hover-revealed
#anchor, throughremarkHeadingIds' 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 toremarkHaruhime. Prosenow stylesh4,dl, figures, task lists, footnotes anddetailswherever they appear in its content.mdxComponentshas six more keys (h4,img,input,details,kbd,div). An app's own key of the same name still wins.- The GFM footnotes label
h2loses its#anchor (it was a visible anchor beside invisible text). ContentPageprints ISO dates as "Oct 4, 2026" instead of "2026-10-04" on every docs, guides and legal page.ContentNav's classes move tocontentNavStyles.ts, shared withToc, with no visual change.
v0.16.0
Added
MapCard,MapSetCard,MapGroup,MapCover,MapPreviewButtonandMapCopyScope: map display from plain props.MapCarddraws 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, andleading,preview,badges,detailsandactionsslots. Itsmapprop takes@haruhimemoe/osu'sBeatmapMetaas-is.MapSetCardis a set with its difficulties,MapGroupa bucket with its heading, count, empty slots and one shared Copy ID scope.MapPreviewButtonplays a set's preview clip from b.ppy.sh (one clip per page),stopMapPreview()stops it. AlsomapCoverUrl,MAP_STATUS_LABELSand the typesMapCardProps,MapCardLabels,MapData,MapSetCardProps,MapSetDifficulty,MapGroupProps,MapCoverProps,MapCoverSize,MapPreviewButtonProps.CopyButtonstatusPosition("end"by default,"start"puts the status before the button) andreserveStatus(keeps the status's width so a press never moves the button).
v0.15.0
Added
- Sortable lists:
useSortable,SortableList,SortableHandle,SortableMoveButtons,SortableLayer,moveItem,SORTABLE_ITEMandSORTABLE_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 withdetail0 lifts and drops for screen readers in browse mode. Up and Down buttons run on the same path. "between" and "onto" containers, nested containers,canDroprefusals with a reason, asynconMovewith 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
Added
Dialog(client): the modal base, a native<dialog>that follows a controlledopen. It focusesinitialFocus, 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 toreturnFocus(). Escape and a backdrop press callonDismissinstead 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 modalalertdialogonDialog, opened from its own trigger or a controlledopen/onOpenChange. Focus starts on Cancel; Enter submits; one run at a time withpendingLabel, busy buttons that keep focus, and Escape and backdrop ignored while pending; a throw keeps it open withfailedMessagein an alert;tone="destructive"uses thedangerbutton;typeToConfirmadds a field (no autocomplete, autocapitalize or spellcheck) and keeps Confirmaria-disableduntil the text matches;returnFocusfor a confirm that removed its own row. New types:ConfirmDialogProps,ConfirmTone.dangerButton variant: white on rose-700, rose-800 on hover.InlineConfirm'sconfirmVariantandTypeToConfirm'svarianttake it too.- README: "Which confirm", the rule for no confirm,
InlineConfirm,ConfirmDialog,ConfirmDialogwithtypeToConfirm, andTypeToConfirm.
Fixed
CommandPaletteis built onDialog: closing it puts back the page's own inlineoverflowinstead 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
Added
SurfaceandsurfaceClasses: the list-item box (Card's b4 and 10px radius at p-3,mdp-4,lgp-5,asdiv, li, section, article, form or p), and its classes for other elements.LinkCardandCardLink: 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.mediafor a full-bleed banner. No landmark.CardGrid: a list of cards, one column on phones,columns2 or 3 from sm/lg,gapsm 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,tilesorgrid.EmptyState: the "nothing here yet" box,dashedorfilled,smormd, 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,accentorquiet, with acurrentitem marked by weight and an underline.SectionHeading: the h2 under a page's h1, with an optionaldetail,actionsand 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 nameCopy <code>unlesscopyLabel.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
Cardtakes its classes fromsurfaceClasses. No visual change.ContentIndexlays its cards out onCardGridandsurfaceClasses: the tiles get Card's 10px radius (from 6px) and a 10px gap (from 8px).
v0.12.0
Added
- Motion tokens in
theme.css:duration-short(150ms),duration-medium(250ms),duration-long(400ms),ease-standard,ease-enterandease-exit. Everytransition-*now readsduration-shortandease-standard, the values it had. - One reduced-motion rule: under
prefers-reduced-motion: reduceevery 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,c4andh1get 8 points lighter andh24 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.TextandtextClasses: a line of text in six tones (defaultc2,mutedc3,subtlec4,error,warningamber-300,success) and three sizes, for the error, warning and muted text apps wrote by hand.downloadonButtonLinkandTextLinkrenders a plain<a download>(no prefetch, no client routing), for any value butfalse.hideLabelonTextInput,Select,Textarea,CheckboxandRadioGroup: the label (legend for RadioGroup) is hidden visually but still names the control, with no gap left above it.ModBadgecolor(ModBadgeColor): any of the six bucket colors, the ten @haruhimemoe/pool palette colors orneutral, 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
c4edge, and code blocks, menus, the palette and its key hints ac4border. 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,h1andh2are defined through lightness variables (--c2-l,--c3-land--c4-ljoin--h1-land--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()addsw-fit(Button, ButtonLink, AsyncButton, CopyButton, Pagination). PassclassName="w-full"where a full-width button is wanted.self-starton buttons is now a no-op. - Buttons at
mdare 44px tall on touch screens (coarse:h-11). Secondary and ghost buttons get ac4edge under more contrast; every button keeps a 1px border in forced colors, and a disabled one uses the system's GrayText there. CopyMarkdownButtonfollows Button's classes again (a test now pins its copy).BrandPage's file links areTextLink 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
c4under more contrast. RangeSlider's value boxes also become 44px tall with 16px text on touch screens: they usefieldClasses, the same field look.