Repository navigation
v0.22.0
A component library worth building on, and a richer rich-text editor.
foldkit-mixins-ui 0.11.0 grows from the parity components to the whole UI
plan: the polish pass over every component (state colour, concentric corners,
the top layer, overlay scroll bars, right to left), the merges into one Alert,
Dialog, Sheet, Menu, Slider and date system, and chat (ChatMessage,
MessageScroller, Thinking Indicator, Thinking Steps, Questionnaire) and Zag's
long tail (Angle Slider, Timer, Marquee, Image Cropper, Signature Pad, QR Code,
Chart, Cascade Select, Floating Panel, Tour). foldkit-mixins 0.12.0 for the
state ink, nests/nested(), surface.field, the shadow and chart tokens;
foldkit-primitives 0.11.0 for ScrollBars, Swipe, ScrollSettle,
PointerPosition and TrackBox; foldkit-richtext 0.4.0,
foldkit-richtext-dom 0.5.0, foldkit-mixins-richtext 0.4.0 and
foldkit-richtext-markdown 0.3.0 for the editor's changes below;
foldkit-remote-server 0.15.0 for RemoteServer.memory's entities and
optional principal; foldkit-auth 0.1.1 and foldkit-ssr 0.7.1 for Fetch
Metadata on a request with no Origin; foldkit-mixins-data-grid 0.2.3 for
the motion tiers; and foldkit-plus 0.4.0. Every package that pins one of
them republishes with a patch to re-pin: foldkit-builder 0.3.3,
foldkit-cms 0.7.3, foldkit-cms-drizzle 0.6.6, foldkit-mixins-builder
0.3.3, foldkit-mixins-crud 0.7.3, foldkit-mixins-form 0.6.2,
foldkit-remote-drizzle 0.12.3, foldkit-richtext-code 0.2.1,
foldkit-richtext-code-shiki 0.2.1 and foldkit-site 0.4.1.
Changes that can break code written for 0.21 are marked "Breaking" in the
entries below; most are in foldkit-mixins-ui and the rich-text packages.
Added
-
foldkit-primitives/dom:TrackBox. A Mount that follows the element
named by itsdata-targetand writes its viewport box (and, asked, its
corner radius) as--fk-<name>-x,-y,-w,-hand-radius, telling
TrackedTarget { target, found }; the Tour's cutout now draws with it
(itsSpotlightMount is gone, its properties are--fk-tour-wand-h,
and its ring follows the target's corner). -
foldkit-mixins-ui: Calendar options, presets and a locale. The
fourth argument ofCalendar.toView,inputsandresolve
(CalendarOptions) takesweekNumbers(ISO weeks),caption: 'label'or
month and year selects (dropdowns, answered byCalendar.show),
pagingwithCalendar.alongsidefor two months side by side, and a
range'spreviewunder the pointer. Presets areCalendar.lastDays,
nextDays,monthOf,dayandpresetsView;Calendar.localeOf(tag)
reads upstream'slocalefrom Intl; the recipe takesoutside: 'hidden'. -
foldkit-mixins-ui: Tabs, Segmented, Breadcrumb and a navigation menu
with panels. Tabs take icons and counts (labelandiconslots, a
Badge after the words),overflow: 'scroll'(one line, its edges
fading),size: 'lg', a vertical tablist's rule at its inline end, and
closable tabs (TabsContext.close: a close button and Delete; answer with
Tabs.closing). A Segmented option takes anicon, alone with
iconOnly; the recipe addstray: 'underline',width: 'fill'andsize: 'lg'. Breadcrumb'scollapse: { max, menu }makes the ellipsis open a
Menu of the hidden steps (Breadcrumb.menuRows,Breadcrumb.chosen).
Nav.viewdraws a navigation menu whose triggers open panels of link
columns in the top layer, with the APG's disclosure-navigation keys, its
open panel inNav.init/Nav.update; the recipe addsindicator: 'pill'
and a column. -
foldkit-mixins-ui: Steps, Accordion, Table, Progress, Rating and
Resizable grow. Steps takemarker: 'dot',connector: 'progress'with
the view'sprogress, andsizeon the Avatar's steps. The Accordion
takesvariant(plain,bordered,separated,filled,ghost),
separators,indicator: 'plus',indicatorPosition: 'start',active
and its ownicon. Table sorts (sortKey,sort: { by, onSort },
aria-sort,Table.nextSort), chooses rows with checkboxes
(selection), opens a row's detail below it (expansion) and stands an
emptystate across itself, all the application's; the recipe adds
rows,densityandheader: 'sticky'. Progress takesshowValue, a
value of stackedsections, abufferandsegments, and the recipe
tone,stripesandsize. Rating takesprecision: 0.5, a read-only
fraction,onClear,showValue, and Home and End (Rating.Ends); a
read-only rating takes noonChange; the recipe addsglyphand an
errortone. Resizable collapses a panel (collapsible,collapsedTo)
on Enter or a double-click, nests either way, andhandle: 'grip'shows
a pill of dots. -
foldkit-primitives/dom:ScrollSettle, a Mount that reports the
item nearest a scroller's start once a scroll rests (scrollend, or a
quiet scroll where it is missing), right to left from the right edge. -
foldkit-mixins-ui: a custom Select, and Combobox chips, clearing and an
empty panel. A select drawn over Listbox:Listbox.partsdraws an
option's icon, label, description and check, and the trigger's value (or
its placeholder, muted) and chevron;Recipes.Listboxgainscheck
(end,start,none),trigger(outline,filled,ghost) and size
lg. The Combobox takeschips(each chosen value a chip in the field,
its remove button choosing it again, which a multiple combobox toggles
off),clearContentandclearLabel(a clear button while the field has
text) andempty(a polite panel in the list's place with nothing to list,
a loading line too);Combobox.partsdraws an option with the typed text
marked.ComboboxChipRenderandComboboxExtraInputsare exported. -
foldkit-mixins-ui: TagsInput'smaxand refusals, PasswordInput's
strength and rules, InputCopy's reset.TagsInput.init({ max })holds at
mostmaxtags; a commit that adds nothing, or not all of the draft, is a
Refusal(Repeat,Empty,Full), said in a politemessageslot
(refusalTextwords it) with a one-time shake thattravelstills.
PasswordInputtakesstrength(aMeter),requirements
(PasswordRequirements listed under the field, each read as met or not)
andcapsLock: { on, onKey }, a polite hint while Caps Lock is on, read
from each key bycapsLockOf.InputCopygains aModel(init({ resetAfter }), default 2000ms) andupdate:Copiedshows the value as
copied and startsReset, which sets it back to "Copy" unless a later copy
came first. -
foldkit-mixins/theme: shadows tinted byshadow-handshadow-c.
Theme.oklch({ shadow: { h, c } })sets two knobs every shadow reads,
defaulting to the neutrals' hue at twice their chroma, so shadows take the
page's hue rather than a neutral black. -
foldkit-mixins-ui: Card, Avatar, Separator, Skeleton, Spinner, Item and
Empty grow their looks. Card takeselevation(0to3, a step of
the surface ladder: lighter in a dark scheme, a contact and an ambient
shadow in a light one) andlift: 'hover'(a lift under the pointer,
timestravel);Card.overlayis the one stretched press control a
pressable card or Item draws. Avatar takessrc,initials,status
andstatusLabel;Avatar.group({ people, max, label, moreLabel })draws
overlapping avatars and counts the rest as one more (AvatarGroupSlots,
Recipes.AvatarGroup({ overlap: 'tight' | 'loose' | 'none' })), and
Recipes.Avatargains sizesxsandxl,shape(circle,rounded,
square),color: 'name'(a hue from the name) andring. Separator
takeslabelanddecorative, and the recipeline(solid,dashed,
dotted),thickness,spacingandinset. Skeleton gainslines,
avatarRow,cardandtableRow({ columns }), andanimation(pulse,
shimmer,none). Spinner takesshowLabel, and the recipekind
(ring,dots,bars,pulse,orbit),sizeandtone(accent,
neutral,current). Item is the row primitive:press,label,
selectedanddisabled,Item.groupfor a list (ItemGroupSlots,
Recipes.ItemGroup({ layout: 'divided' | 'separated' })), and the recipe's
variant(plain,outline,muted) andmedia(plain,icon,
image). Empty takesmedia(icon,illustration),frame(none,
dashed,filled),tone(neutral,danger) andsize. -
foldkit-mixins-ui: menu check rows, groups and shortcuts; Command pages
and hints; a Toast component. A Menu row may be aCheckRow(check: 'checkbox' | 'radio',checked): choosing it sendsSelectedand leaves
the menu open, the application keepingchecked. Any row may name a
group(a heading, and a separator between groups), ashortcut(keys
drawn as Kbd withkbd, named inaria-keyshortcuts) andtone: 'destructive'. A Menubar's menus take the same rows, andMenubar.init(ids, { isAnimated, isModal })passes both to every menu. A Command row takes
icon,description(matched by the query) andopensPage; the view
takespage: { trail, onBack }(Backspace in an empty field goes back),
loading,hintsfor a footer of keys andkbd;Command.hotkey(message)
is ⌘K or Ctrl+K anywhere, andRecipes.Command({ frame: 'bare' })sits in
a dialog.Toastis now a component:init,show,settle,dismiss,
updateandview. Toasts stack after Sonner's, spread out when held,
pause while the pointer or focus is on the stack or the page is hidden
(keeping the time left), show three at most, swipe away toward their
edge, and are spoken in a polite or, for an error, an assertive region.
settleturns aloadingtoast into its outcome, a promise toast; an
actionsendsActedwith its value.Recipes.Toast({ tone: 'rich' })
tints each type.ToastViewstays for toasts that carry their own payload. -
foldkit-mixins-ui: popovers, tooltips and hover cards in the top layer,
and the Sheet.Anchor.layeraround a panel shows it in the browser's
top layer, above every stacking context and outside everyoverflowbox,
with upstream's anchor placing it (the newlayerslots), and anarrow
slot points at the trigger.Popover.anchor(),Tooltip.anchor()and
HoverIntent.anchor()are the placements: off the trigger (the Popover
12px, room for its larger arrow; the others 8px), 8px inside the viewport,
flipping and shifting to stay there. A Hover Card is placed
byHoverIntent.toView'sanchored: { id, anchor? }. The Popover and
Tooltip recipes takearrow: 'none'. The Dialog gainsbody,scroll,
scrollBar,scrollThumbandfooterslots (a long body scrolls under a
footer that stays),Dialog.overlayBars, sizesxlandfull,covered
(a dialog that another was opened from steps back) and
DialogContext.attributes.Sheetis a component over the Dialog: its
handle drags it betweenpointsand, unlessdismissible: false, away,
with vaul's thresholds;Sheet.view({ scalesPage })with
Recipes.sheetPageon the page beside it steps the page back.Swipeis
infoldkit-primitives/dom. -
foldkit-mixins-ui: a visible press and a filled toggle on buttons, four
badge looks, key combinations, richer breadcrumbs, pagination and steps.
A held button scales to 98% (the 2% multiplied bytravel, so reduced
motion holds its size); a solid or primary one also deepens to its
family'sactivefill (a new tone,fill-active) and clears the 1px border
of its own color its fill stands in, so the fill draws in by 1px a side.
A pressed toggle rests on its tone's fill (a solid one on theactive
fill), keeps it hovered (an unfilled one takes the hover fill), deepens to
theactivefill while held, and keeps its text on the fill throughout.
Badgetakesappearance:tinted(default),dot,outlineor
solid.Kbd.combo({ keys, joiner? })draws several keys in onekbd.
Breadcrumb steps takeicons, the view takes a customseparatorand
collapse: { max, onExpand }(withcollapsedOfas the pure split), and
the recipe takes aseparatoraxis:slash(default),chevronor
dot, a separator midway between its steps. Pagination takessiblings?,
boundaries?,numbered?,ends?andrange?(withsummaryOffor
"1–10 of 97", andrange.choose: { sizes, onPerPage, label? }for a
rows-per-page select), with newsummary,first,last,perPage,
perPageSelectandpageCountslots; the current stop holds" / 12",
which the recipe'scompactaxis (narrow, the default, below a 30rem
window;always;never) shows while it hides the other numbers. Steps
take per-stepicons and verticalcontent, whose line reaches both
markers. -
foldkit-mixins-ui: radio cards, and a wrapping row.RadioGroupSlots
gainscard, an option's whole row, which the application draws around
its radio and words (h.label(option.card, …)) and which is
data-checkedwhile its option is;Recipes.RadioGrouptakesvariant: 'card', which frames each card and fills the chosen one. A horizontal
group (aria-orientation="horizontal") wraps. The gallery shows
descriptions, a row, and cards. -
foldkit-mixins-ui: a checkbox group in a row, with an error.
CheckboxGrouptakeserror(the group isaria-invalidand described by
it) andorientation:vertical(default) orhorizontal. The rows sit
in anoptionsslot, spaced on the ladder; a verticallistkeeps them
flush so checked rows stay one block.selectAll: { label, onSet }draws
a first row whose checkbox is checked,mixedor unchecked by
coverage(options, checked)(a disabled option counts for neither) and
controls every option; a press sendsonSetwithsetEnabled(options, checked, on), every enabled option or none, a disabled option keeping the
statecheckedgives it. The checkbox sits on the label's row, so a
description does not pull it down. -
foldkit-mixins-ui: a checkbox check that wipes in. The mark is drawn
clipped away and wipes on over the fast tier, travel-aware, so reduced
motion shows it at once. The mixed dash clears the check's box, borders
and turn. The gallery shows basic, indeterminate and a card checkbox. -
An overlay scrollbar, after Fluid Functionalism's.
ScrollBarsin
foldkit-primitives/dom;ScrollArea.overlayBars,Command.overlayBars,
VirtualList.overlayBars,Menu.overlayBars,Listbox.overlayBarsand
Combobox.overlayBars, each a mixin list passed beside the style;
Utilities.scrollDividerand
scrollTimelinefor a hairline at a scrolled edge. A browser's bar is
thick, always shown and different on every system; this thumb rests at
4px, widens to 6px under the pointer and fades when idle, its widening
travelling so reduced motion keeps only the fades. A coarse pointer keeps
its native bar. The bar sits beside the scroller, not in it, so the edge
fade does not erase it: ScrollArea, Command and VirtualList always draw
it, a popup whenever its panel has ascroll, and the recipe draws it only
onceScrollBarsmarks it scrollable. It is opt-in, since its Mount sends
no Message for a Scene test to resolve. ScrollArea's markup gains aroot
aroundviewportand a bar per axis: a width set on the area goes on
root. -
foldkit-mixins:Theme.nestsandTheme.nested(). A container that
pads its children spreadsTheme.nests(radius, inset, border?), and any
descendant'sborderRadius: Theme.nested()is concentric with it, without
repeating the container's arithmetic.Theme.nested(outer, inset, border?)
is unchanged. -
foldkit-mixins: asurface.fieldtoken for filled controls. The
filled Input, Textarea and Select readvar(--fk-surface-field)— the
defaultstep, so it tracks thesurface-contrastknob — instead of the
page-adjacentsurface.subtle. -
foldkit-mixins-ui: alabelSlot on Segmented, the option's words,
which hold thetext-boxtrim. -
foldkit-richtext-dom: arrows stop on an image, and a checkbox keeps the
caret. An arrow leaving a block for an atom beside it selects the atom,
where the browser stepped over it, and a press on an adornment no longer
takes the caret away from the text. -
foldkit-richtext-dom: a triple click selects its block's text, and Ctrl+A
in code selects the code. The browser's triple-click selection ran on to the
start of the next block, so typing over it swallowed that block's first line
too. In code, a second Ctrl+A selects the document. -
foldkit-richtext: a paste joins the line it lands in. The first pasted
block joins the text before the caret and the last the text after it, so a
copied word lands inside the sentence instead of splitting it onto a line of
its own; an image or table stands between the halves, and a paste into code
is its text. Breaking:Pasteno longer puts content above a block at its
start or below it at its end as separate blocks. -
foldkit-richtext-dom: a press below the last block writes after it. The
caret goes to the end of a last paragraph or heading; after a table, code or
an image a new paragraph opens, where no caret could get below them. -
foldkit-richtext: Backspace and Delete beside a list, a quote, a table or
code. They reach the nearest line on that side: a list's or a quote's last or
first line joins the caret's, and Delete at the end of a list item reaches the
next item; a table or code is stepped into, and an empty line beside one is
removed. All were refused, so an empty line after a table could not be
removed. Backspace at the start of a heading now makes it a paragraph before a
second press joins it. -
foldkit-richtext-dom: a placement'skeymap, and Mod+Shift+S.editorAt
takes chords beside the built-in ones, and Strikethrough has a chord of its
own. The playground binds Ctrl+U (underline), Ctrl+Shift+H (highlight) and
Ctrl+K (the link editor), and its toolbar names them. -
foldkit-richtext:node(…, { indents }), split fromlineBreaks. A kind
can keep line breaks (Enter breaks the line, Enter on an empty last line
leaves it) without Tab indenting it, as a callout of several lines does in
the playground, where Enter used to start a second callout. Breaking: Tab and
a broken line's indentation now followindents, which the standard
CodeBlockdeclares besidelineBreaks; declare it on a code kind of your own. -
foldkit-richtext-dom: Escape closes the slash menu.Message.DismissedSlash
keepsEditorState.slashDismissed, the slash of the query it closed, so the
menu stays shut while that query is typed on and Enter splits the line; it
ends with the query.menuTextOf(view)is what a menu reads, so a view and
Enter agree.EditorStateandEditorViewgain the field. -
foldkit-richtext: an atom is selected whole. A press on an image or a
divider selects it (outlined throughdata-selected), as does Backspace from
the block after it or Delete from the block before; a second press deletes
it, Enter opens a paragraph after it, typing replaces it, and an arrow leaves
it for the text beside it.isAtom(nodes, block)is the test. Deleting a
block now lands the caret where the block was even when it held no text,
where it went to the top of the document. -
foldkit-mixins-ui:Carousel, the APG carousel. A scroll-snap track
of slides named "N of M", previous and next buttons, dots and a polite
status. Its slide and rotation are a Model (Carousel.init({ id, slide?, autoplay?, delay? })) folded withCarousel.update({ count, perView?, loop? }), which returnsCarousel.ShowSlide, the Command that scrolls the
track, smoothly only without reduced motion; a swipe comes back through
ScrollSettle, so the dots follow the finger. Withautoplayit moves on
everydelay(4s), pauses under the pointer, stops when focus arrives by
the keys, quiets its status while it rotates and draws the APG's rotation
button first; the current dot is Fluid's pill, filling over the delay.
thumbnailsstand in for the dots, andperViewshows several slides at
once, a dot per place the track can rest. Recipe axispeek.
Recipes.Carousel,Patterns.Carouseland a gallery page. -
foldkit-mixins-ui:TreeView, the APG tree view. Nested rows with
one tab stop: the arrows move through the rows showing and open, shut,
enter or leave a row; Home and End; a character moves to the next row it
starts; Enter, Space or a click chooses a row. Its Model (current,
open,selected,checked) is folded withTreeView.update(nodes),
which decides each key from its own Model, so keys faster than a frame
still move on. It reusesTreeNavigation'sshown,moveandtabStop
andTypeahead.match. Each item carries its depth as--fk-tree-level.
A node takes aniconand anopenIcon;Recipes.TreeView({ guides: 'lines' })draws a guide down each open branch;checkable(on the view
andupdate) gives every row a tri-state box, the Model'schecked
holding the leaves and a branch reading checked, mixed or not from them
(TreeView.checksOf), Space checking inupdate.Recipes.TreeView,
Patterns.TreeViewand a gallery page. -
foldkit-mixins-ui:ColorPicker, after ReUI's: a saturation and
brightness area (the pointer and the keys), hue and alpha Sliders painted
from the color (Recipes.Slider({ track: 'paint', label: 'hidden' })), the
browser'sEyeDropperwhere there is one (ColorPicker.Eyedrop,
canEyedrop), text in hex, rgb, hsl or oklch that parses to anOption
(format,parse, andcolor('#3b82f6')for a literal in code), and
presets as a radio group of swatches. The color, its format and a draft
being typed areColorPicker.Model(init({ id, color?, format? })),
folded withColorPicker.update, so fast keys each step from the last
color;toOklchreads a color for a theme.ColorPicker.popoverdraws it
as a Popover'stoView.Patterns.ColorPickerand a gallery page. -
foldkit-mixins-ui:Recipes.Sheet, the Dialog from an edge.
shadcn's Sheet and vaul's Drawer as one recipe overDialogSlots: the
panel stands on the edge itsdata-sidenames (top,right,bottom,
left, written bySheet.viewfromSheet.init({ side })) and slides in
from there, the fade kept under reduced motion.handle: 'bar', the
default, shows the handle, a Slot (handleonDialogSlots,aria-hidden,
drawn ash.div(dialog.handle)in the panel), and rounds the inner
corners: from the top or bottom it is the panel's last or first row, and
from the left or right a bar standing on the edge facing the page, so a
bottom sheet with its handle is a drawer as on a phone. One gallery
page. -
foldkit-mixins-ui:InputCopy, a value with a copy button. A
read-only field and a button named after its label;InputCopy.Copyis
the Command that writes the clipboard and reportsCopiedorCopyFailed
asfoldkit-primitives/dom'sClipboardMessage. Thecopiedflag is the
application's; copied, the button reads "Copied" and a status region
announces it.Recipes.InputCopy,Patterns.InputCopyand a gallery page. -
foldkit-mixins-ui:TagsInput, Zag's tags input: the tags as a
list of chips named by the field's label, each with aRemove <tag>
button, and a text input. Enter or the delimiter adds the draft, split at
the delimiter and trimmed, refusing empty and repeated parts; Backspace on
an empty draft removes the last tag; a removal sends focus back to the
input. The tags and the draft are its own small Model (init,Message,
update), and the keys reachupdate, so it decides from the current
draft, not the last frame's. A recipe, a pattern and a gallery page. -
foldkit-mixins-ui:PasswordInput, a password field whose toggle
(aria-pressed,aria-controls) shows it as text,autocompletekept as
current-passwordornew-password.Recipes.PasswordInputreuses
Input's pieces in InputGroup's box, draws the eye withIcons.glyphand
hides the browser's own reveal button.Patterns.PasswordInputand a
gallery page. -
foldkit-mixins-ui:Rating, a row of stars as the APG's radio group
of native radios: the application keepsvalue(anOption, none before
anyone rates) and receivesonChangewith the star chosen. The arrows move
the choice natively,readOnlydraws oneimgthat names the rating, and
disabledstills it. The recipe cuts each star withclip-pathon the
radio's::before, keeping its focus ring whole, and previews the rating
under the pointer in CSS alone.Patterns.Ratingand a gallery page. -
foldkit-mixins-ui:Steps, a stepper. A named ordered list whose
step atcurrentisaria-current="step", those before it complete (a
check in the marker) and those after upcoming, asdata-state, joined by
a connector. The application ownscurrent; givenonSelect, completed
steps are buttons that go back. Recipe axisorientation.Recipes.Steps,
Patterns.Stepsand a gallery page. -
foldkit-mixins-ui:VirtualList,@foldkit/ui's virtual list
adapted. It draws only the rows in view; its Model stays upstream's,
folded with that module'supdate. Therootslot is the box it scrolls
within androweach row's content; the box fills its container's width
and, where the container takes its width from the box, offers 20rem, so
the rows drawn as it scrolls never resize it.Recipes.VirtualList,
Patterns.VirtualListand a gallery page of ten thousand rows. -
foldkit-mixins-ui:Meter,@foldkit/ui's Meter adapted. A
measurement within a range, its root'sdata-level(good,fair,
poor) the HTML meter's judgment of the value againstlow,highand
optimum(Meter.levelOf), which the recipe colours success, warning or
error.Recipes.Meter,Patterns.Meterand a gallery page. -
foldkit-mixins-ui:Progress,@foldkit/ui's Progress adapted,
with Circular Progress as its recipe'sshape: 'ring'.valueis an
Option, none for work whose end is not known, which the bar shows
sliding and the ring turning, still under reduced motion. The root carries
the share done as--fk-progress.Recipes.Progress,Patterns.Progress
and a gallery page. -
foldkit-mixins-ui:Menubar, a row of@foldkit/uiMenus. Its
Model holds the menus and the bar's one tab stop, folded with
Menubar.update, whoseSelectedOutMessage names the menu and the row.
The arrows move the stop while the menus are shut and open the next menu
while one is open; pointing at another trigger while one is open opens it,
and opening any menu shuts the rest without sending focus back to its
button.Recipes.Menubar,Recipes.MenubarMenu,Patterns.Menubarand a
gallery page. -
foldkit-mixins-ui:Nav,@foldkit/ui's Nav adapted, and with it
the navigation menu:Nav.toViewdraws the landmark's links in a list,
the current onearia-current="page", andNav.fluidglides a line to
the current link and a faint pill to the one the pointer nears, as Tabs'
do. The keys stay the browser's (no roving tab stop for a landmark).
Recipes.Nav,Patterns.Navand a gallery page. shadcn's dropdown
panels in a navigation menu are not here yet. -
foldkit-mixins-ui:Sidebar, from the widgets island into the
package: shadcn's sidebar with Fluid Functionalism's collapse. A named
navigation landmark with a header and a footer, groups with or without a
heading that open on the accordion's grid row (several at once), rows with
icons and counts, branches of nested links, the current page marked
aria-currentwith a marker that glides to it (Sidebar.fluid), and a
toggle that collapses it off the page (collapse: 'offcanvas') or to a
rail of icons named in Tooltips ('icons'); on a phone an opener shows it
in a Sheet. Its Model (Sidebar.init({ id, rows, open?, collapsed? }),
Sidebar.update) holds what is open and collapsed, the rail's Tooltips
and the Sheet.width: 'fill'sizes it to a Resizable's panel. A recipe,
a pattern and a gallery page. -
foldkit-mixins-ui:Resizable, from the widgets island into the
package: shadcn's Resizable and the APG's window splitter, two panels with
a handle that is aseparatorwith its value, stepped by the arrows and
sent to the bounds by Home and End, and dragged, following theMove
Mount from where the drag began over the group theResizeMount
measured. The split is a small Model of its own (init,Message,
update) the application keeps; side by side or one over the other. A
recipe, a pattern and a gallery page. -
foldkit-mixins-ui:Command, the widgets island's command palette as
shadcn's Command with Fluid Functionalism's CommandMenu behaviour: a field
that points at the highlighted row througharia-activedescendant, ↑ and
↓ wrapping through enabled rows, Enter running the highlighted one, rows
in groups with shortcuts, and an empty state. The filter (filtered)
matches every query word against a row's label and keywords and never
re-sorts; a highlight filtered away moves to the first enabled row. The
query and highlight are the application's. A shortcut reads left to right
on a right-to-left page. A recipe, a pattern and a page. -
foldkit-mixins-ui:Recipes.HoverCard, the widgets island's hover
card as@foldkit/ui's HoverIntent with a look: a trigger read as a link
and a raised panel that fades in as it is drawn (@starting-style),
placed against its trigger in the top layer byHoverIntent.toView's
anchored. The delays stay HoverIntent's. A gallery
page draws it. -
foldkit-mixins-ui:OtpField, Input OTP, after shadcn's input-otp
rather than the widgets island's cell per input: one real<input>over
cells that only mirror it, so paste, Backspace, the caret and a phone's
one-time-codeautofill are the browser's and no Command moves focus.
The code is the application's, kept to its pattern and length
(OtpField.kept);groupsdraws runs with a separator, and the next cell
draws a blinking caret while the field has focus. A recipe, a pattern and
a gallery page. -
foldkit-mixins-ui:NumberField, from the widgets island into the
package: the APG's editable spinbutton and Zag's number input. The arrows,
PageUp and PageDown, Home and End step it throughSpinValue'sspin, a
typed number commits on change (text that is no number leaves the value),
and steppers out of the tab order disable at their bound; every value is
clamped onto its step (NumberField.settled), a decimal step kept exact.
The value is the application's.Recipes.NumberField,
Patterns.NumberFieldand a gallery page. -
foldkit-mixins-ui:CheckboxGroup, from the widgets island into the
package, as Zag's checkbox group with Fluid Functionalism's rows: a
fieldsetnamed by itslegend, each option the APG's checkbox named by a
labelfor it, the checked values the application's (toggledis the
transition). Drawn as alist, checked rows fill and merge where they
stand together.Recipes.CheckboxGroupreuses the Checkbox recipe's
pieces;Patterns.CheckboxGroupand a gallery page. Accordion, Segmented
and CheckboxGroup now share one step for a multiple choice. -
foldkit-mixins-ui: an alert dialog is the Dialog with
role: 'alertdialog'.Dialog.toView(mixins, { h, role: 'alertdialog' }, draw)(andDialog.resolve) putsrole="alertdialog"on thedialog
bundle;'dialog', the default, writes none, as the<dialog>element has
it.Dialog.isDismissal(message)names upstream's own dismissal (Escape, a
press on the backdrop, its close button): an application that drops those
inupdate, draws no close button, and closes from its own buttons with
@foldkit/ui'sclosehas the APG's alert dialog. One recipe and one
gallery page, Dialog, show both. -
foldkit-mixins-ui: Toggle and Toggle Group, from the widgets islands,
as what was already here. A toggle is a Button withpressed, which
writesaria-pressed; every variant has its on look. A toggle group is a
segmented control:Segmented.viewdraws the options from the
application's pressed values, andSegmented.toggled(pressed, value, { multiple, optional })is the transition, one at a time (kept pressed
unlessoptional) or several. The gallery's segmented control is drawn
withSegmented.view. -
foldkit-mixins-ui:Accordion, from theexamples/widgetsisland into
the package, after the APG pattern, shadcn's and Zag's options and Fluid
Functionalism's craft.Accordion.viewdraws the application's open ids,
each trigger a button in a heading naming its region;Accordion.toggled
is the transition, single ormultiple,collapsibleby default. A panel
opens on a0fr-1frgrid row (no measured height), clipped on its
container, a shut one stays drawn andinert, the chevron turns 90°, and
the open title turns semibold in its own width.Recipes.Accordion,
Patterns.Accordionand a gallery page. -
foldkit-mixins-ui: a table under fluid hover.Table.view({ …, fluid: true })stands the table in aframewith onehighlightbehind the body
rows, following the pointer to the nearest one, after Fluid
Functionalism's: the lines above and below the lit row clear over 80ms, the
header's too when the first row is lit, and the lit row's text lifts.
FluidHovermarks the item it is shown ondata-fluid-litfor this. -
foldkit-mixins-ui: merged choices. In a multiple listbox or
combobox, chosen rows next to each other square the corners between them,
so a run reads as one block. -
foldkit-mixins-ui: popups and dialogs enter and leave. Drawn with
@foldkit/ui'sisAnimated, a menu, listbox or combobox panel fades in
on the fast tier from a little smaller, sliding 4px from the side the
anchor placed it on, and a dialog and its veil on the slow tier from 0.97;
each leaves on its tier's quicker exit, and reduced motion keeps the fade
alone. The Menu component passes it withMenu.init(id, rows, { isAnimated: true }). The gallery's widgets animate. -
foldkit-mixins/utilities:steady(weight), weight without reflow.
The element'sdata-labelholds its text, which a hidden, zero-height
::aftersets atweight, so a label that turns heavier when chosen
moves nothing beside it; the element stays a block, so atext-boxtrim
still works on it. A pressed segmented option and a chosen card title use
it, and the card writes its title intodata-labelitself. -
foldkit-mixins/theme: a surface ladder that nests by itself.
Theme.raise(offset)sets an elementoffsetlevels above the raised
element holding it, or the page (capped at 8), andTheme.raisedSurface()
paints it for its level, after Fluid Functionalism's ladder: in a light
scheme two steps toward white and then flat, so shadow carries elevation,
and in a dark scheme an even step lighter per level (knob.raise-dark).
Popups, popovers, date picker panels and toasts raise by 2 and dialogs by
4, so a popover opened in a dialog lands above it instead of melting into
a surface it shares. A popup keeps its menu look (popup.inverted, new,
and the translucency) over its level. -
foldkit-mixins/utilities:scrollFade(axis), after shadcn's and Fluid
Functionalism'sscroll-fade: a scroller's content dissolves toward an edge
with more to scroll, its true start and end crisp until something is past
them, by a mask a scroll timeline moves; no JavaScript, and no fade where
scroll timelines are missing. ScrollArea and the popups' lists use it. -
foldkit-primitives/dom:FluidHover'sgapReach, the farthest a
click in a gap may land from the highlighted item and still press it;
Card.groupuses 16px, after Fluid Functionalism's card grid. An item with
no layout box yet keeps the highlight's last place and is measured again
for three frames, rather than drawing the highlight at zero. -
foldkit-richtext:InsertBlockandDeleteBlock.InsertBlockputs a
block beside another under fresh identities (without a place, beside the
caret's block);DeleteBlockdeletes one with any container it empties and
moves a caret inside it to the nearest text that stays. The editor sends them
asInsertedBlockandDeletedBlock, andSetPropsasChangedProps. -
foldkit-richtext: a rendering entry'sadornments, elements drawn
before a block's content that are not content (a checkbox, a figure's picture
and caption, an icon). They hold no position and an HTML import drops them; a
click on one withtogglesflips that boolean prop through the document. -
foldkit-richtext: the standardImagetakescaption,widthand
align, all optional; the standard rendering still draws theimgalone. -
foldkit-richtext: deleting a selection across kinds of block. Select
all and Delete did nothing, and so did any range over an image, a table, a
list or a quote: the command refused a range it could not join. Such a range
is now cleared (blocks wholly inside go, the ends are trimmed and joined where
they can be, emptied containers go), and typing, Enter or pasting over it
clears it first. A range that leaves a table cell only loses its text. -
foldkit-richtext-dom:blockAnchor'sside: 'inside-end'. A block's
toolbar inside its top right corner, covering nothing around the block. -
foldkit-mixins-richtext:linkEdit, and Enter that keeps up with typing.
The link editor's Enter sent the edit its last drawing decided, a frame
behind fast keys, so Enter straight after typing applied the address before
the last character, or nothing. Its new optionalsubmittedis sent instead,
forupdateto applylinkEdit(document, selection, draft)to the Model's
own draft. -
foldkit-mixins-richtext: the link editor asks for a link and closes on
Escape. Its field says "Paste or type a link", Escape in it sends the new
optionalclosed, and its button reads Apply rather than Link. -
foldkit-mixins-richtext: the find bar's Previous, Next, Match case and
Close are icons, named byaria-labelandtitle. -
foldkit-richtext-dom: a floating element follows its own size. A
toolbar measured before its styles settled was placed below the selection
for good, covering the next line;selectionAnchorandblockAnchornow
place it again when its size changes, as a slash menu's does while a query
narrows it. -
foldkit-mixins-richtext:blockStyleMenu. The block styles as one
trigger naming the current style and a menu of them, the current one
checked, for a toolbar with other things to hold. -
A slash menu worth reading, and an icon set.
foldkit-richtext-dom's
SlashEntrygainsdescription,groupandicon, and the editor's entries
have all three;matchingEntriesgroups the catalogue under/and ranks a
query's matches, best first.foldkit-mixins-richtext'sslashMenuViewdraws
group headings, each entry's icon, label and description, and "No matches",
through new slots, andrichTextIcon/richTextIconUrlare the chrome's icons
(after Lucide, ISC). -
foldkit-richtext: video, audio and attachments.mediaNodes
(Video,Audio,Attachment) are opt-in vocabulary,mediaRendering
draws them with the browser's controls and a download link, and
mediaBlock(file)makes the block a file's type calls for. Adata:source
is accepted only of the kind's own family, and an attachment's never as HTML,
SVG or XML. -
foldkit-richtext-markdown: footnotes.parse(markdown, { footnotes: true })
reads[^label]and[^label]: …asFootnoteRefandFootnoteDefinition,
andprintwrites them back; without the option they are reported and
skipped, as before. -
foldkit-richtext: typing at a block's edge leaves a mark that does not
extend. Text typed after a link, inline code or footnote reference that ends
a paragraph (or before one that starts it) used to join it, since there was
no neighbouring run to move to; it now gets a run with only the marks that
extend.resolveInsertionalso reaches nested blocks, where it found nothing
before, so a list item or table cell kept extending such marks. -
Footnotes.
foldkit-richtextdeclaresFootnoteRefand
FootnoteDefinitionfor a kit to opt into, reads them withfootnotesOf,
footnoteAtandnextFootnoteLabel, and inserts and renames them with
InsertFootnoteandRenameFootnote(the editor'sInsertedFootnote,
RenamedFootnote);foldkit-mixins-richtext'sfootnotePreviewis the card
at a reference. -
foldkit-richtext-dom: a hint on the empty line.lineHint("Type /
for blocks") marks the empty paragraph the caret is in withdata-hint, for
a stylesheet to draw as it draws the placeholder. Changed: the host
registry'splacePlaceholder/placeholderForareplacePlaceholdersand
placeholdersFor, holding both strings. -
A table of contents.
foldkit-richtext'soutlineOflists a document's
headings andsectionAtthe section a selection is in;
foldkit-mixins-richtext'stableOfContentsdraws them; and
foldkit-richtext-dom'sfocus(hostId, selection)Command sends the reader
into the text with that selection, scrolled into view. -
Find and replace.
foldkit-richtext'sfindMatcheslists a query's
matches (case not counting unless asked),searchDecorationstakes a
currentmatch drawn assearch-current, andReplacereplaces one match or
every one as a single undo step; the editor sends it asReplacedText, and
foldkit-mixins-richtext'sfindReplaceis the bar. Changed: search
ignores case by default; passcaseSensitive: truefor the old exact match. -
foldkit-richtext-markdown: inline markers close as they are typed.
**bold**,*italic*,_italic_,`code`and~~struck~~become
marked text when the closing marker is typed, and typing on is plain
(markdownInlineRules, part ofmarkdownInputRules). An input rule can say
what the next text carries (InputMatch.storedMarks, read by
resolveInputRules), which the editor keeps. -
foldkit-mixins-richtext:tableToolbarandcodeToolbar. The table
toolbar adds and deletes rows and columns around the caret's cell, switches
the header row and deletes the table; the code toolbar picks a code block's
language and sends the caller's Copy Message. Both draw through slots. -
foldkit-richtext-dom: Tab, table Messages, adornment actions, a toolbar's
place. Tab and Shift+Tab sendIndentedandOutdentedwhere
RichText.takesTabsays they mean something, and move focus elsewhere;
InsertedRow,InsertedColumn,DeletedRowandDeletedColumncarry the
table commands. An adornment with anactionis reported as
ActivatedAdornment, which edits nothing.blockAnchor({ side: 'above' })
places a block's toolbar. -
foldkit-richtext: tables as a grid, and Tab.InsertRow,InsertColumn,
DeleteRowandDeleteColumnedit the table around the caret's cell, each as
one undo step (tableAtreads where the caret is).IndentandOutdentare
Tab and Shift+Tab: indentation in code (and Enter keeps a line's), the next or
previous cell in a table, a new row after the last, and in a list item, nesting
it under the item before it or moving it back out.takesTabsays where Tab
means something. -
foldkit-richtext-dom: an empty line can be reached. It holds a
<br data-richtext-filler>, where the caret stands, so it has a line's height
and the arrow keys and a click land on it; before, a new line made with Enter
had no height and the caret could not get to it. A caret restored onto an
element, not only into text, is put back. -
foldkit-richtext-dom: a drag shows itself and scrolls the page. The
dragged block carriesdata-richtext-draggingonce the pointer moves (and the
page's rootdata-richtext-drag, for agrabbingcursor), and a
pointer held near the window's top or bottom scrolls the page, so a block can
be dragged past what the window shows. -
foldkit-richtext:node(…, { lineBreaks: true }), a kind whose text
keeps line breaks: Enter inserts a newline instead of splitting the block,
and Enter on an empty last line leaves it for a paragraph after. The
standardCodeBlockdeclares it, so Enter in code no longer makes a second
code block. -
foldkit-richtext:ClearMarktakes a mark value, clearing only the
runs carrying exactly it, so one discussion's highlight goes without
another's inside it; the editor'sClearedMarkcarries either.eachBlock
andsiblingsOfare exported, for an application walking its document. -
foldkit-richtext-dom:selectionAnchor({ caret: true })follows a
caret too, opening under it, which is where a slash menu goes. -
foldkit-richtext-dom:editorAt(hostId, { slashEntries }), the slash
catalogue Enter in a query resolves against, so an application's own entries
(an image, a table) choose as the editor's do. -
foldkit-remote-server:RemoteServer.memorytakesentities, Sources
for entities no row backs (foldkit-guard'sGuardServer.me), served
beside the rows. One naming an entity the rows' domain declares, or a second
for the same entity, is refused when the backend is made. -
foldkit-primitives/dom:FluidHover, a Mount that moves one highlight
across a container's items: the item nearest the pointer, gaps included, on
thex,yorxyaxis, skipping disabled items and following keyboard
focus. It writes the item's place as custom properties and sends no Message;
withgapClick, a click in a gap presses the highlighted item.follows
lights the item a menu's keys marked, andfromnames the item a fresh
highlight grows out of and returns to. -
foldkit-mixins/theme:Theme.oklch({ neutral: { h } }), a hue for the
neutrals (surfaces, text, outlines, the shadow) apart from the accent's, as
shadcn's base colors are; without it they take the accent's and follow its
knob, as before. The hue is theneutral-hknob. -
foldkit-mixins/theme: a menu's look,Theme.oklch({ menu: { accent, color } }),
as shadcn's menu options: the lit row in a tint of the text or the
accent's fill (bold), and the panel in the page's surface, the other
scheme's (inverted), or either over a blur (translucent). Thepopup
tokens carry it, and the menu, listbox and combobox recipes read them. -
foldkit-mixins/theme:motion.settleandmotion.spring, easings
sampled from springs, for CSS transitions that spring with no JavaScript:
settlecritically damped, landing exactly, andspringat a damping ratio
of 0.88 for large surfaces. -
foldkit-mixins-ui:Card.groupandRecipes.CardGroup, cards as one
keyed list in one column or a grid, divided or apart, with one highlight that
follows the pointer to the nearest card (FluidHover); a press in a gap
presses the highlighted card. -
foldkit-mixins-ui:Recipes.Calendar, the month grid in every mode,
sizedsmormdon the size ladder, for a calendar alone or in a date
picker's panel. The design-system example uses it in place of its own. -
foldkit-mixins-ui:Menu,ListboxandComboboxdraw themselves.
TheirtoViewtakesdrawoptionally; without it, upstream's markup is
drawn from the styled bundles, so a styled menu is
Menu.toView([MenuStyle.mixin], { h })instead of forty lines of panel and
option groups. The forks' default views share the same drawing. -
foldkit-mixins-ui:Calendardraws itself too.CalendarSlotsgains
header, the row of the heading and its paging buttons, which no upstream
bundle holds;Calendar.toView(selection, mixins, { h })draws every mode
through the slots, andCalendar.drawis that drawing for atoViewof
your own.
DatePicker.toView'sdrawis optional the same way. -
foldkit-mixins-ui: tabs glide.TabsSlotsgainsindicatorand
highlight, andTabs.fluidmeasures the chosen tab and moves one hover
to the tab nearest the pointer, born at the chosen tab; drawn, the recipe's
rule or pill springs to a new choice. Until it runs, each tab marks itself.
FluidHoversetsdata-fluid-hoveron its container while it runs, which
a recipe reads to draw a highlight in place of each item's own hover. -
foldkit-mixins-ui: menus, listboxes and comboboxes glide.Menu.fluid,
Listbox.fluidandCombobox.fluidmove one highlight over the open
panel's rows, for the pointer and the keys alike, and a listbox's or
combobox's chosen-row marker glides to a new choice. Their slots gain
rows,highlightand (listbox, combobox)indicator, which the default
draw places.Segmented.fluiddoes the same for a segmented control's
pressed pill, through its newindicatorandhighlightslots. -
foldkit-mixins-ui:Item'scontentis optional, for a row its
title and description say all of, such as a notification. -
foldkit-mixins-ui:ChatMessageandMessageScroller. A chat
message with an author, avatar, time, a sending/sent/failed status with a
retry and named actions, the user's at the end in a bubble;
ChatMessage.groupdraws a sender's following messages as continued. Its
body wraps withtext-wrap: wrap, so a streamed answer never moves a word
off an earlier line, and a streaming message isaria-busy. Recipe axes
layout(chat,thread),bubbleandcontent(text,markdown,
spaced as Prose).MessageScrolleris arole="log"in aScrollArea
that follows the newest message while the reader is at the end, stops when
they scroll up, offers a jump button counting what arrived (Appended),
and holds the message in view as older ones load above; itsFollowEnd
Mount is exported for Scene tests. -
foldkit-mixins-ui:ThinkingIndicator,ThinkingStepsand
Questionnaire. The indicator is an assistant at work: dots, a pulse or
the view's icon beside words a band of light sweeps, and a detail line, in
a polite status drawn always (active: falsedraws it empty); recipe axes
mark,label,tone. Thinking Steps is a trace of reasoning and tool
calls under a button saying the running step or "Thought for 4s", each step
pending, running, done or failed, in words too, with a duration, detail
and steps of its own (onToggleStep); its openness is anOptionthe
application keeps, none following the work.ThinkingSteps.stateOf,
shown,elapsed; axesvariant,connector. The Questionnaire asks
single choice, multiple choice and free text questions, with an "Other"
field, all at once or one at a time;updatesays what is missing and
focuses it, and returnsOutMessage.Answeredonce all are answered.
Questionis a Schema, so an assistant's tool input decodes into it; axes
variant,actions. -
foldkit-mixins-ui:AngleSlider,TimerandMarquee, after Zag's.
A dial in degrees dragged round or stepped by the APG's slider keys, which
go round past 0, withstep,largeStep, labelledmarks,
restrictToMarks, read-only, invalid and a hidden input (recipetone,
track,value,size). A countdown or stopwatch started, paused,
resumed, reset and restarted, its clock read by Commands on Effect's
Clockso TestClock drives a run, arole="timer"with each unit in a
Slot;Timer.phase,Timer.partsOf(recipetone,look,progress,
size). Content that scrolls on without a seam: measured copies filling
the viewport, a speed in pixels a second, four sides, a delay and a round
count, held by a pointer, by focus or by its pause press (WCAG 2.2.2), its
copiesinert, and still under reduced motion (recipespacing,
edges). -
foldkit-mixins-ui:ImageCropperandSignaturePad, after Zag's. A
crop of an image that a drag moves and eight handles resize, zoomed by a
Slider, the wheel and+/-with the selection kept on screen, panned,
turned by quarters, locked to an aspect or round; the crop is the Model's
in the turned image's pixels (ImageCropper.crop), andRequestedExport
returns theExportCommand answeringExported({ url, width, height })
orFailedExport(recipeguides,handles,viewport). Strokes drawn
by one pointer, a pen's pressure used and a mouse's simulated from speed,
kept as data and drawn as smoothed variable-width outlines (pathOf),
with undo, clear, "Sign here" and a typed name instead;toSvgis pure
andRequestedPngreturnsExportPng(recipesurface,size). -
foldkit-mixins: achartgroup inTheme.oklch.chart-1to
chart-5, as shadcn names them: the dataviz reference palette's first five
slots in its order, each a fixed turn from thechart-hknob at a
lightness and chroma set per hue and per scheme. -
foldkit-mixins-ui:QrCodeandChart.QrCode.viewdraws text as
one SVG path in a quiet zone, one image named by what it encodes, with an
overlay (a logo) that raises the level from M to H;QrCode.encodeand
pathOfare pure,QrCode.Downloadsaves the drawn code as PNG or SVG
in its colors, and the recipe draws it dark on light on any page
(scheme,size). Encoded byuqr, now a dependency.Chart.viewdraws
bars, a line, an area or a donut with round ticks, gridlines, a legend and
a tooltip, in percentages, so it fills any width with nothing measured and
renders the same on a server;Chart.Modelholds the lit point, the
arrows, Home and End step through the points while a status says each,
and the data is a table, hidden unlesstable: 'shown'. Series take
chart-1tochart-5; recipe axesheight,grid,indicator. -
foldkit-mixins-ui:CascadeSelect,FloatingPanelandTour. One
value chosen as a path through nested options (CascadeSelect.update({ nodes })) from a top-layer APG tree with a column per level or one level
at a time (layout: 'drill'), the arrows within and between levels,
Home, End and a letter,expandOn: 'hover',parents,clearable,
levelsand a hidden input (recipesize,trigger,width). A
non-modal window whose rect and stage are its Model: dragged, resized
from eight edges within the viewport,min,maxand agrid, moved and
resized by the arrows, minimized, maximized, restored and closed, stacked
by anorderitsRaisedOutMessage updates (FloatingPanel.raise;
recipeheader). A tour of steps beside their targets in the top layer,
over a dim with a cutout the target's presses reach, the target scrolled
into view, Next, Back, the arrows, Skip and Escape, focus back to the
opener andEnded { completed }at the end; a step without a target, or
with a missing one, stands in the middle. -
foldkit-mixins-ui:Anchor.layerBox, the top-layer boxlayer
draws, without itsTopLayerMount, for a box whose own Mount shows it
throughtopLayer. -
foldkit-primitives/dom:PointerPosition({ focus? }), a Mount
reporting one pointer's press, moves (with coalesced points and pressure)
and release in the element's padding box, captured, with move listeners
only during a press;paddingBoxOf({ rect, border })and
measurePaddingBox(element). Infoldkit-mixins-uithe color picker's
area, the angle slider's dial and the signature pad read the pointer
through it (no API change). -
Site: pages for every component above.
Changed
-
foldkit-mixins-ui: the Calendar's resolved days and a Segmented tray
(breaking).ResolvedDays' paging buttons areOptions and its heading
is acaption(Grid,LabelorDropdowns);toView,inputsand
resolvetake an options object fourth,renderamong them. A Segmented
trayis as wide as its options, not its container (width: 'fill'for
the old strip). A tab is a flex row: draw its words in thelabelslot to
keep their trim. -
foldkit-mixins-ui: the Tooltip takes its id; the Toast recipe drops its
accent edge (breaking).Tooltip.toViewandTooltip.resolvetake the
Tooltip'sid, which the arrow points from. -
foldkit-mixins-ui: Select's chevron and the display slots (breaking
for hand-drawn slots). The native Select draws its own chevron and
mutes a chosen placeholder.AvatarSlotsgainsstatus, and the
avatar's root is onerole="img"named by the person, its picture's alt
empty.SeparatorSlotsgainsrootandlabel, the line a border in
outline.default.SkeletonSlotsgainsgroup, a bar a film of the
text.SpinnerSlotsgainspart,rootandtext, a turn about 720ms
timestravel.ItemSlotsgainsoverlay. Empty's title takes the
subtitlerole and its icon isaria-hidden. -
foldkit-mixins,foldkit-mixins-ui: hovers take their surface’s paint,
not grey.unfilledHoverfilms the state ink instead of the text: the
theme's--fk-state-ink(state.ink), unless a surface painted in a hue
writes its paint in the local--_fk-state-ink, so a hover on a painted
surface deepens the paint. The accent and danger tones name their fills, a
subtle alert band its ink (its tint would film the band over itself) and a
bold one its fill; the neutral and ink tones name the theme's ink back, so
a neutral control on a band keeps the theme's hover. New
OklchKnobs.stateHuesets the theme's ink, and one override reaches every
component, neutral ghost and outline buttons, radios, sliders and toggles
included:neutral(today: the text, with painted surfaces naming the text
too through the derivedsurface-inkknob, which a scoped override also
switches at runtime),surface(the default: the text, and surfaces name
their paint),accent(the accent's ink, so hovers lean on the accent). A
var()substitutes where the token is declared, so the state tokens
themselves stay text-based; the ink travels as its own property, read where
the film is drawn. -
foldkit-mixins-ui: in-place menu, listbox and combobox panels draw in the
top layer.Anchor.topLayerwraps an upstream anchor Mount when its config
saysportal: false: it shows the panel as a manual popover around the
Mount’s own acquire and hides it once that Mount releases, keeping the
Mount’s name, args and Messages, and does nothing on an element without the
popoverattribute or where the popover API is missing. Those panels carry
popover="manual"andinset: auto(the anchor writes onlyleftand
top, and the browser'sinset: 0opened them at the viewport's far edge
right to left); no stacking context or ancestoroverflowreaches them,
and the anchor positions them as before. The Menu's submenus and the
Menubar's menus are drawn this way too. An unstyled panel keeps the
browser's popover border, padding andCanvasfill, which every recipe
replaces. Panels that relocate through an upstream portal keep upstream’s
behavior, since moving a shown popover hides it again. The popup recipes
take awidthaxis:triggerreplaces the panel's 14rem minimum with its
trigger's width (min-inline-size: var(--button-width, 14rem)), the
default for Combobox, andautokeeps it, Listbox's default. Popover,
Tooltip, Hover Card and DatePicker panels, modal<dialog>and arrows stay
where upstream draws them until upstream owns top-layer mounting (their
Mounts attach inside upstream views). -
foldkit-mixins-ui: aMenucomponent, by a button, at a point, or
down a submenu, over the Menu adapter.Menu.init(id, rows, { isAnimated?, isModal? })(a row withitemshosts a submenu, one level
deep;isAnimatedmarks the menu's and its submenus' panels for the
recipe's fade), folded withMenu.update, drawn withMenu.view({ model, rows, buttonContent, onMessage, opensAt?, direction?, isRowDisabled?, mixins, submenuMixins? }, h), the style one of itsmixins.opensAt: 'point'withMenu.target(onMessage, h)on the area is the context menu:
a right press records its point and thecontextmenuafter it opens the
menu there.Selectednames the value and the submenu it came from.
Menu.fluidglides the highlight;Menu.overlayBarsdraws the overlay
bar. The panel adapter (Menu.toView,Menu.resolve) stays for a
@foldkit/uiMenu the application folds itself, for typed items or
groups, andMenu.drawAtPoint(point, h)as itsdrawwith
Menu.pointTarget({ onPress, onOpen }, h)on the area opens one at a
point (give its anchorgap: 0).MenuSlotsgainslabel,suband
chevron, andscrollBarandscrollThumbdrawn while a panel has a
scroll. One recipe, one pattern, one gallery page. -
foldkit-mixins-ui: one date system:Calendar.Selection.
Single,RangeorMultiple, owned by the application
(Calendar.empty(mode); fold eachSelectedDatethrough
Calendar.picked, which orders a range's ends;Calendar.isComplete
says when a picker may close). Draw inline by spreading
Calendar.inputs(selection, mixins, { h })into upstream's view inputs:
itstoView(Calendar.toView(selection, mixins, { h })) marks each
chosen day (data-range-start,data-range-end,data-in-rangeon a
range's band), the grid of a Range or Multiple selection is
aria-multiselectable, and itsmaybeSelectedDateagrees with the marks.
In a popover,DatePicker.update(selection)keeps the panel open while
the day leaves the selection unfinished, andDatePickerView.viewtakes
selectionandtriggerContent(selection)in place of
maybeSelectedDate; undernameeach chosen day is a hidden input of its
own, soFormData.getAll(name)reads a range's[start, end]. The
gallery's Calendar and Date picker pages show one day and a range.
Breaking:Calendar.toViewtakes the selection first. -
foldkit-mixins-ui: oneSlider, for a number or a{ low, high }
range.Slider.view({ id, label, value, onChange, … }, h)takes either
kind, andonChangeis handed the same kind. Both are native
input type="range"thumbs, so the drag, the keys, right to left and the
form value are the browser's: one number is a thumb across the whole
track, named by the label; a range is two thumbs in a group, each bounded
by the other (Slider.movedsnaps, bounds and orders a range, as the APG's
multi-thumb slider).marksdot values on the track (data-filledwhere
the fill covers them) andshowValuedraws each value, both hidden from
assistive technology.Recipes.Sliderserves both, in Fluid
Functionalism's look:track: 'thick'(the default, the fill reaching
each thumb's far edge) or'thin', andvalue: 'above'or'inside'(a
pill thumb).Patterns.Slideris the pattern. Breaking:@foldkit/ui's
Slider is no longer adapted (Slider.resolve,Slider.toViewand
ResolvedSliderare gone, andSliderSlotsis the new view's, with slots
root,label,control,track,fill,mark,valueand
thumb); an application hosting that Submodel draws it with Slots of its
own.orientation: 'vertical',ticks, labelledmarks({ value, label }) and the recipe'svalue: 'end',track: 'paint'andlabel: 'hidden'
are the view's too; upstream's Escape-to-cancel and read-only mode are
lost. -
foldkit-mixins-ui: oneAlert, inline or a banner.Alert.viewtakes
layout: 'inline'(a live region for something that just happened:
role="alert"whenassertive,role="status"otherwise) or
layout: 'banner'(a full-width strip there on load: asectionlandmark
named bylabel, never a live region). Both take an optional icon, now
aria-hidden, a title and description,actions(each a link or a button
that sends a Message) and a dismiss button (onDismiss,dismissLabel).
The root writesdata-layoutanddata-dismissible, and the banner keeps
room for the dismiss button only when it has one.Recipes.Alertgains
anemphasisaxis (subtle, the default, orbold) and the slots
actions,actionanddismiss;Patterns.Alertis the pattern, and
the gallery's Alert page shows both layouts. Breaking:layoutis
required (writelayout: 'inline'where an alert stood alone). -
foldkit-mixins-ui: Menubar, Menu, TreeView, Resizable and a
horizontal Listbox takedirection?: 'ltr' | 'rtl', and under'rtl'
ArrowLeft and ArrowRight swap. A view cannot read the page's direction,
so these components moved the wrong way right to left: a submenu opened on
ArrowRight away from where it was drawn, and a split's handle grew the
panel the arrow pointed away from. Each view now translates the two arrows
before building its Message (as Zag'sgetEventKeydoes), so Messages and
updatestay in reading order, and draws the direction as its root's
dir, so the CSS reads what the keys read; the Menu's submenu opens on
the left with its chevron pointing there, and Resizable reverses a drag's
horizontal movement. Tabs, RadioGroup and Calendar, whose arrows upstream
bundles handle, takeTabs.readingKeys,RadioGroup.readingKeysand
Calendar.readingKeysbeside the style: a Mount that reads each arrow by
the element's:dir()before upstream's handler does, opt-in because a
Scene test must resolve every Mount with a Message.Segmented.fluid,
Tabs.fluidandNav.fluidmeasure their indicator again when adir
changes. -
foldkit-auth,foldkit-ssr: a request with noOriginis taken by
Fetch Metadata.AuthServer.resolverandSSR.entry'soriginsread
Sec-Fetch-Site: same-originin place of a missingOrigin(a
same-originGET), as they already did forOrigin: null, and still
refuse a request with neither. The auth README says why the check rests on
the origin rather than onSameSiteor a token. -
foldkit-mixins-ui: toggles on the ladder, and Fluid's switch. A
checkbox, radio or switch with no size stands on the ladder's icon rung
(16px, 14px compact) instead of defaulting tomd. The switch is a 34×20
track with a 16px thumb (its height a quarter over the toggle's edge, its
track 1.7 of that), and its thumb stretches 2px into a pill under the
pointer, growing back from the far end when on. Breaking: an unsized toggle
no longer meansmd, and the switch is larger than its old 29×16. -
foldkit-mixins-ui: an icon, a dialog and a tooltip on the systems. An
Icon with no size stands on the size ladder (16px, 14px compact) instead of
defaulting tomd; a dialog is one notch narrower in a compact region (10%,
as Fluid Functionalism's 400px to 360px); and a tooltip drawn with
isAnimatedfades in on the fast tier, sliding 4px toward its trigger from
the side it opens on. Breaking:Recipes.Icon({})no longer meansmd. -
foldkit-mixins/theme,foldkit-mixins-ui: states as tints, and
concentric corners.Theme.oklchgains astategroup, what a state lays
over the surface under it as a mix of the element's own text color
(hover, the see-throughtintandtint-hover, andedge, a hairline
inside an image), with its amounts as knobs; andTheme.nested(outer, inset, border?)gives the radius of a surface inset in another. The menu,
popover, pagination and calendar hovers readstate.hoverinstead of the
muted surface, which vanished on a page of that surface; the neutral button
is see-through; avatar and card images carry the edge; and a popup's rows,
a segmented option and a pill tab round concentrically inside their
container. Breaking: a popup's panel has thexlradius, and the recipes
readstate.*, which a palette made withoutTheme.oklchmust define. -
foldkit-mixins/theme,foldkit-mixins-ui: six type roles. Atype
token group andTheme.role(name):display,title,subtitle,body,
captionandmicro, each a size and a whole-pixel leading that step with
the size ladder, after Fluid Functionalism's (body at 14px on 20). The
recipes set text by role (controlsbody, badges and tooltipscaption,
keycapsmicro, dialog titlestitle), tab and segmented labels are
trimmed to their capitals, a field's text is 16px on a coarse pointer
so iOS does not zoom into it, and Typography takes the roles as levels.
Breaking:control.textis gone (readTheme.role('body')), and dialog,
empty-state and card titles are smaller. -
foldkit-mixins/theme,foldkit-mixins-ui: one size ladder. After
Fluid Functionalism's: acontroltoken group holds the two ends (36px and
28px controls, with their padding, gap, inset and icon), astepknob
chooses between them, andTheme.ladder(name)writes the blend, read on the
element that uses it, so--fk-knob-step: 1on a toolbar makes everything
in it compact, a menu it opens included while the panel stays in the page
(portal: false). Buttons, fields, selects, tabs,
segmented controls, table rows, calendar cells and the rows of a menu,
listbox or combobox all stand one rung tall, so a popup's row lines up with
its trigger. Breaking: a recipe'ssizeonly sets the step on its own
element (smcompact,mddefault,lga half step up at 40px), and no
recipe defaults it any more, so an unset size follows the region. The
popups'sizesets it on theirwrapperand their panel (which upstream
portals by default), Tabs' on thetablist.
Segmented's sizes aremd,smandxs(24px). -
foldkit-mixins/theme: three motion tiers, and reduced motion keeps the
fades.motion.fast,moderateandsloware 80, 160 and 240ms, each with
an exit a tier quicker (fast-exit,moderate-exit,slow-exit, eased by
ease-exit), after Fluid Functionalism's springs. Atravelknob multiplies
the durations of what moves or resizes, andDefaults.resetsets it to 0
underprefers-reduced-motioninstead of zeroing every transition, so a
highlight snaps to its row while its fade still plays. The recipes' gliding
overlays share one transition (hover onfast, a chosen marker on
moderate), and the switch's thumb travels onmoderate. Breaking:
motion.normalis gone (readmoderate, orslowfor a loop's period), and
a transition of a transform written outside the recipes no longer snaps
under reduced motion unless it multiplies byknob.travel. -
foldkit-mixins/theme: the surface contrast knob reaches 2.5 times as
far, and defaults to 26%. At its top it moved a muted surface four points
of lightness, which read as doing nothing; the default look is the one 65%
drew. Breaking: a theme that setssurfaceContrastdivides it by 2.5 to keep
its look ('60%'is'24%'). -
foldkit-mixins/theme:radius.fullsquares off whereradius-factoris
0, as a boxy theme's pills should; at any other factor it is a pill.
Recipes.Badgerounds byradius.xl, so the radius knob reaches it, and
Recipes.Avataris a disc at the default factor, rounder squares below it. -
foldkit-remote-server:RemoteServer.memory'sprincipalis optional
for every principal type. It is only wholayeranswers as: without it,
and with a principal type that does not admitundefined, the backend is a
MemoryServed(its store andserver, nolayer), for serving through
serveFetchorhandlersas each request's principal. Code that omitted it
for such a type used to fail to compile; it now has nolayerto call. -
foldkit-mixins-ui:Cardis pressable, selectable and dismissible.
press({ onClick }or{ href, external? }) stretches one button or link
over the card, named bylabelor the title;selected,disabled,
dismiss, animageand amediatile are new,contentis optional, and
the view takes the application's Message. Breaking:actionsits at the end
of the header rather than the footer, so a card that put its button in
actionputs it infooter. The recipe addsorientation(stacked,
inline) andsurface(outlined,elevated,filled,plain) axes and a
compactpadding, and its padding is one private--_fk-card-pad. -
foldkit-mixins-ui:DatePicker.toView(mixins, draw)handsdrawthe
date picker's builder. Breaking: it took a context with a builder, which an
application had no way to give for the date picker's own Message;
DatePickerView'stoViewinput is called with that builder too. -
foldkit-mixins-uipeers onfoldkit-primitives, forCard.group's
highlight. -
foldkit-mixins-ui's stateless views take the application's builder.
Alert,AspectRatio,Avatar,Badge,Breadcrumb,ButtonGroup,
Empty,Icon,Item,Kbd,Label,ScrollArea,Separator,
Skeleton,Spinner,TableandTypographyare generic in the Message,
where they tookHtmlBuilder<never>, so what they hold can send one: an empty
state's button, a badge in a pressable card. A call with no Message is
unchanged.
Fixed
foldkit-mixins-ui: an unknown Progress holds still under reduced
motion. The reset runs every animation for 0.01ms there, so the ring's
quarter arc stood at a random angle each frame, and the bar's sweeping
fill stopped near its last frame, mostly outside the track. The ring now
stands still, as a Spinner's does, and the bar's fill rests faint across
the track, as it does with notravel. Moving stripes, which slid at
random the same way, stand still too.foldkit-mixins-ui: a loading Toast's ring holds still under reduced
motion. It breathed instead, and the reset's 0.01ms made the breath
flicker; it now stands still, as every loading ring does.foldkit-mixins-ui: the Questionnaire's "Other" label reads as an
option. The other field's label was the Input's, which a text answer's
question also uses, so it looked like a question heading. Its words are now
in a newotherLabelSlot, which the recipe draws as the options' labels.foldkit-mixins-ui: Cascade Select's columns stay on a phone's screen.
At 390px the popup's columns ran past the window's edge. When the popup
opens,PlaceCascademeasures whether a column per level of the deepest
path fits the window, and the columns layout drills in when it does not
(the Model's newcramped, sent asPlaced({ cramped })). The panel is
capped to the window too, so one narrowed while open scrolls its columns.foldkit-mixins-ui: the Angle Slider's needle clears the value in the
middle. It ran from the center to the thumb, straight through "45°" or
"1 of 4"; it now stops 2em of the value's type short of the center, and
runs to the center only when the value is drawn below the dial.foldkit-mixins-ui: the Image Cropper shows its image. The recipe hid
the image under any ancestor withoutdata-ready,bodyincluded, so it
stayed hidden after loading; the image now carries the mark itself.foldkit-mixins-ui: Steps and the Navigation menu at a phone's width.
In a narrow Steps row the current title ran over the next connector; it
is now cut with an ellipsis, and the connectors shorten so a short word
stays whole. A dot marker's dot sat above its connector, pushed up by the
hidden number's row. A Navigation menu trigger's chevron, squeezed in a
narrow row, drew as a slanted tick; it keeps its size. A panel's groups,
a fixed row of 12rem columns, ran off a phone's screen; they wrap.foldkit-mixins-ui: the Popover's arrow can be seen. It was drawn,
but as an 8px square in the panel's own fill and faint line, half of it
showing in the gap: a nub too slight to see. The Popover's arrow is now
space.smacross (arrow's newsize) and its panel 12px off the
trigger, so the arrow crosses most of the gap.foldkit-mixins-ui: TagsInput keeps keys typed fast out of the tag just
added. Keys typed before the emptied input was drawn joined the tag
Enter had just added.foldkit-mixins-ui: display fixes in a dark scheme. A chosen card and
a Card Group's highlight no longer read as sunken; a card's "New" badge
sits on its title's line; a Separator and a Skeleton show on a dark page.foldkit-mixins-ui: rows, pills and highlights follow their container's
corner. A menu's, listbox's or combobox's highlight and chosen marker,
a Command's rows, a Menubar's triggers, a Segmented option's pill and
hover, and a pill Tabs' chosen pill and hover kept a fixed radius that did
not meet the panel or tray around them. Each container now publishes its
corner withTheme.nests, and they readTheme.nested().foldkit-mixins-ui: InputCopy's focus rings follow the group's corners.
The input's and button's inset rings were square inside a rounded group
that clips, so their outer corners were cut off; they now round those
corners to the group's inner radius.foldkit-mixins-ui: Steps line up, and fit a phone. Each step's text
was centered on its marker, so a step with a description stood its title
higher; titles now start on the marker's middle line, and the connectors
sit on that line too. A horizontal row takes its container's width and,
below 32rem of it, shows only the current step's label (the others stay
for a screen reader), where every label had overlapped the markers.foldkit-mixins-ui: Pagination fits a narrow row. At 390px "Previous"
ran into the "1" beside it; the row now wraps and centers, and Previous
and Next have room either side.foldkit-mixins-ui: an Alert stacks its title over its description.
Side by side, a narrow alert squeezed its title into a column of single
words. The icon keeps its own column beside both.foldkit-mixins-ui: a Hover Card no longer moves its trigger. The
Anchor writestopandleftbut noposition, so an unportaled card
stood in the line beside its trigger and the centered row shifted; the
anchored placement now takes the panel out of the flow.foldkit-mixins-ui: a Segmented option's words sit centered. The
text-boxtrim on the option ended at the hidden semibold copy after the
words, its last line, so only their top was trimmed and they stood 2.5px
high.Segmented.viewnow draws the words in a newlabelSlot that
holds the trim; an option drawn by hand with adata-labelshould do the
same.foldkit-mixins-ui: smaller centering fixes. The Checkbox's check
sits a pixel lower, on center, and is placed physically like its sides;
a Kbd's line is trimmed to its capitals with the height given back as
padding; a radio centers on the first line of a label beside it in a row
aligned to its top (calc((1lh - edge) / 2)).foldkit-mixins-ui: Progress's indeterminate bar sweeps right to left
underdir="rtl". The fill starts at the inline start but moved by a
physicaltranslate, so right to left it appeared mid-track and slid off
the wrong side; a mirrored keyframe now runs under:dir(rtl), with the
same timing, so reduced motion still holds it still.foldkit-mixins-ui: the Switch works right to left. Its thumb starts
at the inline start but moved by a physicaltranslate, so right to left
it slid off the far side; it now moves left there.foldkit-mixins-ui: the RadioGroup recipe stacks a vertical group. The
group was a row whatever itsaria-orientation, and upstream's groups are
vertical by default; it is now a column unlessaria-orientationis
horizontal. The README and the skill now say to draw each option as a
labelaround its radio, since upstream's label bundle carries only an id
and a bare label took no press (the gallery's did not).foldkit-mixins-ui: a Skeleton bar shows in a container that sizes to
its content. Itsinline-size: 100%resolved to nothing there (a
centered column, a flex item), so the bar computed 0px wide and the
gallery's skeleton showed nothing. A bar now has a4remfloor.foldkit-mixins-ui: chevrons point the right way right to left. The
Accordion's, Sidebar's, TreeView's and Menu submenu rows' chevrons and the
Carousel's arrows drew their two sides with logical borders, which mirror
underdir="rtl", so a shut row's chevron pointed up and the arrows
pointed up and down. They and the Steps check now sharecornerSides
(physical sides), and the Sidebar gains the right-to-left turn it lacked.foldkit-mixins-ui: a Sidebar section's links and a TreeView group's
rows hold still while they open. The heading row stretched into the room
the opening grid left mid-transition, so the rows below it dropped (about
16px in the Sidebar) and rose back.
Sidebar and TreeView now share oneopensBelowpiece that keeps rows at
the top (align-content: start).foldkit-mixins: injected styles keep the page's layer order. The
<style>a draw injects carries the page's own@layerorder first. It
carried none where the page had one, so once it stood ahead of the page's
sheet (a test's or an application's sheet put in after it), its
@layer components { … }madecomponentsthe first layer, under the
reset, and a button's text fell back to the reset's 16px.foldkit-mixins-ui: a badge squeezed in a row keeps its words inside it.
The page reset floors every element'smin-widthat 0, so a badge in a
narrow row shrank under its unwrapped text, which ran into the next one.foldkit-mixins-ui:Recipes.Dialogcenters the open panel.@foldkit/ui
draws the open dialog over the whole viewport, so the panel sat at its top.foldkit-mixins-ui:Recipes.Sliderkeeps a track wide enough to drag
(12rem at least) in a row or a centered box, where it shrank to its label.foldkit-mixins-ui: a menu, listbox or combobox marks the row the keys
are on. The shared popup row styled only:hover, so moving by the keys
showed nothing; it now reads upstream'sdata-active, and the focused panel
draws no browser ring around itself. A row's hover is a tint of its text, which
reads in a dark scheme, where the muted surface was all but the panel's own.