NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
Packagist · #1943 most downloaded on Packagist
AdminLTE - admin control panel and dashboard that's based on Bootstrap 4
Last release 8 days ago
30 Sep 2026
Release timing varies
gaps range from 2 weeks to 8 months
Some releases are documented
notes for 23 of the last 60 stable releases
Nothing withdrawn
no release was ever pulled
12 years old
99 releases · first in 2015
Fix brand logo to stay centered on mini sidebar mode by @dfsmania in #6114
Full Changelog: v4.9.1...v4.10.0
The demo charts are drawn with Chart.js (MIT) instead of ApexCharts. From 5.2.0 ApexCharts is published under its own commercial "ApexCharts License" rather than MIT, and a template that thousands of projects copy into their products cannot carry that on to them. Every chart on Dashboard v1, v2 and v3 and on the charts showcase is rebuilt on Chart.js 4.5.1 with the same data, colours and sizes: area and line charts (gradient fills), grouped columns, doughnuts, the radial bar (concentric doughnut rings with the average in the middle), the mixed bar-and-line chart with two value axes, and the dashboard sparklines. Chart.js is loaded from jsDelivr with an SRI hash, only on the pages that draw charts, exactly as ApexCharts was — the admin-lte npm package has never depended on a chart library and still does not. Gzipped chart JavaScript per charted page drops from 131.5 kB (ApexCharts 3.37.1 JS + CSS) to 72.8 kB (Chart.js 4.5.1 plus the 2.3 kB inline theme preset below).
The showcase page is now charts/chartjs.html and the sidebar entry reads "Chart.js". charts/apexcharts.html stays as a redirect so existing links keep working.
One chart theme preset, following the colour mode live. The charted pages include a short preset (src/html/components/_chartjs.astro) that sets Chart.defaults from AdminLTE's CSS variables — body font, secondary text for ticks, 8% gridlines with no vertical grid on category axes, rounded bars, a tooltip and legend styled like a card — and restyles every chart on the page when <html data-bs-theme> changes (the ColorMode toggle, the OS in auto mode, or your own code) or when the web font finishes loading. Under <html dir="rtl"> legends and tooltips are laid out right-to-left. The ApexCharts colour-mode helper that every demo page carried in its shared scripts (#6105) is gone with it, so the 85 pages without charts are 1.6 kB lighter too. The dark-mode charts recipe, the Recommended Integrations, Migration and RTL docs are updated to match.
One Chart.js detail worth knowing if you adapt the preset: Chart.js copies the scale defaults into each chart's config when the chart is created, so changing Chart.defaults and calling chart.update() leaves existing axes in the old colours. The preset drops those copied colours before it updates; see the comment in the component.
.brand-text keeps its base margin-left: .5rem while .sidebar-mini.sidebar-collapse shrinks it to max-width: 0, so 8px of margin on a zero-width element pushed the logo off the rail's centre line. The margin is now cleared in that state — the hover rule that expands the rail already re-declared margin-left: .5rem, which is what the collapsed state should have mirrored all along (#6114, by @dfsmania).Dev dependencies refreshed — eslint 10.8.1 -> 10.9.1, eslint-plugin-unicorn 73 -> 74, @typescript-eslint/* and typescript-eslint 8.67.0 -> 8.69.0, astro 7.2.4 -> 7.2.10, @astrojs/mdx 7.0.7 -> 8.0.0, @astrojs/markdown-remark 7.2.4 -> 7.3.0, rollup 4.62.4 -> 4.63.1, terser 5.50.0 -> 5.51.2, happy-dom 20.11.6 -> 20.12.0, sass 1.103.0 -> 1.103.1, globals 17.11.0 -> 17.12.0. Every one of the 114 built files in dist/ is byte-identical afterwards, so this changes nothing for consumers.
@astrojs/mdx 8.0.0 is a major but not an Astro 8 requirement — it peers astro@^7.2.6 — and it flattens the tree rather than growing it: four nested duplicates of @astrojs/markdown-remark and @astrojs/internal-helpers go away and @astrojs/markdown-satteri is hoisted to the top level. npm ci from a clean checkout resolves with no peer warnings.
TypeScript stays on 6.0.3. 7.0.2 is out and Dependabot proposes it monthly, but two peers still refuse it: @astrojs/check@0.9.10 (the latest) declares typescript@^5.0.0 || ^6.0.0, and typescript-eslint@8.69.0 declares >=4.8.4 <6.1.0. The upgrade fails at npm ci with ERESOLVE, so it cannot land until both move.
One column per quarter.
A page whose content ended near a sheet boundary still printed one blank sheet.
A page whose content ended near a sheet boundary still printed one blank sheet.
.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. In the demo set UI/ribbons.html was the case: 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 — zeroing one without the other would have pulled the bottom area up over the content.
This is the last of what #6113 reported. Every one of the 90 pages in dist/ was printed to PDF and its final sheet checked: none prints a blank trailing sheet. One page changes page count; nothing else moves.
If you are coming from 4.8.x, the headline change is in 4.9.0 — the header, sidebar and footer no longer print, and data-lte-print="app" brings them back.
npm install admin-lte@4.9.1<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/admin-lte@4.9.1/dist/css/adminlte.min.css" />
<script src="https://cdn.jsdelivr.net/npm/admin-lte@4.9.1/dist/js/adminlte.min.js"></script>Full notes: CHANGELOG
Full Changelog: v4.9.0...v4.9.1
.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.
A printed page is a document again — the header, sidebar and footer no longer print.
AdminLTE 3 left all three off the paper. v4 printed them, and on a page that prints as a real document the result was the invoice in #6113: 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. A one-page invoice printed as two, the second all but empty.
The content now gets the full width of the paper. Nothing else about the print styles changed: link targets are still printed after external links, buttons still keep an outline, everything is still black on white.
Both ways back are still there, and both are now honoured properly:
<!-- print the whole shell, as 4.0–4.8 did -->
<html lang="en" data-lte-print="app"><!-- or bring back one piece: .d-print-flex for the header (it is a navbar),
.d-print-block for the sidebar and footer -->
<aside class="app-sidebar bg-body-secondary shadow d-print-block">…</aside>.d-print-none still hides anything you don't want on paper, in either mode. data-lte-print is now matched per token, so data-lte-print="plain app" sets both; data-lte-print="plain" is unaffected.
Reported by @johnnyq.
The layout kept its viewport-sized dimensions in print (#6113).
.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, along with the max-width: 100vw on the header, main and footer.
The print block set display on elements it did not need to.
Forcing display: block on .app-sidebar is 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 scoped out instead, and nothing in the print block forces a display value on the header, sidebar or footer any more.
adminlte.js is untouched; adminlte.css grows by 64 bytes gzipped.
npm install admin-lte@4.9.0<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/admin-lte@4.9.0/dist/css/adminlte.min.css" />
<script src="https://cdn.jsdelivr.net/npm/admin-lte@4.9.0/dist/js/adminlte.min.js"></script>Full notes: CHANGELOG
Full Changelog: v4.8.5...v4.9.0
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.
nanoid bumped to 3.3.18 in the lockfile — GHSA-2v37-7h3g-55p8 (high), transitive and dev-only. npm audit now reports zero vulnerabilities. Nothing shi…
A housekeeping patch: building AdminLTE from source is quiet again. Nothing in dist/ changed behaviour — the compiled CSS is byte-identical to 4.8.4, so this release matters only if you compile the Sass yourself.
npm run production printed six Sass deprecation warnings.
Sass 1.95.0 deprecated the ternary if($condition, $if-true, $if-false) in favour of the CSS if(). The palette sheets used it in three places — lte-needs-aa() in colors/_variables.scss, and the text-colour pick in colors/_primary.scss twice — and since both adminlte-colors.scss and adminlte-colors-v3.scss pull those partials in, three call sites produced six warnings.
They are now plain @if statements. Deliberately not the if(sass(cond): a; else: b) syntax Sass suggests: that would require Sass >= 1.95 of everyone compiling AdminLTE from source, and we ship the SCSS. @if compiles on every version, produces byte-identical CSS, and stays stylelint-clean.
Worth recording for anyone who chases this upstream: Bootstrap uses the ternary if() 82 times across bootstrap/scss and warns too — but css-compile already passes --quiet-deps, so none of that reaches your console. Every warning in this report was AdminLTE's own code (#6111, reported by @lfiorini).
markdown.rehypePlugins. Astro 7.2.4 deprecates the top-level remarkPlugins / rehypePlugins / remarkRehype keys in favour of an explicit markdown.processor — which would have put a fresh warning into the very command this release is about. The responsive-tables plugin now goes through unified() from @astrojs/markdown-remark; same pipeline, all 90 built pages byte-identical.nanoid bumped to 3.3.18 in the lockfile — GHSA-2v37-7h3g-55p8 (high), transitive and dev-only. 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/check accepts ^5 || ^6, so the toolchain still cannot move.npm install admin-lte@4.8.5<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/admin-lte@4.8.5/dist/css/adminlte.min.css" />
<script src="https://cdn.jsdelivr.net/npm/admin-lte@4.8.5/dist/js/adminlte.min.js"></script>Full notes: CHANGELOG
Full Changelog: v4.8.4...v4.8.5
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 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/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/check accepts ^5 || ^6, so the toolchain still cannot move.Reference tables on the documentation pages pushed the page sideways on a phone.
Reference tables on the documentation pages pushed the page sideways on a phone.
Markdown pipe-tables render as a bare <table> with no scroll container, so anything wider than about three columns overflowed the viewport — 265px of it on the Colors page at 390px wide, right after 4.8.3 fixed the header's own overflow.
They are now wrapped in .table-responsive with a tab stop, exactly like the hand-written tables beside them: the table scrolls inside the content column, and the scrollable region stays keyboard-reachable. Desktop layout is unchanged.
Only affects the documentation pages shipped in dist/docs/; adminlte.css and adminlte.js are untouched.
npm install admin-lte@4.8.4Full notes: CHANGELOG
Full Changelog: v4.8.3...v4.8.4
<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.
Every page scrolled sideways on a phone.
At 390px the header's right-hand item row — messages, notifications, fullscreen, user menu — is a few pixels wider than the viewport at Bootstrap's default 1rem nav-link padding. document.scrollWidth measured 412 against a 390 clientWidth on all 90 demo pages: a horizontal scrollbar on every screen, on every AdminLTE 4 page.
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 and 390/390 after; desktop unaffected.
If you check this yourself: compare scrollWidth with clientWidth, not window.innerWidth. The layout viewport widens to match the overflow, so the innerWidth comparison silently passes — which is how this survived this long.
npm install admin-lte@4.8.3Full notes: CHANGELOG
Full Changelog: v4.8.2...v4.8.3
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.
A maintenance release: the build toolchain is current again. dist/ is byte-identical to 4.8.1 , so there is nothing to change in your project — upgrad
A maintenance release: the build toolchain is current again. dist/ is byte-identical to 4.8.1, so there is nothing to change in your project — upgrade only if you build AdminLTE from source.
astro 7.1.6 → 7.2.3 · @astrojs/mdx 7.0.5 → 7.0.6 · eslint 10.8.0 → 10.8.1 · @typescript-eslint/{eslint-plugin,parser} + typescript-eslint 8.66.0 → 8.67.0 · vitest 4.1.10 → 4.1.11 · happy-dom 20.11.1 → 20.11.2 · axe-core 4.12.1 → 4.13.0 · terser 5.49.2 → 5.50.0 · postcss 8.5.25 → 8.5.26 · globals 17.9.0 → 17.11.0 · concurrently 10.0.4 → 10.0.5 · lockfile-lint 5.0.0 → 5.0.1
All within their current majors. Lint, 74 unit tests, bundlewatch and the axe accessibility gate are green.
TypeScript 7.0.2 is out, but typescript-eslint still declares peerDependencies.typescript: ">=4.8.4 <6.1.0" and @astrojs/check accepts ^5 || ^6. The toolchain moves when they do.
npm install admin-lte@4.8.2Full notes: CHANGELOG
Full Changelog: v4.8.1...v4.8.2
[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 admin-lte@4.0.0, eight releases after that was current. sync-version.mjs now stamps admin-lte@X.Y.Z 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/check accepts ^5 || ^6, so the toolchain cannot move yet. It will be picked up when those two allow it.A keyboard-accessibility fix on top of 4.8.0, reported by @johnnyq ( #6109 ).
A keyboard-accessibility fix on top of 4.8.0, reported by @johnnyq (#6109).
data-lte-primaryBoth palette sheets shipped a literal Sass variable name in their compiled output:
[data-lte-primary] .pagination {
--bs-pagination-focus-box-shadow: 0 0 0 $pagination-focus-box-shadow-width rgba(var(--bs-primary-rgb), 0.25);
}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 was invisible wherever data-lte-primary was in use. Introduced in 4.6.0.
The variable it named never existed in Bootstrap either (the pagination focus shadow comes from $focus-ring-box-shadow), so interpolating it would not have compiled. The value is now built from the focus-ring tokens and reuses the --bs-focus-ring-color the sheet already sets, so the ring follows the theme colour:
--bs-pagination-focus-box-shadow: 0 0 0 0.25rem var(--bs-focus-ring-color);A unit test now scans the compiled output of all three sheets for unresolved Sass variables. Compilation cannot catch this class of typo, which is exactly why it shipped — this was the only instance in the repo, and adminlte.css was never affected.
npm install admin-lte@4.8.1<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/admin-lte@4.8.1/dist/css/adminlte.min.css" />
<script src="https://cdn.jsdelivr.net/npm/admin-lte@4.8.1/dist/js/adminlte.min.js"></script>Full notes: CHANGELOG
Full Changelog: v4.8.0...v4.8.1
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).@johnnyq measured the AdminLTE 3 palette against WCAG AA and found eight colours that miss it with the text v3 assigns them ( #6110 ). The values stay
@johnnyq measured the AdminLTE 3 palette against WCAG AA and found eight colours that miss it with the text v3 assigns them (#6110). The values stay exactly as they were — there is now a switch for the text.
data-lte-contrast="aa"<html lang="en" data-lte-contrast="aa">Flips the text on the eight colours whose assigned ink falls short, leaving every background byte-identical:
| Colour | Default (v3 rule) | With aa |
|---|---|---|
teal #20c997 |
white — 2.13:1 | black — 9.86:1 |
cyan #17a2b8 |
white — 3.04:1 | black — 6.90:1 |
green #28a745 |
white — 3.13:1 | black — 6.70:1 |
olive #3d9970 |
white — 3.51:1 | black — 5.99:1 |
lightblue #3c8dbc |
white — 3.67:1 | black — 5.73:1 |
fuchsia #f012be |
white — 3.79:1 | black — 5.54:1 |
pink #e83e8c |
white — 3.82:1 | black — 5.50:1 |
blue #007bff |
white — 3.98:1 | black — 5.28:1 |
It 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 that data-lte-primary produces, which is where this mattered most: one attribute puts a colour on buttons, pills, pagination and list groups at once.
Three details worth knowing:
#1f2d3d clears 4.5:1 on only two of the eight. The colours that already carry dark text — lime, orange, yellow — keep #1f2d3d and are left alone.data-bs-theme="light" so its nav links follow the heading. The demo pages do that for you when the switch is on.adminlte-colors.css emits not one byte for this and the attribute is safe to leave in your markup either way.UI → Colors and Theme Customize gained a toggle, and the Colors page documents it.
data-lte-primary="info" gave white text on cyan with the v3 sheet loaded. The presets applied the active sheet's contrast rule to Bootstrap's own theme colours, and v3's YIQ test puts #0dcaf0 at 149.8 against a threshold of 150 — a tenth of a point from dark text — so it produced 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.npm install admin-lte@4.8.0<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/admin-lte@4.8.0/dist/css/adminlte.min.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/admin-lte@4.8.0/dist/css/adminlte-colors-v3.min.css" />
<script src="https://cdn.jsdelivr.net/npm/admin-lte@4.8.0/dist/js/adminlte.min.js"></script>Full notes: CHANGELOG
Full Changelog: v4.7.0...v4.8.0
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.A print follow-up to 4.6.0, from @johnnyq again ( #6108 ) — plus a printing bug that turned out to be worse than reported.
A print follow-up to 4.6.0, from @johnnyq again (#6108) — plus a printing bug that turned out to be worse than reported.
data-lte-print="plain"Two rules in AdminLTE's print stylesheet restyle content unconditionally: the target of every external link is printed after it, and buttons are outlined in black. Both are HTML5 Boilerplate conventions, sensible when you're printing a copy of a web page — and wrong for an invoice, quote or work order that goes to a customer, where Pay online (https://pay.example.com/inv/12345?token=…) is not what anyone wants on paper.
<html lang="en" data-lte-print="plain">That turns both off. Text still prints black on white, so nothing becomes unreadable, and it works on any container if only part of the page is the document:
<div class="card" data-lte-print="plain">…the invoice…</div>Setting it from JavaScript right before window.print() works too. Hide the chrome with Bootstrap's own .d-print-none, which has worked on the sidebar and header since 4.6.0.
Note .d-print-none could not have been the opt-out here: it hides an element, and these rules restyle elements that are meant to print.
.nav-link alongside .btn, and the sidebar prints by design (since the 4.0.0 print fix), so a printed page came out with a black rectangle around all 85 menu items. The outline now applies to .btn only — a button still reads as a button on paper — while nav links keep the ink-saving black-on-transparent treatment and print as ordinary text.--bs-primary on :root, which is exactly what leaves .btn-primary blue. It now gives the two approaches that work: $primary in Sass, or data-lte-primary with the palette sheet.New recipe: Printing a page as a document — the attribute, scoping it to part of a page, hiding chrome, and printing from a button.
npm install admin-lte@4.7.0<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/admin-lte@4.7.0/dist/css/adminlte.min.css" />
<script src="https://cdn.jsdelivr.net/npm/admin-lte@4.7.0/dist/js/adminlte.min.js"></script>Full notes: CHANGELOG
Full Changelog: v4.6.0...v4.7.0
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.Two reports from @johnnyq , both fixed — and the second one turned into the feature the AdminLTE 3 palettes were missing.
Two reports from @johnnyq, both fixed — and the second one turned into the feature the AdminLTE 3 palettes were missing.
primary — data-lte-primary (#6107)Bootstrap compiles $primary into its components: .btn-primary ships with --bs-btn-bg: #0d6efd, not var(--bs-primary). Overriding the token repainted the utilities and left every button, pagination, checkbox and focus ring blue — which is why picking a skin looked half-applied.
Both colour sheets now rewire those components and point them at a palette colour with one attribute:
<html lang="en" data-lte-primary="lightblue">Nothing changes without the attribute, and it works on any container, so one page can carry more than one primary.
What follows the colour: .btn-primary and .btn-outline-primary (hover, active and disabled states 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, plus AdminLTE's .card-primary, .direct-chat-primary and .toast-primary — in both colour modes, with Bootstrap's dark-mode link tint.
data-lte-primary="danger" needs no palette colour.--bs-primary; in Sass, $lte-palette-custom: ("brand": #7a5af8) generates a full preset, shades and contrast text included.Documented on the Colors page.
d-print-none did nothing on the sidebar and the app wrapper (#6106). The print layout fix from 4.0.0 set display: grid !important / display: block !important on those two elements and, being later in the cascade, beat Bootstrap's .d-print-none. Both rules are now scoped with :not(.d-print-none) — the print layout still applies by default, and the display utilities win where a page asks them to..skip-link:hover painted var(--bs-primary-emphasis), which Bootstrap does not define, so the background computed to transparent and left white text on the page background.Computed colours in both palette sheets are emitted as hex instead of Sass's rgb(5.647%, 40.784%, 39.216%) (cleancss leaves values inside custom properties alone), which pays for most of the new feature: adminlte-colors.css is 7.8 kB gzipped, adminlte-colors-v3.css 9.1 kB. adminlte.css is unchanged.
npm install admin-lte@4.6.0<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/admin-lte@4.6.0/dist/css/adminlte.min.css" />
<!-- optional: the extended palette, or adminlte-colors-v3.min.css for the AdminLTE 3 originals -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/admin-lte@4.6.0/dist/css/adminlte-colors.min.css" />
<script src="https://cdn.jsdelivr.net/npm/admin-lte@4.6.0/dist/js/adminlte.min.js"></script>Full notes: CHANGELOG
Full Changelog: v4.5.0...v4.6.0
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.AdminLTE 4.5.0 adds the one thing 4.4.0's palette couldn't offer: the AdminLTE 3 colours exactly as they were.
AdminLTE 4.5.0 adds the one thing 4.4.0's palette couldn't offer: the AdminLTE 3 colours exactly as they were.
4.4.0 brought the extra colours back as a palette designed for 4.4 — and some of you liked the originals better. Fair enough. A second opt-in sheet, dist/css/adminlte-colors-v3.css, gives you exactly those: 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 (white on lightblue, olive, teal, cyan …; dark on lime, orange, yellow), emitting the same class families as the designed palette: --bs-* tokens with dark-mode variants, .bg-*, .text-bg-*, .text-*, .border-*, .link-*, .bg-gradient-*, .card-*, .callout-*, .direct-chat-*.
The two sheets share class names, so load one of them:
<!-- either the palette designed for 4.4 … -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/admin-lte@4.5.0/dist/css/adminlte-colors.min.css">
<!-- … or the AdminLTE 3 originals -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/admin-lte@4.5.0/dist/css/adminlte-colors-v3.min.css">text-bg-* + data-bs-theme on the header and sidebar (text-bg-gray-dark is the exact sidebar-dark-* background).$lte-palette-contrast: "wcag" | "yiq" knob (plus $lte-palette-yiq-threshold, -text-dark, -text-light), so $lte-palette-custom and $lte-palette-aliases work with the v3 sheet too. npm run palette -- --check and the unit tests keep the v3 map in step.Requested in #6103 by @johnnyq. adminlte.css and adminlte.js are unchanged.
npm install admin-lte@4.5.0<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/admin-lte@4.5.0/dist/css/adminlte.min.css">
<script src="https://cdn.jsdelivr.net/npm/admin-lte@4.5.0/dist/js/adminlte.min.js"></script>Full details in the CHANGELOG.
Full Changelog: v4.4.0...v4.5.0
Full Changelog: v4.4.1...v4.5.0
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).AdminLTE 4.4.1 is a small patch on top of yesterday's 4.4.0: the demo's charts now follow dark mode.
AdminLTE 4.4.1 is a small patch on top of yesterday's 4.4.0: the demo's charts now follow dark mode.
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 — no reload. All demo charts got ids. Documented as the “Charts that follow dark mode” recipe on the Recipes page, with the pattern for other libraries.adminlte.css and adminlte.js are unchanged from 4.4.0 — this release updates the demo pages and docs shipped in dist/.
npm install admin-lte@4.4.1<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/admin-lte@4.4.1/dist/css/adminlte.min.css">
<!-- optional: the extended palette -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/admin-lte@4.4.1/dist/css/adminlte-colors.min.css">
<script src="https://cdn.jsdelivr.net/npm/admin-lte@4.4.1/dist/js/adminlte.min.js"></script>Full details in the CHANGELOG.
Full Changelog: v4.4.0...v4.4.1
Full Changelog: v4.4.0...v4.4.1
_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).Extended colour palette (`dist/css/adminlte-colors.css`) — an opt-in stylesheet (≈ 5 kB gzipped; adminlte.css is unchanged) that adds fourteen colours
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).Ribbons left the card corner bare. The banner was centred on its clipping square, but the square's centre is the point *furthest* from the corner — so
64px of it is readable at the default size. .ribbon-lg and .ribbon-xl give more room. Documented on the Miscellaneous components page.Gallery page (`pages/gallery.html`) — a filterable media library: responsive tile grid, per-item action menus, a live result count and an empty state.
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.Advanced form pages: forms/advanced.html demonstrates the recommended Tom Select and Flatpickr integrations (searchable single select, tag-style multi
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/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/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.Sass deprecation policy: the build no longer silences all warnings (--quiet); it silences only dependency warnings and the known @import deprecation (…
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 "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 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 module-system migration is intentionally deferred until Bootstrap ships module-system Sass (Bootstrap 6) — Bootstrap 5's partials are designed around @import's shared global namespace and cannot be loaded individually via @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 example page — both login pages have linked forgot-password.html since 4.0.0, but the page didn't exist (dead link in every deployed d
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 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.…+ bundlewatch) passes and npm audit remains at 0 vulnerabilities. (supersedes Dependabot PRs #6065–#6074)
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)Hardened the social preview script's static file server against path traversal and error detail leakage (CodeQL alerts #87–#92). Dev-only script, not
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)All dependencies bumped to latest; axios pinned via npm overrides to clear a transitive advisory. npm audit remains at 0 vulnerabilities.
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.…through @astrojs/check. npm audit now reports 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/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/check. npm audit now reports 0 vulnerabilities.Sidebar Persistence: enablePersistence now defaults to false (was true). Opt in via data-enable-persistence="true" on .app-sidebar. BREAKING CHANGE
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)Dependencies: Updated 8 packages to latest versions
shx
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 dif
This 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:
Resolved all npm security vulnerabilities (0 vulnerabilities remaining)
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/eslint-plugin: 7.18.0 → 8.34.1@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.3Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →