github quasarframework/quasar quasar-v2.25.0

Important

A few behavior changes worth checking before you upgrade:

  • QMenu no longer claims role="menu" and QBtnDropdown's toggle no longer emits aria-haspopup -- a menu may only contain menuitem* children, so the old markup was invalid. Declare role="menu" on the wrapped QList (its actionable QItems then expose themselves as menuitems automatically) and use the new toggle-aria-haspopup prop on QBtnDropdown.
  • SSR'd virtual lists now render their first slice on the server, so item templates of a server-rendered QVirtualScroll/QTable run on the server too and must be SSR-safe.
  • QSlideTransition starts animating immediately instead of 100ms later, which shifts the timing of every QTree / QExpansionItem / QStep slide.
  • Third-party language packs: this release adds colorPicker and uploader sections plus several keys. All are optional in the types and read with optional chaining, so existing packs keep working and compiling -- but they will announce nothing for those internals until updated.

New

  • feat(QTree): opt-in virtual scroll mode -> new virtual-scroll prop family, @virtual-scroll event and key-based scrollTo(key, edge). Only the rows around the viewport exist in the DOM, so cost stays flat however much of the tree is expanded (22.6k nodes, dev mode: expand-all 2.2s -> 23ms, filter keystroke on a fully expanded tree 560ms -> 29ms, ~600 DOM elements at any scale)
  • feat(QTree): methods for the state v1 exposed through its per-node meta map -> getIndeterminateNodes(), isIndeterminate(key), getParentNode(key) and tri-state getTickState(key) (returns the true/null/false a QCheckbox takes as its model); the header and body slot scopes gain an indeterminate boolean next to ticked (#17298) (#12762)
  • feat(ui): SSR renders the virtual scroll initial window -> server-rendered virtual lists emit their first slice instead of zero items, so crawlers and first paint finally see content. QTable's virtual-scroll mode inherits it; QSelect is unaffected
  • feat(QDate): roving-tabindex keyboard navigation for the day grid -> the calendar is a single Tab stop instead of up to 31. Arrows move by day/week, Home/End to the week edges, PageUp/PageDown by month (Shift for year), all crossing month boundaries and honoring RTL, the options prop and the navigation-min/max limits
  • feat(QTime): direct numeric keyboard entry on the time units -> typing digits on a focused hour/minute/second sets it directly, accumulating multi-digit values within 1.5s (#12467)
  • feat(QSplitter): Enter collapses the primary panel to its minimum limit and restores the pre-collapse position on the next press (#12466)
  • feat(QSlider/QRange): the slider role now sits on the focusable element, and the whole family gains Home/End
  • feat(QRange): WAI-ARIA multi-thumb slider semantics -> new left-thumb-aria-label and right-thumb-aria-label props
  • feat(QBtnDropdown): new toggle slot -> renders next to the dropdown arrow, so content like a QTooltip can finally attach to the toggle button in split mode (#16955)
  • feat(QBtnDropdown): new toggle-aria-haspopup prop -> names the popup's role on the control that actually opens it, in both the regular and the split design
  • feat(QList/QItem): new role prop -> the documented override path (e.g. menuitemcheckbox, or role="menu" on a QList)
  • feat(QLayout): new $layout-marginal-background and $layout-marginal-color Sass variables for the marginal sections (#10476)
  • feat(QDrawer): new escape-key event
  • feat(QField): the control slot scope gains ariaInvalid, ariaDescribedby and ariaErrormessage
  • feat(a11y): the component internals consumers cannot reach now carry accessible names -> QColor's view tabs, header value field and hue/opacity sliders, QUploader's icon-only buttons and hidden file input, QTable's selection checkboxes plus their empty column header, and QPagination's navigation landmark. Adds the colorPicker and uploader lang sections plus table.selectAllRows, table.selectRow and pagination.label, translated across all 72 language packs

Accessibility

  • fix(QTime): implement the WAI-ARIA spinbutton pattern -> the hour/minute/second controls are localized spinbuttons instead of mislabeled toggle buttons; arrows adjust the focused unit (key-repeat works, the page no longer scrolls) and Home/End jump to the first/last valid value (#12467)
  • fix(QDate): give the calendar controls real accessible semantics -> days announce their full localized date instead of a bare number, expose selection through aria-pressed and mark today with aria-current="date"; the view switcher becomes a real button that Space activates
  • fix(QOptionGroup): implement the WAI-ARIA radiogroup keyboard pattern -> a radio group is a single Tab stop with arrow-key navigation, wrapping, skipping disabled radios and mirrored in RTL (#17638)
  • fix(QOptionGroup): announce a disabled group whatever its type -> aria-disabled was set only for radios
  • fix(QSplitter): implement the WAI-ARIA window splitter keyboard pattern -> the separator is a focusable, value-carrying separator; arrows move it by 1% (10px in px unit) and Home/End jump to the limits (#12466)
  • fix(QSplitter): give the separator bar an accessible name -> new separator-aria-label prop
  • fix(QTabPanels/QTabs): complete the WAI-ARIA tabs pattern -> QTabPanel is focusable so keyboard and screen reader users can reach panel content with nothing focusable inside, and the tabpanel role lives solely on the panel components (#6560)
  • fix(QStepper/QCarousel): correct the ARIA semantics of their panel patterns -> QStep drops the orphaned tabpanel role and becomes a group labeled with its title, conveying aria-current="step" even with header navigation off; QCarousel's navigation is exposed as the tabs pattern it actually is (#6560)
  • fix(QEditor): complete the WAI-ARIA toolbar/textbox contract -> role=toolbar with a localized name, a single Tab stop with roving tabindex, arrow navigation (wrapping, RTL-mirrored) and Home/End (#15675)
  • fix(QEditor,QTable): name the internals that reuse existing lang keys -> QEditor's toolbar toggles convey their state with aria-pressed instead of color alone, and the hyperlink editor's URL field gets a name
  • fix(QMenu/QList/QItem): derive valid ARIA roles instead of claiming role=menu (#17162)
  • fix(QMenu): close the popup when TAB moves focus out of it -> focus no longer drops onto the browser UI leaving the menu open (#15675)
  • fix(QMenu): mark its anchor as the popup's trigger -> aria-expanded/aria-controls stay in sync without apps mirroring the open state by hand
  • fix(QMenu,QDialog): render menu portals inside their aria-modal dialog -> menus anchored in a modal QDialog (a QSelect's listbox included) were pruned from the accessibility tree and never announced (#17078)
  • fix(QSelect): align the popup with the ARIA combobox pattern -> the listbox role wraps only the options, which now carry aria-setsize/aria-posinset (#17344)
  • fix(QSelect): keep a readonly select focusable and announced -> it used to emit no focusable element at all
  • fix(QSelect): expose a disabled select instead of dropping its control
  • fix(QSelect,QTable): give the rows-per-page selection an accessible name (#9689)
  • fix(QInput,QSelect,QField): expose error state to assistive tech (#18323)
  • fix(QFile): expose error state to assistive tech (#17306)
  • fix(use-field): drop the error ARIA references when no message renders -> <q-input error hide-bottom-space> pointed aria-errormessage/aria-describedby at a missing id
  • fix(QTree): tickable nodes trapped the keyboard -> tickboxes leave the Tab order and Space toggles them from the node header (#17603)
  • fix(QTree): make every visible node keyboard-reachable, disabled ones included -> arrow navigation used to skip every leaf in a tree without selection
  • fix(QTree): don't hijack keys typed into interactive header content (#17790)
  • fix(QTree): demote a stale roving Tab stop -> re-expanding could leave two Tab stops
  • fix(QTree,useRefocusTarget): never let focus land in an aria-hidden control (#17810)
  • fix(QCarousel): give the prev/next arrows an accessible name -> new carousel lang-pack section, translated across all packs (#7423)
  • fix(QDrawer): let ESCAPE dismiss the drawer in its modal states, under exactly the conditions the backdrop accepts a click (#7423)
  • fix(QItem): attach pointer listeners only on interactive items -> WCAG checkers flagged the no-op handlers on a non-clickable QItem (#7423)
  • fix(QStepper): announce a disabled step header as an unavailable button instead of inert text
  • fix(QRating): ArrowDown/ArrowUp move focus in the documented radio-group direction
  • fix(QSlider,QRange): say "no value" instead of announcing a limit as one -> a null QSlider announced the minimum, an untouched QRange the whole range as selected
  • fix(QSlider,QRange,QImg): emit valid ARIA in three edge cases
  • fix(QIcon): keep the ligature text out of the accessibility tree (#18084)
  • fix(a11y): give the scrollable regions keyboard access -> QMarkupTable and QTable's horizontally scrolling middle are Tab stops, so a table wider than its container is no longer pointer-only
  • fix(ui): drop spec-invalid ARIA from QToggle, QPagination, QChip, QFab, QImg, QLinearProgress, QTimeline and QBtn
  • fix(types): keep the screen-reader lang keys optional -> a third-party language pack written before them kept compiling

Performance

  • perf(QTree): the tree was rebuilt around lazy rendering, per-node render isolation and incremental state -> collapsed subtrees mount on first expansion instead of being pre-rendered behind v-show (22.6k-node tree, dev mode: 2196ms/299,713 DOM elements -> 31ms/169), a state change re-renders only the affected nodes, and the monolithic O(N) meta rebuild became layered derivation that recomputes only on its own triggers. Per-key state of keys that left the model is pruned, and getTickedNodes()/getExpandedNodes() use keyed lookups
  • perf(QSlideTransition): start animating immediately instead of 100ms later -> every QTree, QExpansionItem and QStep slide loses 100ms of idle latency, and duration <= 0 short-circuits to an immediate settle
  • perf(ui): a broad mount-cost sweep -> useQuasar() resolves through the instance's global properties (~0.6ns/read vs ~37ns), size styles are module-cached, and trivial computeds were flattened out of the render path across ~70 files (QBtn, QItem family, the checkbox family, QChip, QTab/QRouteTab, QList, spinners and 12 bulk-mounted leaves, plus use-dark/use-align/use-form/use-transition/use-router-link/use-split-attrs)
  • perf(ui): every component of that sweep was then re-benchmarked in a real browser, and the computeds whose removal cost more than it saved were restored
  • perf(QIcon): module-level parse cache, no per-instance computeds
  • perf(date): compile formatDate masks once and cache per mask
  • perf(QScrollArea): allocation-free @scroll payload assembly
  • perf(ui): allocation-free isKeyCode, loop-based sumSize, regex-free hexToRgb and allocation-free mask token testers
  • perf(QInput/QSelect/QTab): test the cheap event flags before shouldIgnoreKey()

Fixes

  • fix(QSelect): allow cancelling keyboard actions (#18507)
  • fix(QInput): allow cancelling mask keyboard actions (#18515)
  • fix(QEditor): allow cancelling the CTRL keyboard shortcuts -> @keydown.ctrl.b.prevent could observe the key but not stop QEditor toggling bold
  • fix(QTab/QRouteTab): allow cancelling the keyboard actions -> the event was emitted after the tab had already acted on it
  • fix(QEditor): commit a link only when the user asks for it -> opening the link toolbar used to wrap the selection in an <a href="https://"> straight away, leaving a broken link behind if you walked away (#18289)
  • fix(QEditor): keep the link editor open when the command sits in a toolbar dropdown (#17920)
  • fix(ui): add validation for the q-editor link
  • fix(QMenu): keep popups open on clicks inside a fullscreen-detached child (#18512)
  • fix(QMenu): follow the anchor through a fullscreen detach -> the menu stayed open at stale coordinates, painted over by the fullscreen element (#18513)
  • fix(useFullscreen): keep focus and caret through the detach/restore moves -> a caret placed in a QEditor before entering fullscreen was lost (#17843)
  • fix(QMenu/QTooltip): follow an anchor still moving during the enter transition -> the popup no longer lands at a stale spot and snaps into place at transition end
  • fix(QDialog/QMenu): don't throw on focus when the inner ref holds a non-Element stub (#12780)
  • fix(QDialog/QTooltip): resolve fall-through attrs on the render path -> a dynamic :class on an open QDialog never reached the portal root, and a dynamic id went stale on QTooltip
  • fix(QDialog): stop fighting the iOS scroll position while the viewport is zoomed
  • fix(QScrollArea): stop trapping fixed-positioned children -> a position: fixed descendant anchored itself to the scroll area and was clipped there (#12109)
  • fix(QSelect): display falsy (non-null) selected values with fill-input (#17763)
  • fix(QInput): restore the textarea inline styles when autogrow is toggled off (#17535)
  • fix(QFab): stop forwarding the FAB's own props to the trigger button -> every FAB shipped an anchor carrying invalid DOM attributes like direction="up" and labelposition="right"
  • fix(ui): QFab/QFabAction internal top/bottom label was never stacked -> an internal label-position="top"/"bottom" laid icon and label out in a row instead of a column
  • fix(QColor): drop the dead hideSelection prop from the alpha slider
  • fix(QTree): align dense virtual scroll root leaves with the nested layout
  • fix(ui): QVirtualScroll SSR-rendered padding mismatching the client's -> no more "Hydration style mismatch" warning in every SSR app rendering a QVirtualScroll
  • fix(ui): Morph SSR hydration class false-positive
  • fix(ui): Screen plugin -> honor the documented config > screen > bodyClasses key on SSR, which never emitted its body class into server-rendered HTML
  • fix(ui): widen useQuasar()'s inject() fallback past the no-instance case
  • fix(ui): actually print the UMD missing-Vue guidance error -> it was dead code behind a cryptic "Cannot read properties of undefined" throw

Other

  • Test infrastructure: SSR hydration coverage for every component and directive (with a Quasar-config and icon-set install axis), a hydration sweep over the playground, a UMD suite running the built bundles under both global Vue builds, a cross-engine regression sweep page, and an [Accessibility] category in the Specs workflow

Donations

Quasar Framework is an open-source MIT-licensed project made possible due to the generous contributions by sponsors and backers. If Quasar is useful in your workflow and you want to support ongoing maintenance, please consider the following:

Don't miss a new quasar release

NewReleases is sending notifications on new releases.