almasaeed2010/adminlte
AdminLTE is a popular MIT-licensed admin dashboard template built on Bootstrap 5.3 with vanilla JavaScript (no jQuery). Fully responsive, highly customizable, and easy to use for web appsβfrom mobile to desktop. Live demo and docs available.
.app-main carries padding-bottom: $grid-gutter-width * .5 β breathing room above the fold on screen, trailing whitespace on paper. 12px of it was enough to push a page that otherwise ended 1px inside the second sheet onto a third, empty one; UI/ribbons.html was the case in the demo set, printing three sheets for two sheets of content. The padding is now cleared in print, along with the negative margin-bottom on .app-content-bottom-area that exists only to cancel it. One page of the 90 in dist/ changes page count; nothing else moves (#6113, reported by @johnnyq).A printed page is a document again β the header, sidebar and footer no longer print. v3 left all three off the paper; v4 printed them, and the result on a real document was the invoice page in the report: 200px of every sheet spent on a menu the reader cannot click, the invoice squeezed into what was left (its own table wrapping "Pro plan subscription" across three lines), and the sidebar's 85 menu items running past the foot of the first sheet β so a one-page invoice printed as two pages, the second one all but empty. The content now gets the full width of the paper. Print the whole shell again with data-lte-print="app" on <html> or <body>, or bring back one piece of it with Bootstrap's own utilities β .d-print-flex for the header, which is a navbar, .d-print-block for the sidebar and footer. .d-print-none still hides anything you don't want on paper, in either mode (#6113, reported by @johnnyq).
data-lte-print is now matched per token, so data-lte-print="plain app" sets both. data-lte-print="plain" is unaffected.
The layout kept its viewport-sized dimensions in print. .app-wrapper carries min-height: 100vh and max-width: 100vw, and neither was cleared for print even though the print block deliberately rewrites that same element's grid. Paper has no viewport to fill: the wrapper reserved a full sheet however short the page was, which is why the printed invoice put its footer at the foot of a sheet of empty grey with the content in the top third, and why anything following the wrapper opened a new page. Both are now reset in print, together with the max-width: 100vw on the header, main and footer (#6113, reported by @johnnyq).
The print block set display on elements it did not need to. Forcing display: block on .app-sidebar was what made .d-print-none unable to hide it before 4.6.0, and the same shape would have made .d-print-* unable to show it now. Printing the shell is a matter of not hiding it, so the rules that hide it are simply scoped out instead, and nothing in the print block forces a display value on the header, sidebar or footer any more.
npm run production printed six Sass deprecation warnings. The palette sheets used the ternary if() in three places β lte-needs-aa() in colors/_variables.scss and the text-colour pick in colors/_primary.scss, twice β and Sass 1.95.0 deprecated that syntax in favour of the CSS if(). Both adminlte-colors.scss and adminlte-colors-v3.scss pull those files in, hence six warnings for three call sites. They are now plain [@if](https://github.com/if) statements rather than Sass's suggested if(sass(...): ...; else: ...), which would need Sass >= 1.95 of anyone compiling AdminLTE from source. Compiled output is byte-identical (#6111, reported by @lfiorini).
For the record, Bootstrap's own if() calls warn too β 82 of them across bootstrap/scss β but css-compile already passes --quiet-deps, so nothing under node_modules reaches the console. Every warning in that report was AdminLTE's own.
dist/ is byte-identical, so this changes nothing for consumers.markdown.rehypePlugins. Astro 7.2.4 deprecates the top-level remarkPlugins / rehypePlugins / remarkRehype keys in favour of an explicit markdown.processor, so the bump above added a fresh warning to the very command #6111 was about. The responsive-tables plugin now goes through unified() from [@astrojs](https://github.com/astrojs)/markdown-remark β the same remark/rehype pipeline, now a direct dev dependency. All 90 built pages are byte-identical, .table-responsive wrapping included.nanoid bumped to 3.3.18 in the lockfile β GHSA-2v37-7h3g-55p8 (high), a transitive dev-only dependency where a custom generator could loop forever on size: 0. npm audit now reports zero vulnerabilities; nothing shipped in dist/ was ever affected.typescript-eslint still declares peerDependencies.typescript: ">=4.8.4 <6.1.0" and [@astrojs](https://github.com/astrojs)/check accepts ^5 || ^6, so the toolchain still cannot move.<table> with no scroll container, so anything wider than three columns overflowed the viewport β 265px of it on the Colors page. They are now wrapped in .table-responsive with a tab stop, exactly like the hand-written tables next to them, so the table scrolls inside the column and stays keyboard-reachable. Desktop layout is unchanged.Every page scrolled sideways on a phone. At 390px the header's right-hand item row (messages, notifications, fullscreen, user menu) is a little wider than the viewport at Bootstrap's default 1rem nav-link padding, so document.scrollWidth came out 22px past clientWidth on all 90 demo pages β a horizontal scrollbar on every screen. Below sm the header now halves --bs-navbar-nav-link-padding-x; from sm up nothing changes (the user menu keeps its 182px width and its label). Measured 412/390 before, 390/390 after, desktop unaffected.
Worth noting for anyone who checks this themselves: measure scrollWidth > clientWidth, not > window.innerWidth β the layout viewport grows to match the overflow, so the innerWidth comparison silently passes.
[email protected]. Cloudflare's Email Obfuscation treats any package@version in a text node as an email address and rewrites it as a mailto link β so every version-pinned snippet on the live docs pages was unusable: 31 of them on Integrations (Flatpickr, Tom Select, Quill, Chart.js β¦), 8 each on Introduction and Deployment, 7 on Getting Started. The docs pages now carry <!--email_off--> markers, the same fix docs.adminlte.io already used.package.json. Introduction, Getting Started, Deployment and Colors still pinned [email protected], eight releases after that was current. sync-version.mjs now stamps [email protected] in the docs sources alongside the SCSS/TS banners, so npm version keeps them honest. A deliberate floating admin-lte@4 (the README quick start) is left alone.dist/ is byte-identical, so this changes nothing for consumers.typescript-eslint still declares peerDependencies.typescript: ">=4.8.4 <6.1.0" and [@astrojs](https://github.com/astrojs)/check accepts ^5 || ^6, so the toolchain cannot move yet. It will be picked up when those two allow it.data-lte-primary. Both palette sheets shipped a literal $pagination-focus-box-shadow-width in their compiled output: Sass does not evaluate $variables inside a custom property value β it emits them verbatim rather than failing β so the typo compiled cleanly, reached the browser as text, and made the declaration invalid. --bs-pagination-focus-box-shadow then resolved to nothing and keyboard focus on .page-link became invisible, a keyboard-accessibility regression introduced in 4.6.0. The value is now built from Bootstrap's focus-ring tokens (the variable it named never existed; the right one is $focus-ring-width) and follows the theme colour. A unit test now scans the compiled output of all three sheets for unresolved Sass variables, since compilation cannot catch this class of typo (#6109, reported by @johnnyq).data-lte-contrast="aa" β WCAG AA text on the AdminLTE 3 palette, without changing a single colour. The v3 sheet reproduces v3's YIQ text rule faithfully, and that rule assigns white text to eight colours that do not reach 4.5:1 for normal text: teal (2.13:1), cyan (3.04), green (3.13), olive (3.51), lightblue (3.67), fuchsia (3.79), pink (3.82) and blue (3.98). The attribute β on <html>, <body> or any container β flips exactly those eight to the ink that passes (5.28:1 to 9.86:1), leaving every background byte-identical, and covers everywhere the palette paints text on its own colour: .text-bg-*, .bg-gradient-*, .card-* headers, .direct-chat-* and the button, badge and pill text data-lte-primary produces. It has to be true black: AdminLTE 3's own dark ink #1f2d3d clears 4.5:1 on only two of the eight, so the colours that already carry dark text (lime, orange, yellow) keep #1f2d3d and are left alone. Rules are emitted per colour only where the palette's own rule falls short, so the designed palette β every colour AA with white by construction β emits nothing and the switch costs it nothing. UI β Colors and Theme Customize gained a toggle that also re-pairs a flipped sidebar or header with data-bs-theme="light" so its nav links follow (#6110, reported by @johnnyq).data-lte-primary gave Bootstrap's info white text on the v3 sheet. The primary presets applied the active sheet's contrast rule to Bootstrap's own theme colours too, and v3's YIQ test puts #0dcaf0 at 149.8 against a threshold of 150 β one tenth of a point from dark text β so data-lte-primary="info" produced white on cyan at 1.96:1 while .btn-info on the same page stayed black. Bootstrap's theme colours now keep Bootstrap's own contrast rule; the palette colours keep the sheet's.data-lte-print="plain" β print a page as a document, not as a copy of a screen. Two rules in the print stylesheet restyle content unconditionally: the target of every external link is printed after it (a[href^="http"]::after), and buttons are outlined in black. They are the HTML5 Boilerplate conventions and they suit printing a web page, but they are wrong for an invoice, quote or work order that goes to a customer β Pay online (https://pay.example.com/inv/12345?token=β¦) is not what anyone wants on paper. The attribute turns both off, on <html>, <body> or any single container, while text stays black on white so nothing becomes unreadable. Note that .d-print-none could not have served as the opt-out: it hides the element, and these rules restyle elements that are meant to print (#6108, requested by @johnnyq). Documented as the βPrinting a page as a documentβ recipe..nav-link, and the sidebar prints by design (since 4.0.0), so a printed page came out with a black rectangle around all 85 menu items. The outline now applies to .btn only; nav links keep the ink-saving black-on-transparent treatment and print as ordinary text (#6108).--bs-primary on :root β the one approach that leaves .btn-primary blue, which is what led to #6107. It now gives the two that work: $primary in Sass, or data-lte-primary with the palette sheet.primary β data-lte-primary. Bootstrap compiles $primary into its components, so .btn-primary ships with --bs-btn-bg: #0d6efd rather than var(--bs-primary): overriding the token repainted the utilities and left every button, pagination, checkbox and focus ring blue, which is why a coloured skin looked half-applied. Both colour sheets now rewire those components to variables and point them at a colour with one attribute β <html lang="en" data-lte-primary="teal"> β covering .btn-primary / .btn-outline-primary (hover, active and disabled shaded exactly as Bootstrap shades its own), .btn-link, links and .link-primary, the --bs-primary token trio, .nav-pills, .pagination, .progress-bar, .list-group-item.active, .dropdown-item.active, .accordion, .form-control / .form-select focus, checkboxes, radios, switches, .form-range, --bs-focus-ring-color, and AdminLTE's .card-primary, .direct-chat-primary and .toast-primary β in both colour modes, with Bootstrap's dark-mode link tint. Nothing changes without the attribute, and it works on any container, so one page can carry more than one primary. Bootstrap's own theme colours are presets too (data-lte-primary="danger"), a brand colour outside the palette needs only --bs-primary (hover shades are then derived with color-mix() where supported), and $lte-palette-custom: ("brand": #hex) generates a full preset in Sass. Every skin preset now names the primary that goes with it, so applying a skin on UI β Colors or Theme Customize recolours the buttons too; both pages gained a picker and a live preview, and the Colors docs page a section (#6107, requested by @johnnyq).d-print-none did nothing on the sidebar and the app wrapper. The print layout fix from 4.0.0 set display: grid !important on .app-wrapper and display: block !important on .app-sidebar, and β being later in the cascade and equally !important β beat Bootstrap's .d-print-none, so those two elements could not be hidden for printing. Both rules are now scoped with :not(.d-print-none): the print layout still applies to the standard layout, and the display utilities win where a page asks them to (#6106, reported by @johnnyq)..skip-link:hover painted var(--bs-primary-emphasis), which Bootstrap does not define (it emits --bs-primary-text-emphasis), so the background computed to transparent and left white text on the page background. It now uses the emphasis token β dark in light mode, light in dark mode β with the body background for the text.rgb(5.647%, 40.784%, 39.216%) (cleancss leaves values inside custom properties alone), which pays for a good part of the new feature: adminlte-colors.css is β 8 kB gzipped and adminlte-colors-v3.css β 9.3 kB. adminlte.css is unchanged.npm run palette -- --check and the unit tests also verify that every skin's primary exists in its own palette and that both sheets emit the rewiring rules and a preset per colour.dist/css/adminlte-colors-v3.css. A second opt-in sheet for everyone who preferred the original colours: the 18 AdminLTE 3 colours (lightblue, navy, olive, lime, fuchsia, maroon, blue, indigo, purple, pink, red, orange, yellow, green, teal, cyan, gray, gray-dark) with the values from the 3.2 build and v3's text-colour rule (Bootstrap 4's YIQ test β white on lightblue, olive, tealβ¦, dark on lime, orange, yellow), emitting exactly the same class families as the designed palette. Load one sheet or the other; they share class names. Built from the same Sass β new $lte-palette-contrast: "wcag" | "yiq" knob (with $lte-palette-yiq-threshold, $lte-palette-yiq-text-dark/-light) β so $lte-palette-custom and $lte-palette-aliases work with it too. Twelve AdminLTE 2 skin presets (skin-blue, -black, -purple, -green, -red, -yellow and their -light variants) in v4 terms. UI β Colors and Theme Customize gained a palette switch that swaps the stylesheet live and shows that palette's tiles, presets and pickers; the Colors docs page documents both. npm run palette -- --check and the unit tests keep the v3 map and its metadata in step (#6103, follow-up for @johnnyq)._scripts): it sets ApexCharts' global default theme.mode from <html data-bs-theme> before any chart is created, keeps the chart background transparent, and watches the attribute so every chart with a chart.id follows the toggle, the OS preference in auto mode, or your own code. All demo charts got ids. Documented as the βCharts that follow dark modeβ recipe (#6105, reported by @Yusk03).dist/css/adminlte-colors.css) β an opt-in stylesheet (β 5 kB gzipped; adminlte.css is unchanged) that adds fourteen colours β orange, amber, olive, teal, sky, indigo, violet, fuchsia, pink, and the chrome neutrals navy, steel, slate, graphite, midnight β as --bs-* tokens plus every family Bootstrap generates for its own theme colours: .bg-*, .text-bg-*, .text-*, .border-*, .link-*, .bg-gradient-* (also for the eight theme colours, as in v3), .card-* / .card-outline, .callout-*, .direct-chat-*, with the subtle/emphasis tokens for dark mode and a hover shade for .btn.bg-*. The palette is generated, not picked: in OKLCH, each chromatic colour is the lightest value whose white text still reaches 4.6:1 (WCAG AA), hues sit β₯ 17Β° from every Bootstrap theme colour and β₯ 20Β° from each other, chroma is capped at parity with primary, and the five neutrals are cool-tinted and sit at the lightness steps popular admin kits use for sidebars. scripts/palette.mjs regenerates it (npm run palette) and checks it (--check); a unit test keeps the Sass map and the demo data in step. Add your own with $lte-palette-custom: ("brand": #hex), replace the set with $lte-palette, restore v3 names with $lte-palette-aliases: ("lightblue": "sky", "maroon": "pink"). Documented on the new Colors page (docs/colors.html) β palette, class families, 17 skin presets in four families (light, semi-dark, full dark, coloured & gradient) each with a layout thumbnail, widget-set presets, brand-colour recipe, design rules with a hue/chroma plot β and demonstrated on UI β Colors (UI/colors.html), which recolours its own sidebar and header live from thumbnail presets, shows the four dashboard sets and every component, and has a brand-colour checker that reports contrast, proposes a same-hue value that passes, and prints the Sass snippet (#6103, requested by @johnnyq).mixins/_search-field.scss): a quiet pill with the icon inside as the submit button, coloured only from theme-relative tokens (--bs-emphasis-color-rgb, --bs-body-color), so it reads as soft glass on any coloured or dark header / sidebar and soft grey on a white one, instead of a boxed .input-group with an outline button that turned near-black under data-bs-theme="dark". The header field widens slightly on focus; below md an icon links to the search page. New variables $lte-search-field-* (width, focus width, font size, padding, icon space); the Main Header docs snippet, which still showed v3's inert data-widget="navbar-search", now shows the real markup.generate/theme.html) offers the palette in its sidebar / navbar / footer pickers under "Extended palette", gained a preset row with the same thumbnails, reflects the page's current state on load, and defaults the colour mode to dark when a palette colour is picked; its three duplicated scripts are one controller..sidebar-wrapper was sized with a hard-coded 100vh β header height, which assumed only the brand sat above it; the SidebarSearch field added in 4.3.0 sits between the brand and the wrapper (on purpose, so it stays put while the menu scrolls), so the wrapper overflowed the sidebar by exactly the filter's height and the last menu items were cut off. .app-sidebar is now a flex column: brand and filter keep their size (flex: 0 0 auto), .sidebar-wrapper fills the rest (flex: 1 1 auto; min-height: 0) and scrolls β in fixed layouts, the fixed-header-only case, and the off-canvas sidebar. Anything else placed above or below the menu now sizes correctly too, without a height calc (#6104, reported by @dfsmania).64px of it is readable at the default size. .ribbon-lg and .ribbon-xl give more room. Documented on the Miscellaneous components page.pages/gallery.html) β a filterable media library: responsive tile grid, per-item action menus, a live result count and an empty state. It carries no image library at all β the tiles are a Bootstrap .ratio box plus .object-fit-cover, and the category filter is ~25 lines of page-local script, so nothing is added to adminlte.js and no CDN dependency is imposed on anyone who just wants the grid. A lightbox stays deliberately out of scope; it is the part that needs a dependency.pages/search-results.html) β typed results (page / user / file / project, each with its own icon and badge) over a tabbed type filter, plus a refine panel, related searches, pagination and a worked "no results" state. The navbar search now posts to it with name="q" instead of action="#", so the search added alongside SidebarSearch has a real destination.bi-translate dropdown listing five locales with the current one marked via .active + aria-current, and hreflang on each item. It is markup only, by design; the new "A language switcher wired to a real locale" recipe in the docs covers the four things that separate a working switcher from set dressing β real per-locale links rather than # plus a click handler, the locale living in the URL or a cookie rather than localStorage (the server has to know it before it renders), marking the current language for assistive tech, and serving adminlte.rtl.css alongside dir="rtl" when switching to an RTL locale.SidebarSearch plugin β a filter field above the sidebar menu (data-lte-toggle="sidebar-search" on an input, optional data-lte-target for the menu). Typing hides non-matching entries and expands whatever submenu holds a match; a group whose own name matches keeps its entire subtree, so expanding it is never a dead end. Clearing the field β ESC, the native clear button, or clear() β restores every entry and each submenu's previous open state, including the inline display Treeview's slide animation leaves behind. Entries are hidden with the hidden attribute rather than a class, so they leave the accessibility tree too. Fires filtered.lte.sidebar-search with { query, matches }; both listeners are delegated on document, so a sidebar rendered after load needs no re-initialisation. Wired into every demo and docs page, and documented at docs/javascript/sidebar-search.html.role="search" form with a visually-hidden label, shown from the md breakpoint up, posting to the new search results page..ribbon-wrapper + .ribbon, sizes .ribbon-lg / .ribbon-xl) β the corner banner from v3, rebuilt. The wrapper clips the banner and the banner is centred on it before rotating, so the whole geometry follows one custom property (--lte-ribbon-size) and stays correct at every scale; the clip inherits the parent's corner radius, so it follows a rounded card instead of cutting a square out of it. Colour comes from a .text-bg-* utility and long labels truncate rather than breaking the shape. rtlcss mirrors both the corner and the rotation, so RTL needs no extra markup. New demo page at UI/ribbons.html..user-block, .post, .widget-user, .widget-user-2, .description-block) β the profile cards and activity-feed blocks from v3, keeping their v3 class names so migrating markup works unchanged, but laid out with grid and flex rather than floats and coloured with Bootstrap utilities. .user-block renders avatar, name and meta from flat markup with no wrapper element. New demo page at widgets/social.html.UI/general.html β the three Bootstrap components the components showcase did not yet cover.docs/components/card.html, docs/components/miscellaneous.html) β between them they document every AdminLTE component that ships CSS but had no reference until now: cards (anatomy, colour variants, states, tabbed cards, Sass variables), callouts, ribbons, small box, info box, timeline, the social/post widgets, progress-bar sizes, themed toasts and the image-size utilities.dist/css/adminlte-select2.css) β an opt-in, CSS-only sheet for projects that can't drop Select2 yet. AdminLTE still bundles neither Select2 nor jQuery and nothing is added to adminlte.css; load this one extra stylesheet after select2.css and a Select2 you bring yourself inherits Bootstrap 5.3's form metrics (.form-select-sm / -lg on the original <select> are honoured), focus ring, .is-invalid / .is-valid states, input-group corner rounding, badge-style multi-select pills, and data-bs-theme dark mode β including the body-appended dropdown. It also raises the dropdown above .modal, which removes the usual need for dropdownParent. Source lives in src/scss/compat/_select2.scss for anyone compiling their own Sass; RTL and minified variants are generated alongside the rest of the CSS (#6097, requested by @saddam2512).ajax β load + loadThrottle, templateResult β render.option, select2("destroy") β ts.destroy(), and so on).src/html/components/docs/faq.mdx. The docs FAQ has been rendered from the typed sections array in src/html/pages/docs/faq.astro since the accordion rewrite, and nothing imported the .mdx any more β but two later commits kept editing it, so those edits silently did nothing. It is the only docs .mdx with no importer; every other one is rendered by its matching pages/docs/*.astro.forms/advanced.html is now the real pattern, not a setTimeout sketch: request debouncing via loadThrottle, in-flight cancellation via AbortController, and page-by-page loading through the virtual_scroll plugin against a 72-record stub whose { results, next } response shape matches a real endpoint β swap the stub body for a single fetch() and it works unchanged. The single select also gained the clear_button plugin.UI/general.html, UI/ribbons.html, widgets/social.html, pages/gallery.html, pages/search-results.html and docs/components/miscellaneous.html. Unit tests are up to 62 (from 51). The two new pages needed three Bootstrap-default contrast fixes the gate caught: .text-secondary under 4.5:1 on the tertiary page ground, the nav-tab link blue under it on the same ground (the tabs moved onto the card surface, which is the documented .card-tabs shape), and .btn-outline-secondary under it on a card footer.UI/general.html are demonstrated where they are legible. The warning, info and light outline variants draw their text in a light colour that falls below 4.5:1 on a light surface β adding the page to the axe gate surfaced it. They now sit on a dark panel, with a note saying why, which is also the guidance Bootstrap gives for them.adminlte.min.css 42.5 β 43.5 kB and adminlte.min.js 7.5 β 8.5 kB gzip (and the matching RTL/ESM/unminified entries).index2.html pointed at pages/examples/invoice.html; the invoice page is at pages/invoice.html. Caught by a new link-resolution pass over every built page.#navigation no longer resolves to the app header. ensureSkipTargets() looked its two skip-link targets up with a single selector list each (#navigation, nav, [role="navigation"]), but a selector list resolves in document order rather than by which selector matched β so on every demo page, where the header <nav> precedes the sidebar menu that already carries id="navigation", the header was stamped with the id a second time. That left a duplicate id in the document, pointed the "Skip to navigation" link at the header instead of the menu, and silently redirected any application code doing document.querySelector("#navigation") to the wrong element. An element the page already gave the id to now always wins.forms/advanced.html demonstrates the recommended Tom Select and Flatpickr integrations (searchable single select, tag-style multi-select, option groups loaded on demand; date, date-time, range, and time pickers), and forms/editors.html demonstrates Quill 2 in both the snow (toolbar) and bubble (inline) themes. CDN assets are pinned to the versions recommended on the docs Integrations page and carry SRI hashes. Both pages are linked from the sidebar's Forms menu.layout/top-nav.html β a sidebar-less page whose primary navigation lives in the app-header as a plain Bootstrap navbar-expand-lg (collapse toggler on mobile, no PushMenu). No new CSS was needed: without an .app-sidebar in the markup, the app-wrapper grid's auto sidebar column collapses to zero width on its own.<html data-bs-theme="light|dark">, then the OS preference β so a theme rendered server-side from a cookie or a user record is no longer overwritten on load, and no longer lost the first time the OS preference changes. Applications that manage data-bs-theme themselves β including anyone using a custom Bootstrap theme name, which ColorMode cannot resolve β turn the module off entirely with <html data-lte-color-mode="off">: nothing is written on load, on a [data-bs-theme-value] toggle click, or when the OS preference changes, and the pre-paint snippet in <head> honours it too (#6084, reported by @cytech; #6093, thanks @dfsmania). The snippet now flags the values it resolves itself with data-lte-theme-resolved, so a computed theme is never mistaken for one the page declared.initialize() / teardown() lifecycle API for client-side rendered layouts: frameworks that build the layout after DOMContentLoaded into a persistent <body> (GWT and similar widget toolkits) can call adminlte.initialize() once the layout is attached β it tears down the previous cycle's listeners and re-runs every plugin's initialisation against the current DOM, and is safe to call repeatedly (#6083, thanks @themarioga). teardown() is the inverse, for apps that unmount the layout. Re-initialisation was hardened along the way: the accessibility module's per-field invalid listeners no longer stack across cycles, an initialize() call made while the document is still loading no longer suppresses the initial DOMContentLoaded pass, and calling initialize() from inside a lifecycle callback no longer recurses.eslint-plugin-astro 2 β 3 and eslint-plugin-unicorn 72 β 73, whose new single-line-block-comment-style rule prompted converting four one-line banner comments in src/ts/util/index.ts to line comments. Also Astro 7.1.6, ESLint 10.8, typescript-eslint 8.66, Sass 1.102, Rollup 4.62.4, Terser 5.49.2, PostCSS 8.5.25, happy-dom 20.11.1, [@astrojs](https://github.com/astrojs)/mdx 7.0.5, plus concurrently, fs-extra and globals. TypeScript stays on 6.0.3: 7.0 has shipped, but both typescript-eslint (peer >=4.8.4 <6.1.0) and [@astrojs](https://github.com/astrojs)/check (peer ^5 || ^6) still cap it, so installing it fails dependency resolution outright. Nothing in dist/ changed as a result. Supersedes #6085β#6092.base-component.ts and push-menu.ts (#6082, thanks @dfsmania), the adminlte-docs.scss banner is now version-synced by npm version along with the other source banners, and the README's screenshots were downscaled to WebP for faster cold-cache loads.<body class="sidebar-collapse"> had that state overwritten by PushMenu's responsive pass during initialisation, so a deliberately collapsed sidebar expanded itself on wide screens. The responsive pass now runs only when the sidebar was not already collapsed in the markup (#6076, thanks @dfsmania).docs/color-mode.html still carried the pre-4.1 inline toggler, which bound its own handlers to the same [data-bs-theme-value] buttons as the bundled module and persisted to a different localStorage key (theme instead of lte-theme). It has been removed β the page now uses the shipped ColorMode like every other page.dist/js/adminlte.esm.js (+ .min) ships alongside the UMD build, generated .d.ts files ship under dist/js/types/, and package.json gains module, types, and a full exports map (with sass/style conditions and ./dist/* + ./src/scss/* subpaths). import { PushMenu } from "admin-lte" now resolves natively in Vite/webpack and type-checks out of the box β previously the package shipped a single minified UMD file with no typings at all.getInstance(element), getOrCreateInstance(element, config?) and dispose(), backed by a per-element WeakMap registry (instances are garbage-collected with their elements β Turbo-safe). The data API now uses delegated document-level listeners, so toggles inside content inserted after page load (AJAX partials, Turbo Frames) work without re-initialisation. PushMenu is finally controllable programmatically via PushMenu.getInstance(sidebar).ColorMode module in the bundle: the light/dark/auto switcher (persisted in lte-theme, OS-preference aware, [data-bs-theme-value] data-API, changed.lte.color-mode event) is now part of adminlte.js. Applications no longer need to copy the demo's inline script; the demo pages now use the bundled module. Only the tiny no-flash snippet in <head> remains inline, by design.bootstrap declared as a peer dependency β the Sass source imports it, so [@use](https://github.com/use) "admin-lte/src/scss/adminlte" now works after a plain npm install admin-lte (npm installs the peer automatically). Documented the required Sass load-path setup.npm run production; plus npm run test-a11y β an axe-core check over key built demo pages that fails on serious/critical WCAG violations, with a dedicated GitHub workflow.tabindex="0" + role="log"), and muted footnote text uses text-body-secondary instead of the failing text-secondary.CustomEvents dispatched on the component's root element (the card, the nav item, the sidebar) β previously most were non-bubbling and card events fired on whatever was clicked, including the <i> icon. Animated actions gained cancelable "before" events (collapse/expand/remove.lte.card-widget, expand/collapse.lte.treeview, open/collapse.lte.push-menu) and their "after" events (collapsed, expanded, removed, opened, β¦) now fire when the animation completes, not when it starts. If you listened for card events on the tool buttons themselves, listen on the card or on document instead.adminlte-docs.css, loaded only by the docs pages. Together with the 4.0.4 dedup, adminlte.min.css is down to ~40.4 KB gzip (from 46.7 KB in 4.0.3); bundlewatch budgets tightened accordingly._bootstrap-variables.scss copy (which had to be re-synced by hand every Bootstrap release) is gone; AdminLTE's ~10 actual changes now live in a small _bootstrap-overrides.scss loaded before Bootstrap's own variables. Compiled CSS is byte-identical.--quiet); it silences only dependency warnings and the known [@import](https://github.com/import) deprecation (--quiet-deps --silence-deprecation=import), so new deprecations in AdminLTE's own code surface at build time. All deprecated global built-ins (map-get, map-keys) migrated to the sass:map module. The full [@use](https://github.com/use) module-system migration is intentionally deferred until Bootstrap ships module-system Sass (Bootstrap 6) β Bootstrap 5's partials are designed around [@import](https://github.com/import)'s shared global namespace and cannot be loaded individually via [@use](https://github.com/use).ColorMode module (the old copy-paste script it showed used a stale storage key); tsconfig.json no longer carries the invalid "root": true option.forgot-password.html since 4.0.0, but the page didn't exist (dead link in every deployed demo). Now shipped and listed in the sidebar under Examples βΊ Version 1.aria-expanded, stamped on init and kept in sync on open/close.files allowlist instead of the .npmignore denylist. Stray local files can no longer leak into the tarball (4.0.2 shipped an untracked working file this way), and the demo/docs HTML β which SECURITY.md advises never to deploy β is no longer published to npm. Unpacked size drops from 12.7 MB to 9.0 MB (177 β 95 files). Also declares engines: node >= 20._docs.scss was imported from both adminlte.scss and parts/_core.scss, and Sass [@import](https://github.com/import) duplicates output β ~23 KB of dead weight in each of the four dist stylesheets.show.bs.modal (capturing on shown stored an element inside the modal, so closing dropped focus to <body>); [autofocus] is respected#live-region, and sidebar overlay are now reused when a restored <body> snapshot already contains them β previously they accumulated one copy per navigation.remove() now actually removes the card from the DOM after the animation (it only hid it, so hidden form fields kept submitting); clicking the collapse toggle mid-animation reverses it instead of being swallowed; widget events now dispatch on the toggle button itself rather than a clicked <i> icon; minimize() cleans up its inline styles.<ul> (never true), so open() on an already-open item slid its own menu shut.matchMedia on the actual breakpoint crossing β mobile URL-bar/keyboard resizes and same-side width changes no longer re-expand a sidebar the user collapsed; the default breakpoint (991.98) now matches the CSS convention, fixing a 992px off-by-one.--bs-light-bg, which doesn't exist in Bootstrap 5.3 (now --bs-tertiary-bg).data-widget="navbar-search" has no implementation in v4); every page now has exactly one <h1> (page titles were <h3>); breadcrumbs are wrapped in <nav aria-label="breadcrumb">; all icon-only buttons (card tools, topbar toggles) have aria-labels; auth forms have real <label>s and <main> landmarks; dated "Google+" copy updated.turbo:load, so PushMenu, TreeView and the other JS components keep working after Turbo swaps the <body> on in-app navigation (previously they went dead after the first link click). Each init cycle uses an AbortController whose signal is aborted on turbo:before-render, so the window/document-level listeners are torn down before re-init instead of stacking up on every navigation. (#563, #5890 β diagnosed and prototyped by @MarkDaleman in #6058)maximized/minimized events are now driven by the native fullscreenchange event instead of the request/exit calls. The UI no longer flips when a fullscreen request is denied (permissions policy, missing allowfullscreen, lost user gesture), and it now stays in sync when the user exits with ESC or F11. (builds on @webgo-oss's report in #6055)id and a name now receive a stable, generated error-message id instead of colliding on a shared -error id and appending a new orphaned error node on every re-validation. (#6055, reported by @webgo-oss)npm run production pipeline (lint + Astro build + bundlewatch) passes and npm audit remains at 0 vulnerabilities. (supersedes Dependabot PRs #6065β#6074)npm run production no longer fails β the dev-only scripts/social-preview.mjs tripped 9 ESLint errors when building from source. The script is now lint-clean. (reported by @lfiorini)django.contrib.admin, {{ form }} rendererscripts/social-preview.mjs, dev-only β excluded from the npm package).accessibility.js no longer assigns role="navigation" to <ul>/<ol> elements, which broke list semantics and failed the Lighthouse accessibility audit. (reported by @lfiorini)axios pinned via npm overrides to clear a transitive advisory. npm audit remains at 0 vulnerabilities.docs/integrations.html) β copy-paste install snippets for Flatpickr, Tom Select, noUiSlider, Pickr, IMask, Dropzone, FilePond, Quill, EasyMDE, Toast UI Editor, ApexCharts, Chart.js, Tabulator, FullCalendar, SortableJS, GLightbox, and a comparison of icon libraries. AdminLTE doesn't bundle these β the page shows how to drop them in.prefers-color-scheme integration. Wiring JS shipped globally in _scripts.astro so the toggle works on every demo page automatically.forms/general.html into three focused pages β forms/elements.html (inputs, selects, checks/radios/switches, ranges, floating labels), forms/layout.html (horizontal, inline, sizing, width), and forms/validation.html (Bootstrap native validation + tooltip variant).bi-circle-fill-half-stroke (a FontAwesome class name); replaced with the correct Bootstrap Icons class bi-circle-half..table-head-fixed now respects dark mode. Hardcoded background-color: $white swapped for var(--bs-body-bg) so the sticky header tracks the active colour scheme.iconMaximize.style.display = 'block' directly, overriding the icon library's natural display value (visible on FontAwesome). Now toggles Bootstrap's .d-none utility class. Markup updated accordingly..fixed-header. New companion rule pins the sidebar sticky on sidebar-expand-* breakpoints, giving the menu its own scrollbar instead of scrolling with the page.index2.html no longer flickers on browser zoom. Pinned an explicit height: 350 to break the ApexCharts ResizeObserver feedback loop most visible on Edge.$btn-close-filter, $carousel-control-icon-filter$btn-close-white-filter, $carousel-dark-indicator-active-bg, $carousel-dark-caption-color, $carousel-dark-control-icon-filter$modal-content-color (null vs Bootstrap's var(--bs-body-color))baseUrl removed, moduleResolution: "bundler" set to clear deprecation warnings).eslintrc.json (ignored since ESLint 9), along with three deps that only it referenced: eslint-plugin-import, eslint-config-xo, eslint-config-xo-typescript. Added explicit deps for [@eslint](https://github.com/eslint)/js, globals, and typescript-eslint (now required to be declared since ESLint 10 stopped resolving them transitively).stylelint-config-twbs-bootstrap peer pinned via npm overrides so it accepts stylelint 17 ahead of an upstream release. This removes the need for --legacy-peer-deps on npm install.yaml pinned to ^2.9.0 via overrides to clear the dev-only stack-overflow advisory chain through [@astrojs](https://github.com/astrojs)/check. npm audit now reports 0 vulnerabilities.sidebar-without-hover body class that prevents collapsed mini sidebar from expanding on hover (#5837)
layout/collapsed-sidebar-without-hover.htmlenablePersistence now defaults to false (was true). Opt in via data-enable-persistence="true" on .app-sidebar. BREAKING CHANGE.fixed-footer and .layout-fixed (#5805)overscroll-behavior: contain to prevent page scroll when scrolling sidebar on mobile (#5864)calc() syntax in _bootstrap-variables.scss to match Bootstrap 5.3 format (#5951)removeComments: true to tsconfig.json for smaller unminified bundles (#5953)enablePersistence option (default: true)lte.sidebar.statesetup-node action from v3 to v4 across all workflowsFORCE_COLOR: 2 environment variable to codeql.yml for consistency-d flag to -r for recursive directory zipping:scope > selector to only target direct card-body/footer children.eslintignore file
eslint.config.jsshx
copy-assets script to use shx mkdir and shx cp commandsflatten-build script to use shx cp and shx rm commandsshx package as dev dependency for cross-platform shell command supporttransition: none and opacity: 1 instead of display: block/assets/img/user.jpg caused 404 errors on mobileThis release resolves critical production deployment issues and ensures consistent behavior between development and production environments across different deployment scenarios.
./css/ for root, ../css/ for sub-pages)rtlcss plugin interference with LTR builds
rtlcss now only runs during RTL-specific builds (NODE_ENV=RTL).rtl.css files for right-to-left language support/assets/img/...) to relative pathssidebar-open class to all layouts for consistent styling.sidebar-menu .nav-link { width: 100%; } for better UXintegrity attributes from all CDN resources
crossorigin="anonymous" for security while removing brittle SRI checkssrc/html/public/ for hot-reloadingdist/css/ and dist/js/ then flattens structuredist/ folder to repository
prefer-global-this and prefer-string-slice linting issues/themes/v4/ or similar pathsnpm run dev - all features verified workingnpm run production - 37 pages built successfully, 0 errorspython3 -m http.server - full functionality confirmed# Build for production
npm run production
# Deploy via FTP (upload entire dist/ folder contents)
# Or serve locally for testing
cd dist && python3 -m http.server 8080
dist/ contents to public_html/ or equivalentdist/ contents to public_html/admin/ or similardist/ in your hosting platformAutomatic Updates (No Action Required):
Recommended Actions:
dist/ structurenpm run dev) and production buildsBreaking Changes:
This release modernizes AdminLTE to ES2022 standards and implements comprehensive WCAG 2.1 AA accessibility compliance, making it one of the most accessible admin templates available.
.browserslistrc for ES2022 compatibility
AccessibilityManager class (src/ts/accessibility.ts)
prefers-reduced-motion for users with vestibular disorderssrc/scss/_accessibility.scss.sr-only and .sr-only-focusable classeslight/dark)ACCESSIBILITY-COMPLIANCE.md: Comprehensive documentation
// Initialize accessibility features
const a11y = initAccessibility({
announcements: true, // Enable live announcements
skipLinks: true, // Add skip navigation links
focusManagement: true, // Enhanced focus handling
keyboardNavigation: true, // Full keyboard support
reducedMotion: true // Respect motion preferences
});
// Public methods
a11y.announce('Content updated', 'polite');
a11y.focusElement('#main-content');
a11y.trapFocus(modalElement);
a11y.addLandmarks();
Automatic Features (No Action Required):
Optional Enhancements:
<!-- Add accessibility-enhanced form -->
<form class="needs-validation" novalidate>
<div class="mb-3">
<label for="email" class="form-label">Email <span class="required-indicator">*</span></label>
<input type="email" class="form-control" id="email" required>
<div class="invalid-feedback" role="alert"></div>
</div>
</form>
<!-- Use accessible color classes -->
<div class="alert alert-success text-accessible-success">Success message</div>
For Developers:
import { accessibilityUtils } from './adminlte.js'.sr-only, .touch-target, .text-accessible-*This release represents a complete modernization of the AdminLTE codebase, bringing it up to current standards with the latest tooling, dependencies, and best practices.
npm start - Quick development server launchnpm run build - Convenient build command for developmentnpm run production with bundlewatch integration"type": "module" to package.json.eslintrc.json to modern eslint.config.jsoutput: 'static' for proper static site generationMajor version upgrades:
astro: 4.15.12 β 5.10.0eslint: 8.57.1 β 9.29.0[@typescript-eslint](https://github.com/typescript-eslint)/eslint-plugin: 7.18.0 β 8.34.1[@typescript-eslint](https://github.com/typescript-eslint)/parser: 7.18.0 β 8.34.1eslint-config-xo: 0.44.0 β 0.47.0eslint-config-xo-typescript: 4.0.0 β 8.0.1eslint-plugin-astro: 0.34.0 β 1.3.1eslint-plugin-unicorn: 52.0.0 β 59.0.1stylelint-config-twbs-bootstrap: 15.1.0 β 16.1.0Minor/patch updates (50+ packages):
bootstrap: 5.3.3 β 5.3.7sass: 1.78.0 β 1.89.2typescript: 5.6.2 β 5.8.3prettier: 3.3.3 β 3.5.3For Users:
For Developers:
npm start command replaces manual npm run deveslint.config.js (old .eslintrc.json removed)Recommended Actions:
npm install to get latest dependenciesnpm start for developmentnpm run production for production buildsHow can I help you explore Laravel packages today?