Repository navigation
0.2.0
Added
data-table: a server-driven table (search, filters, sortable columns, page size, pages) in a Turbo Frame, with
the PHP classes a table extends (AbstractDataTable) copied intosrc/UXor/DataTable/. A table counts its
rows (countRows()), then loads one page once (loadRows()); no page starts pastmaxRows()rows (10,000), so a
request never makes the database skip more.Filter::choice()takesarray<int|string, string>choices. Back and
Forward walk every state, and thedata-tablecontroller makes the form show the URL's state in every field; Back
pressed while a change is still loading cancels the change, so the URL and the table never disagree. Its checks of
Turbo's cached copies are theturborecipe's module, which it installs with it.data-table-live:data-tableas a Live Component (AbstractLiveDataTable), with row selection for bulk actions;
its state is in the URL. The selection holds at mostmaxSelection()ids (1,000) of at most 128 characters,
enforced on what the browser sends before anything uses it; leaving the page drops it, and Back and Forward do not
bring it back. Its checks of Turbo's cached copies are theturborecipe's module, which it installs with it.autocomplete: searchable selects with Symfony UX Autocomplete (Tom Select), styled with the theme, through the
form theme ('autocomplete' => true) or theAutocompletecomponent. Itsautocomplete-synccontroller keeps Tom
Select showing the<select>'s value after a Live re-render, a form reset and Back, and the hidden<select>keeps
its label's name (aria-labelledby).popover: free content anchored to a button, in a non-modal dialog; it closes on Escape, a click outside or when
the focus leaves it, and before Turbo caches the page. Beside a frame visit promoted to history (a data table's
pages) it stays open, and Back shows it closed. Its checks of Turbo's cached copies are theturborecipe's module,
which it installs with it.calendar: pick a date, several dates or a range, with hidden inputs for forms (dispatchinginputandchange)
and amodelprop for Live Components; invalid dates are ignored, and a range never spans a disabled day. It
follows a reset of its form, unless a listener cancels the reset.date-picker: aCalendarin aPopover, opened from a button or a field where the date can be typed; a
DateTypewith'block_prefix' => 'flowbite_date_picker'renders as one through the form theme.dropzone: file uploads with Symfony UX Dropzone, styled with the theme: drag and drop or browse, a preview of
the picked image, several files that add up across picks; focus follows a pick or a removal, and a file dropped
outside the input is refused. In a POST form, the file picked shows again after Back and Forward. Its checks of
Turbo's cached copies are theturborecipe's module, which it installs with it.chart: charts drawn with Symfony UX Chart.js in the theme's colors, light and dark, each with its data as a table;
from arrays or aChartBuilderInterfacechart, updated in place by Live Components.editor: a rich text editor (Tiptap) storing restricted HTML, with a keyboard-friendly toolbar, a link dialog and
a counter;EditorTypesanitizes every submit with symfony/html-sanitizer and refuses too long input (a 422 with the
field's error, never a cut value), and theflowbite_editor_htmlfilter prints stored HTML. White space is stored as
the editor shows it, and the server counts characters as the counter does. Back, also from a frame visit promoted to
history, builds the editor again with its content.markdown-editor: a Markdown field, a native textarea with a toolbar writing Markdown and a Preview tab rendered on
the server by a Live Component;MarkdownTyperefuses too long input, and theflowbite_markdown_htmlfilter prints
stored Markdown the same way (CommonMark without raw HTML or images, then sanitized). Back keeps what was typed.side-nav: a multi-level navigation tree (WAI-ARIA tree view): links in branches that open and close at any depth,
arrow keys, Home, End and type-ahead, the branch of the current page open, and the open branches kept across Turbo
visits, Back and Forward insessionStorage(two trees sharing astorageKeyagree). Its current-link marking is
thenavigationrecipe's module, which it installs with it.mobile-nav: the app's navigation on small screens, in a modalDraweropened by a menu button in theNavbar
(menublock); the focus goes to the current page, and the drawer closes on a link, Escape, the backdrop, before
Turbo caches the page and when the screen grows to the sidebar's width. Its link-follow check and its closing when
the screen grows are thenavigationrecipe's module, which it installs with it.nav-menu: the navbar's menu, a disclosure navigation: links and buttons opening submenus of links, nested at any
depth; opening one closes the others, Escape closes the innermost and focuses its button, a click outside, the
focus leaving and Turbo caching the page close them all; the current page and its submenus are marked, a submenu
near the edge opens towards the other side, and the same menu opens in place in aMobileNav. Its current-link
marking and its link-follow check are thenavigationrecipe's module, which it installs with it.section-nav: vertical tabs that navigate between the pages of one area (settings): links with
aria-current="page"marked by the server or from the URL, a column on large screens and a strip that scrolls
sideways, with the current section in view, on small ones. Its current-link marking is thenavigationrecipe's
module, which it installs with it.floating: the positioning thatdropdown,popover(sodate-picker) andtooltipshare, one JavaScript
module (assets/lib/uxor-floating.js) that those recipes install with them: placement, flip, shift into
the viewport, following the trigger on scroll and resize.turbo: what the recipes ask about Turbo's copies of a page, one JavaScript module
(assets/lib/uxor-turbo.js) that the recipes using it install with them: whether aturbo:before-cache
comes from a frame visit promoted to history, whether an element isdata-turbo-permanent, and whether a controller
connects in a cached copy.navigation: what the navigation recipes share, one JavaScript module (assets/lib/uxor-navigation.js):
marking the current page's link, giving the renderedaria-currentback, whether a click on a link navigates this
tab, closing a navigation opened over the page when the screen grows.theme: chart series roleschart-1tochart-6andchart-other, checked for contrast in both themes.- A gallery of every recipe and every example of its README, light and dark, with its code and install command, on
GitHub Pages (https://xormania.github.io/uxor/), published with each release. FOR-AGENTS.md, a page for the coding agent given the repository's URL (setup, which recipe for what, the rules,
every recipe), andllms.txt, every page with one line, linking the files of this release. Both are written from
README.md's recipe tables and checked in CI.SECURITY.md: how to report a vulnerability privately.
Changed
-
Breaking: the kit is now UXor, at https://github.com/xormania/uxor (gallery:
https://xormania.github.io/uxor/).ux:install … --kit=commands that name the old repository
(xormania/flowbite-xor) stop working: the toolkit expects GitHub's archive to hold a folder named
<repository>-<version>, and after the rename GitHub names ituxor-<version>. Reinstall with
--kit=https://github.com/xormania/uxor(or…/uxor:<version>). The kit's own names change with it:theme:assets/styles/flowbite-xor.cssis nowassets/styles/uxor.css. Inassets/styles/app.css, replace
@import './flowbite-xor.css';with@import './uxor.css';, then delete the old file (it is yours, so carry
any change you made to it over first).autocomplete,dropzone,editor:assets/styles/flowbite-xor-autocomplete.css,flowbite-xor-dropzone.css
andflowbite-xor-editor.cssare nowuxor-autocomplete.css,uxor-dropzone.cssanduxor-editor.css: change
their@importinapp.cssthe same way. The spinner's keyframesflowbite-xor-autocomplete-spinare now
uxor-autocomplete-spin.floating,turbo,navigation:assets/lib/flowbite-xor-floating.js,flowbite-xor-turbo.jsand
flowbite-xor-navigation.jsare nowuxor-floating.js,uxor-turbo.jsanduxor-navigation.js; the recipes'
controllers import the new names. A controller of your own that imports../lib/flowbite-xor-<name>.jsimports
../lib/uxor-<name>.js.- PHP: the namespace
App\FlowbiteXor\…is nowApp\UXor\…, copied intosrc/UXor/instead of
src/FlowbiteXor/(data-table,data-table-live,editor,markdown-editor). Replace every
use App\FlowbiteXor\withuse App\UXor\(tables extendingAbstractDataTableorAbstractLiveDataTable,
forms usingEditorTypeorMarkdownType), then deletesrc/FlowbiteXor/.
0.1.0 shipped only the theme's stylesheet under its old name; the other files above were renamed before their
first release and matter only to a project that installed them fromdev. After reinstalling, delete the old
files, whichux:installdoes not remove. -
Installing without a version (
--kit=https://github.com/xormania/uxor) installsmain, which now holds
the last release only;--kit=https://github.com/xormania/uxor:devinstalls the work merged since. -
theme: on-fill roles inuxor.css, the color of text on a solid fill (fg-on-brand,fg-on-success,
fg-on-danger,fg-on-warning,fg-on-dark) and of the toggle's knob (knob). All are white, so nothing looks
different, and a project with a light brand color can setfg-on-brandto a dark one.button,indicator,
calendar,tabs(pill),tooltip,avatar(group count) andtoggleuse them instead oftext-whiteand
bg-white. Upgrading: reinstallthemewith those recipes, or those components lose their white text. -
Motion: transitions name the properties they animate and stop under
prefers-reduced-motion.tabstriggers fade
their colors only (wastransition-all), thetoggleknob animatestranslateand its border color (was
transition-all), and themodalbackdrop's transition names its color (was every property); these, thesidebar
width and chevron, thenav-menuandside-navchevrons,tablerow hovers and thetoastfade change at once
under reduced motion. There, a dismissedalerthides and a closedtoastleaves the page at once, without
waiting for the fade, and thespinnerand theskeletonpulse run three times slower. -
dropdownandtooltipplace their content with thefloatingrecipe's module instead of a copy each, and depend
on that recipe (ux:installaddsassets/lib/uxor-floating.js). Where they place it does not change. -
dropdown,modal,drawer,toastandlayouts(form-reset) use theturborecipe's module instead of a
copy of its checks each, andsidebarthenavigationrecipe's; each depends on that recipe (ux:installadds the
module). What they do does not change. -
tabs: the keyboard of the WAI-ARIA tabs pattern: the selected tab is the list's one Tab stop, the arrow keys of
the list's orientation (Up and Down in a vertical list, which now hasaria-orientation) select the previous and
next tab, Home and End the first and last, skipping disabled tabs; Tab moves on to the panel, a Tab stop of its own,
and a selected tab disabled in place (a Live re-render) hands the selection on. AnidPrefixprop keeps twoTabs
of a page from sharing ids. -
layouts: Back and Forward show a GET form with the values of the URL, and a POST form as the user left it; the
form-resetcontroller on<body>resets each GET form of a restored page, and autocomplete fields, calendars and
date pickers follow it. With a layout of your own, adddata-controller="form-reset"to its<body>. The
data-tablesearch form keeps its own reset, so a table works withoutlayouts. -
layouts: on small screens, the app layout's menu button opens thesidebarblock's navigation in aMobileNav
(a modal drawer) instead of theSidebarover the page. -
layouts: anavbar_navblock, the navbar's menu (NavMenu:Links andNavMenu:Submenus), shown in the navbar
frommdand in the mobile nav's drawer below. -
layouts: thesettingslayout renders its navigation withsection-nav; fillsettings_navwith
SectionNav:Items (plain<li><a>items still render, without the controller's marking). -
layoutsnow depends onsection-nav,mobile-navandnav-menu, whichux:install layoutsinstalls with it. -
navbar: amenublock, at the start of the bar, for aMobileNav, and anavblock after the brand, for a
NavMenu, in a<nav>named bynavLabeland shown frommdup. -
form-theme: anEditorTyperenders as theeditorrecipe'sEditor, aMarkdownTypeas themarkdown-editor
recipe'sMarkdownEditor, and aDropzoneTypeas thedropzonerecipe'sDropzone, never as UX Dropzone's own
theme (a field whose valid files a 422 sent back says they were not kept); a single-textDateTypeopted in with
'block_prefix' => 'flowbite_date_picker'renders as thedate-pickerrecipe's picker. -
dropdown,modal,drawer: the README examples size their wrapper withmin-h-*classes instead of astyle
attribute, which a Content Security Policy blocks. -
The docs no longer track the official
flowbite-4kit, an initial reference only:UPSTREAM.mdis removed, and
its notes on toolkit and platform behavior moved todocs/NOTES.md.NOTICEkeeps the credit. -
README.mdis a short introduction: what the kit is, how to install it, its requirements. The recipe list moved
todocs/RECIPES.md, and the updating, Turbo and Live Components, security and versioning sections to
docs/GUIDE.md.
Fixed
-
modal: the backdrop fades, in as the dialog opens and out before it closes (150 ms); its transition classes never
ran before, so it appeared and went at once. The dialog stays open and modal while the backdrop fades out, then
closes and gives the focus back. Underprefers-reduced-motionit does not fade and the dialog closes at once, as
before; before Turbo caches the page it closes at once too. Chromium runs both fades, Safari the fade in only,
Firefox neither. -
dropdown,modal,drawer: one left open when the page was left (a link inside it, Back, Forward) came back
after Back open but not working (a dialog no longer modal, the page behind it usable), its trigger still
aria-expanded="true"; it now closes and its trigger collapses before Turbo caches the page, and it opens again as
before. A frame visit promoted to history (a data table's pages) keeps the page on screen: the menu or dialog stays
open, and the copy Back shows is closed. A drawer reopened after a move in the DOM (adata-turbo-permanent
element) no longer shows its trigger collapsed. -
dropdown: one open inside adata-turbo-permanentelement closed when a visit moved it into the next page; it now
stays open, as the user left it, like the modal and the drawer. -
dropdown: ArrowRight on a submenu's item opened the submenu but left the focus on the item, and closed a submenu
already open (by a click); it now opens the submenu, or keeps it open, and focuses its first item. -
modal,drawer: a Live re-render that replaced the container of the component (a wrapper whose id changes)
logged "Missing target element" errors. The old controller now disconnects without them; the new one works as
before (an open modal stays open, its new trigger expanded; a drawer starts as itsopenvalue says). -
modal: in WebKit, an open modal whose container a Live re-render replaced closed (withoutmoveBefore, the morph
moves the<dialog>out of the top layer); it now shows that same dialog as a modal again. -
tooltip: a tooltip shown when its link visited another page (or when a frame visit was promoted to history) came
back shown after Back or Forward, its trigger no longer hovered; it now comes back hidden. A tooltip in a
data-turbo-permanentelement stays shown through a visit while its trigger keeps the focus. -
toast: a toast outside the permanentToastRegioncame back on Back, for another full timeout; Back and Forward
no longer show it again, and a frame visit promoted to history leaves it on screen. Toasts in the region still stay
across visits until they time out or are closed. -
sidebar: withlocalStorageblocked, a Turbo visit expanded a collapsed sidebar; the collapse now lasts until the
next page load. Forward to a page left with the sidebar open over it showed the menu button expanded on a closed
sidebar, and the sidebar stayed open over the page when the screen grew to a desktop, so shrinking it again showed
it open and the menu button took two clicks; the menu button now follows the sidebar on reconnect, and the sidebar
closes where it sits beside the page. -
theme-toggle: with the system in dark mode and the light theme chosen, the button showed no icon
(flowbite.min.cssgates its owndark:hiddenonprefers-color-scheme); it now shows the moon. A switch no
longer fades table rows into the new theme: the color transitions it starts are finished at once. -
button: the keyboard focus was a 4px ring right against the fill, in a tint of it (brand-mediumonbrand:
1.8:1, 1.4:1 in the dark theme; the outline variants' gray ring 1.1:1 on the page), so hard to see. Every variant
now shows a 2pxbrand-mediumoutline 2px apart from the button, on:focus-visibleonly (no ring after a click),
at 3:1 or more against the page in both themes (WCAG 1.4.11). -
input: the "With Button" example shows theflowbiteset's search icon (flowbite:search-outline), not
tabler:search. -
theme: native controls follow the theme on screen, not the system's:uxor.csssetscolor-scheme: lighton the
page andcolor-scheme: darkunder.dark. With the system dark and the light theme chosen, the calendar's month
and year menus showed dark text on a dark menu (and light text on a light one the other way round); select menus,
date fields and scrollbars now match the theme.calendar: its month and year selects give their options the
menu's colors (bg-neutral-primary-medium,text-heading), so Flowbite's white select background no longer sits
under white option text in the dark theme. -
input: anInputwith notype(a text field all the same) had no inline padding, its text against the left
border, as in theform-fieldexample's username: Flowbite's base rule pads only typed fields. It is now padded
liketype="text". -
select: a long option ran under the chevron: the Select'spx-3replaced the room Flowbite's base rule leaves for
it. The Select now pads its inline end (pe-10), so right to left keeps the room on the chevron's side; the same
indata-table-live's filter and page size selects. A multiple Select (a list box) no longer shows the chevron
(Flowbite's rule removing it loses to its ownselect:not([size])), and the "Multiple" example drops its
"Choose a country" option, which pushed a fifth row half into the bottom of the box. In the dark theme its
selected row is dark (thethemefix above), not light gray. -
pagination: in the dark theme the current page's number read at 2.7:1 (text-fg-brandon
bg-neutral-tertiary-medium); the current page now takestext-fg-brand-strongonbg-brand-softer, as the kit's
other current items, at 4.5:1 or more in both themes. axe passed it, as it passes all one-character text: the
accessibility scan now also measures every text of every README example in both themes. The data tables' pagers
show the same fix. -
stat-card,dashboard-home: at tablet widths, beside the sidebar, the values spilled out of their cards
("$3,141,592" in 182px of text in a 94px card). The dashboard's cards now take three columns once the block is 42rem
wide (a container query), each value a step smaller (text-2xl) in a narrow column, and a value still too long
wraps inside its card. The stat card example lays its grid out the same way. -
navbar: at 320px the search was crushed to a sliver over the brand and the actions pushed out of the bar, the page
scrolling sideways. On a phone the search now takes a row of its own under the brand and the actions, a long brand
wraps, and the menu button keeps its size. -
data-table,data-table-live: in a flex or grid parent (the gallery's preview, a grid cell) the table's minimum
width set the component's, so the toolbar and the pager ran off a phone's screen; the component now shrinks to its
parent and the table scrolls in its own frame. -
badge,table: README examples that overflowed a phone's width: the badge rows wrap, and the table without border
sits in a horizontally scrolling wrapper like the other table examples. -
floating(sodropdown,popover,date-pickerandtooltip): an element placedleftorrightwith room
on neither side, as on a phone, stayed on its side past the viewport's edge and pushed the page sideways (the
"Placements" popover at 390 pixels); it now opens below its trigger, or above it, and when no side has room it is
kept inside the viewport's width. Where it fits, it lands where it did.floatingexportsorigin(), the
containing block's start in viewport coordinates. -
tooltip: in a wrapper with a border (the dashboard's "+9k" avatar), the tooltip was placed from the border's
outer edge, 2 pixels off, and at the right edge of a phone pushed the page sideways; it is now placed inside the
border, 8 pixels from its trigger and within the viewport.
Security
button,badge,dropdown,paginationandavatar: link and image URLs are guarded props, so an unsafe
scheme cannot be introduced through theirhreforsrcattributes. Upgrading: anhrefonButton,
Badge,Dropdown:ItemorPagination:Linkwith a scheme other thanhttp(s),mailtoortel(or a relative
URL) now renders#; anAvatar:Imagesrcother than relative orhttp(s)(data:,blob:) renders nosrc,
so the fallback shows; anhrefon aButton,BadgeorDropdown:Itemthat is notas="a"is no longer
rendered; andPagination:Link'shrefdefaults to#.- Every component, and
form-field'slabelAttrandhelpAttr, leaves out an attribute whose name starts with
on(in any case, after leading spaces): an inline event handler given to a component renders nothing. Behavior
goes throughdata-actionand a Stimulus controller. Upgrading: anonclickor other handler passed to a
component no longer renders; move it to a controller. - Every component reads the URL attributes given to it as a browser reads their names, in any case (
HREF,
FormAction,Src):hrefandformactionkeep a relative,http(s),mailtoortelURL, any other
rendering#, andsrca relative orhttp(s)URL, any other rendering nosrc. Upgrading: asrcsetor
pingattribute given to a component no longer renders.