diff --git a/.build/check-html.ts b/.build/check-html.ts
index f96b2512f..18c5d253f 100644
--- a/.build/check-html.ts
+++ b/.build/check-html.ts
@@ -11,6 +11,7 @@
// pnpm run check-html http://localhost:3000
import { createReadStream, existsSync, readFileSync, statSync } from 'node:fs'
import { createServer, type Server } from 'node:http'
+import { availableParallelism } from 'node:os'
import { extname, join, resolve } from 'node:path'
import { chromium, type Browser, type Page } from 'playwright'
@@ -41,6 +42,10 @@ const MIME: Record = {
// Ids generated by libraries at runtime; duplicates there are theirs, not ours.
const VENDOR_ID_PREFIXES = ['apexcharts-', 'jvm-', 'SvgjsSvg', 'SvgjsLinearGradient']
+// Two pages per core: each page sits idle for the settle delay below, so one
+// worker per core leaves the machine waiting. Override with CHECK_HTML_WORKERS.
+const WORKERS = Number(process.env.CHECK_HTML_WORKERS) || Math.min(16, availableParallelism() * 2)
+
// Console noise from third-party demos (WebGL driver messages, Google Maps without a key, plyr).
const CONSOLE_IGNORE = [/GL Driver Message/, /Google Maps JavaScript API/, /allowfullscreen/, /web-share/, /No available adapters/, /\[vite\]/, /astro/i]
@@ -175,7 +180,7 @@ const crawl = async (browser: Browser, base: string, paths: string[]) => {
await page.close()
}
}
- await Promise.all(Array.from({ length: 4 }, worker))
+ await Promise.all(Array.from({ length: WORKERS }, worker))
await context.close()
for (const [href, from] of links) {
const url = href.startsWith('/') ? base + href : new URL(href, base + from).href
diff --git a/.dockerignore b/.dockerignore
index 14e19c43c..09a81547f 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -6,3 +6,6 @@ node_modules/
**/dist/
**/.astro/
packages-zip/
+.env
+.env.*
+.vercel/
diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml
index ba4810479..e649a9d74 100644
--- a/.github/actions/setup/action.yml
+++ b/.github/actions/setup/action.yml
@@ -1,19 +1,39 @@
name: Set up the workspace
description: Restore the turbo cache, install pnpm and Node.js, install the dependencies
+inputs:
+ turbo-cache:
+ description: Restore and save the turbo cache. The release job turns it off so a cache written by another run cannot end up in a published package.
+ default: 'true'
+
runs:
using: composite
steps:
- - name: Cache turbo build setup
+ # The key carries the workflow name: cache keys are write-once, so with one
+ # key per commit the quickest workflow (lint) saved its near-empty cache and
+ # the build never got to store its outputs.
+ #
+ # A push starts from an empty cache and saves only what this build produced,
+ # which keeps the entry at the size of one build. Pull requests restore the
+ # latest entry of the base branch and save nothing.
+ - name: Save the turbo cache
+ if: inputs.turbo-cache == 'true' && github.event_name == 'push'
uses: actions/cache@v6
with:
- path: .turbo
- key: ${{ runner.os }}-turbo-${{ github.sha }}
+ path: .turbo/cache
+ key: ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.sha }}
+
+ - name: Restore the turbo cache
+ if: inputs.turbo-cache == 'true' && github.event_name != 'push'
+ uses: actions/cache/restore@v6
+ with:
+ path: .turbo/cache
+ key: ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.sha }}
restore-keys: |
- ${{ runner.os }}-turbo-
+ ${{ runner.os }}-turbo-${{ github.workflow }}-
- name: Install PNPM
- uses: pnpm/action-setup@v6
+ uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
- name: Set up Node.js
uses: actions/setup-node@v7
@@ -22,5 +42,5 @@ runs:
cache: 'pnpm'
- name: Install pnpm dependencies
- run: pnpm install
+ run: pnpm install --frozen-lockfile
shell: bash
diff --git a/.github/workflows/add_to_project.yml b/.github/workflows/add_to_project.yml
index 81111ce88..954f6ecd2 100644
--- a/.github/workflows/add_to_project.yml
+++ b/.github/workflows/add_to_project.yml
@@ -8,6 +8,10 @@ on:
types:
- opened
+permissions:
+ issues: read
+ pull-requests: read
+
jobs:
add-to-project:
name: Add new issue and pr to project
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 7fa03165b..7829e11ec 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -19,14 +19,6 @@ jobs:
- name: Set up the workspace
uses: ./.github/actions/setup
+ # turbo directly: `pnpm run build` also zips the package, which only the release needs
- name: Build
- run: pnpm run build
-
- - name: Install Playwright
- uses: ./.github/actions/playwright
-
- - name: Check rendered html
- run: pnpm run check:html preview/dist docs/dist --quiet
-
- - name: Check markup classes
- run: pnpm run check:markup-classes --html preview/dist --html docs/dist --css preview/dist/dist/css --css preview/dist/dist/libs --css preview/dist/preview/css --css docs/dist/css --css docs/dist/preview/css
+ run: pnpm exec turbo build
diff --git a/.github/workflows/bundlewatch.yml b/.github/workflows/bundlewatch.yml
index ad6f532a6..916bfd2a4 100644
--- a/.github/workflows/bundlewatch.yml
+++ b/.github/workflows/bundlewatch.yml
@@ -7,6 +7,9 @@ on:
- main
- dev
+permissions:
+ contents: read
+
env:
FORCE_COLOR: 2
diff --git a/.github/workflows/calibreapp-image-actions.yml b/.github/workflows/calibreapp-image-actions.yml
index 1e16b2d83..8b12d7207 100644
--- a/.github/workflows/calibreapp-image-actions.yml
+++ b/.github/workflows/calibreapp-image-actions.yml
@@ -25,6 +25,7 @@ jobs:
persist-credentials: false
- name: Compress Images
- uses: calibreapp/image-actions@main
+ # pinned to a commit: this job holds a write token
+ uses: calibreapp/image-actions@26d5b54006db4da7a1b29903f33404a6f77b58cf # main, 2026-09-02
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/html-checks.yml b/.github/workflows/html-checks.yml
new file mode 100644
index 000000000..cd095e9cb
--- /dev/null
+++ b/.github/workflows/html-checks.yml
@@ -0,0 +1,33 @@
+name: HTML checks
+
+on:
+ pull_request: null
+ push:
+ branches:
+ - dev
+
+permissions:
+ contents: read
+
+jobs:
+ html-checks:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Clone repository
+ uses: actions/checkout@v7
+
+ - name: Set up the workspace
+ uses: ./.github/actions/setup
+
+ # only the two sites the checks read; the Build workflow covers the rest
+ - name: Build the preview and the docs
+ run: pnpm exec turbo build --filter=@tabler/preview --filter=@tabler/docs
+
+ - name: Install Playwright
+ uses: ./.github/actions/playwright
+
+ - name: Check rendered html
+ run: pnpm run check:html preview/dist docs/dist --quiet
+
+ - name: Check markup classes
+ run: pnpm run check:markup-classes --html preview/dist --html docs/dist --css preview/dist/dist/css --css preview/dist/dist/libs --css preview/dist/preview/css --css docs/dist/css --css docs/dist/preview/css
diff --git a/.github/workflows/lockfiles.yaml b/.github/workflows/lockfiles.yaml
index cb9f2de3d..361cda05b 100644
--- a/.github/workflows/lockfiles.yaml
+++ b/.github/workflows/lockfiles.yaml
@@ -14,7 +14,7 @@ jobs:
- name: Clone Tabler
uses: actions/checkout@v7
- name: Prevent lock file change
- uses: xalvarez/prevent-file-change-action@v3
+ uses: xalvarez/prevent-file-change-action@dbc67c205c9ed02aa39c497b422c24f364c3e8dd # v3.0.1
with:
githubToken: ${{ secrets.GITHUB_TOKEN }}
pattern: Gemfile.lock|pnpm-lock.json|pnpm-lock.yaml
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 873c5e37f..445b897e4 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -26,16 +26,18 @@ jobs:
- name: Set up the workspace
uses: ./.github/actions/setup
+ with:
+ turbo-cache: 'false'
- name: Update npm # trusted publishing requires npm >= 11.5.1
- run: npm install -g npm@latest
+ run: npm install -g npm@11.19.1
- name: Build
run: pnpm run build
- name: Create release Pull Request or publish to NPM
id: changesets
- uses: changesets/action@v2
+ uses: changesets/action@ae32849d5ba541f9ae29e40e22a623bc13562f51 # v2.1.2
with:
version-script: pnpm run version
publish-script: pnpm run publish