Compare commits

...

79 Commits

Author SHA1 Message Date
BG-Software
e4e1ff40c1 Change LambdaTest image to TestMu AI in README.md 2026-01-21 17:50:35 +01:00
Paweł Kuna
938e9d35cc refactor: migrate Vite configuration from .ts to .mts (#2594) 2026-01-12 02:52:15 +01:00
Paweł Kuna
82e3c39585 Enhance markdown typography configuration (#2590) 2026-01-11 17:24:23 +01:00
Paweł Kuna
eac69eb35b Add branch and PR guidelines (#2589) 2026-01-11 17:24:10 +01:00
Paweł Kuna
684f40e7c1 Enhance customization guide with font size and color variables (#2588) 2026-01-11 16:49:28 +01:00
dependabot[bot]
f6414b3c94 chore(deps-dev): bump pnpm from 10.6.5 to 10.27.0 (#2584)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-11 01:30:19 +01:00
codecalm
c3e6aa1bd3 refactor: update stylesheet linking logic in default.html 2026-01-10 21:30:34 +01:00
codecalm
8e3cddb70f Remove Playwright configuration and related dependencies from the project, including visual regression tests and associated scripts. 2026-01-09 21:51:57 +01:00
Paweł Kuna
857988dd44 Migrate core JavaScript to TypeScript (#2582)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-09 21:49:22 +01:00
Paweł Kuna
a24d5cab13 Add markdown linting script (#2585) 2026-01-09 21:39:18 +01:00
codecalm
0c654c61f0 chore: update Node.js version to 22 in configuration files 2026-01-08 17:23:59 +01:00
codecalm
8e73f57140 docs: update browser support documentation to reflect minimum version requirements and clarify unsupported browsers 2026-01-08 16:50:48 +01:00
codecalm
f0fb9c66c0 chore: update browserslist configuration to support newer versions and remove outdated entries 2026-01-08 16:48:50 +01:00
dependabot[bot]
29d9d4b5df chore(deps): bump actions/cache from 4 to 5 (#2568)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-06 16:38:36 +01:00
Paweł Kuna
84c31d1383 chore: update dependencies and improve Eleventy configuration (#2581) 2026-01-06 16:12:56 +01:00
Paweł Kuna
41fd82b388 update icons to v3.36.1 (#2580) 2026-01-06 02:11:14 +01:00
codecalm
abac36c580 chore: update illustrations for dark and light themes 2026-01-06 01:58:46 +01:00
Paweł Kuna
301e77898c refactor: migrate rgba() to color-mix() and color-transparent() (#2579) 2026-01-06 01:54:34 +01:00
Paweł Kuna
a14425792b Update SCSS variables and button styles (#2559) 2026-01-06 00:32:01 +01:00
Paweł Kuna
48dbd1ed1b refactor: migrate build system from Rollup to Vite (#2578) 2026-01-06 00:26:42 +01:00
codecalm
ee8875deb6 fix: add aria-orientation attribute for vertical navigation in nav-segmented.html 2026-01-06 00:19:23 +01:00
codecalm
c0a93b8611 refactor: simplify SRI generation process and improve error handling in generate-sri.js 2026-01-05 23:32:16 +01:00
codecalm
42081245b4 fix: update input background variable to use form background color 2026-01-05 23:05:34 +01:00
codecalm
d56e1a2bac chore: add .env and sri.json to .gitignore 2026-01-03 01:43:34 +01:00
codecalm
c6e8879bb6 chore: remove unused sri.json file 2026-01-03 01:43:05 +01:00
codecalm
a811fdb662 chore: update copyright year in LICENSE and correct date format in text-features.html to 2026 2026-01-03 01:42:00 +01:00
codecalm
63a35a849c fix: fix EU flag svg
Some checks failed
Argos Tests / test (push) Has been cancelled
Bundlewatch / bundlewatch (push) Has been cancelled
Release / Release (push) Has been cancelled
2025-12-12 19:56:38 +01:00
ethancrawford
94e1a95ffb Allow Offcanvas docs page to scroll properly (#2565) 2025-12-11 20:07:12 +01:00
Paweł Kuna
83ec6f8bcc feat: add Tour component using Driver.js (#2549) 2025-12-08 21:08:22 +01:00
ethancrawford
e3d86c519b feat: upgrade apexcharts to v5 and add CSS variables for dynamic chart colors (#2555)
Co-authored-by: codecalm <codecalm@gmail.com>
2025-12-08 21:08:03 +01:00
Paweł Kuna
f9d6076014 refactor: Update build scripts and asset management across packages (#2558) 2025-12-02 18:51:54 +01:00
dependabot[bot]
f264470d8f chore(deps): bump actions/checkout from 5 to 6 (#2550)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-30 23:52:18 +01:00
Paweł Kuna
ec9469332e feat: Add card gradients page and gradient card components (#2554) 2025-11-29 11:58:51 +01:00
codecalm
deb887b4aa fix: update appData function to use dynamic path resolution for core files 2025-11-28 15:11:12 +01:00
codecalm
f9551c3b8e fix: sort illustration filenames and update illustrations.json content 2025-11-27 03:07:08 +01:00
Aastha Gupta
3aba62e652 fix: add border-top-radius to card table first/last cells (#2505)
Co-authored-by: Paweł Kuna <1282324+codecalm@users.noreply.github.com>
2025-11-26 19:31:42 +01:00
Paweł Kuna
059bae1cf6 fix: refactor Bootstrap exports for consistency (#2545) 2025-11-26 19:31:11 +01:00
Igor Bezlepkin
41ed22a128 Remove redundant nullish coalescing operators in popover and tooltip (#2553)
Co-authored-by: codecalm <codecalm@gmail.com>
2025-11-26 19:30:55 +01:00
Paweł Kuna
e206d7a908 fix: Fix white space on left side when scrollbar is present (#2548) 2025-11-26 19:30:27 +01:00
Paweł Kuna
2dc7edae36 fix: update dark theme SCSS variables (#2546) 2025-11-26 19:30:15 +01:00
Paweł Kuna
8bc6fa7fd1 fix: Use CSS variables for status colors and include social colors (#2547) 2025-11-26 19:29:46 +01:00
Paweł Kuna
a198b0c7c5 Init Geist font (#2552) 2025-11-26 19:29:27 +01:00
codecalm
b1f711635b fix: remove sticky-top class from icons banner card 2025-11-26 19:24:42 +01:00
codecalm
d0fe913453 Refactor SCSS utilities to use logical properties for border-radius 2025-11-24 00:06:57 +01:00
Paweł Kuna
0106d6b7d2 Update SCSS to use logical properties (#2537) 2025-11-23 22:05:45 +01:00
Paweł Kuna
14ed4693a5 Refactor SCSS imports, remove compile warnings (#2536) 2025-11-17 21:25:16 +01:00
codecalm
3bcd82ae9f Update illustrations for dark and light themes 2025-11-15 16:41:49 +01:00
Paweł Kuna
40a9b5ac27 Update Bootstrap and other dependencies to latest versions (#2521) 2025-11-15 16:38:19 +01:00
Paweł Kuna
99b9ea45f6 feat: Add Task List page (#2528) 2025-11-15 13:45:53 +01:00
Paweł Kuna
4ce08cad53 feat: Add Crypto Dashboard with dynamic data and menu updates (#2527) 2025-11-15 13:43:48 +01:00
Paweł Kuna
b0fa6559da feat: Add four new modals (new task, edit profile, confirm delete, change password) (#2529) 2025-11-15 13:42:27 +01:00
Paweł Kuna
c7070180dc feat: add All Elements UI components page (#2524) 2025-11-15 13:42:11 +01:00
Paweł Kuna
9a17b72a60 Update dependencies (#2519) 2025-11-15 13:39:45 +01:00
Paweł Kuna
0c7996321b feat: Add print styles using media-print mixin (#2531) 2025-11-15 13:37:23 +01:00
codecalm
ba7bb880c4 style: Update badge positioning and size in navbar and adjust SCSS variables for consistency 2025-11-15 13:37:14 +01:00
Paweł Kuna
5018aa9113 fix: Fix .btn-icon size by aligning min-width calculation (#2530) 2025-11-15 13:15:12 +01:00
Paweł Kuna
8d8727f587 feat: Add language selector dropdown to navbar (#2526) 2025-11-02 20:49:13 +01:00
Paweł Kuna
6e656ad1de fix: Input Icon Z-Index Issue with Form Validation (#2525) 2025-11-02 20:48:36 +01:00
codecalm
0042472f9a feat: add new statistics cards to widgets page and update card styles 2025-11-02 18:57:51 +01:00
codecalm
21eb18f918 refactor: remove GitHub source code button from sidebar navbar 2025-11-01 22:34:20 +01:00
Paweł Kuna
cf04a00f8e Update Tabler Icons to v3.35.0 (#2520) 2025-10-24 00:27:39 +02:00
dependabot[bot]
41bcebc0a7 chore(deps): bump actions/setup-node from 5 to 6 (#2516)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-24 00:14:05 +02:00
dependabot[bot]
7475114ef1 chore(deps): bump xalvarez/prevent-file-change-action from 2 to 3 (#2504)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-24 00:13:54 +02:00
Kam
8ce84e0f2f Fix deprecated install command for Yarn in docs (#2497) 2025-10-24 00:09:46 +02:00
codecalm
496704b163 Merge branch 'main' of https://github.com/tabler/tabler into dev 2025-10-24 00:08:42 +02:00
codecalm
e098fdfaa6 fix: ensure generated IDs do not start with a number in headings
Some checks failed
Bundlewatch / bundlewatch (push) Has been cancelled
2025-10-06 20:18:50 +02:00
codecalm
58417be796 Merge branch 'main' of https://github.com/tabler/tabler into dev
Some checks failed
Close inactive issues and pr / test (push) Has been cancelled
Argos Tests / test (push) Has been cancelled
Bundlewatch / bundlewatch (push) Has been cancelled
2025-09-23 23:39:09 +02:00
codecalm
a7fccda74c fix: remove analytics environment variables and adjust layout inclusion
Some checks failed
Bundlewatch / bundlewatch (push) Has been cancelled
2025-09-23 23:15:32 +02:00
codecalm
dbb5e7d2ed fix: restore analytics environment variables in appData 2025-09-23 23:07:40 +02:00
codecalm
5da9078f55 fix: add domains 2025-09-23 23:06:26 +02:00
codecalm
1cd1fcaf28 refactor: clean up comments in analytics script 2025-09-23 23:03:57 +02:00
codecalm
b4ab1100ef fix: update PostHog initialization to use dynamic API key 2025-09-23 23:02:22 +02:00
codecalm
af41699e84 remove old analytics 2025-09-23 22:59:55 +02:00
codecalm
6e2e4e3317 add analytics to docs 2025-09-23 22:57:04 +02:00
codecalm
d8077f438c clean prettier comments 2025-09-23 22:55:26 +02:00
codecalm
0c07677606 clean prettier comments 2025-09-23 22:54:13 +02:00
codecalm
14418a1c08 fix posthog 2025-09-23 22:28:41 +02:00
codecalm
7b74fee012 init posthog stats 2025-09-23 22:24:36 +02:00
codecalm
9893b11ed2 init posthog stats
Some checks failed
Argos Tests / test (push) Has been cancelled
Bundlewatch / bundlewatch (push) Has been cancelled
2025-09-23 22:19:25 +02:00
412 changed files with 11774 additions and 5088 deletions

View File

@@ -1,6 +1,10 @@
>= 1%
last 2 versions
Firefox ESR
>= 0.5%
last 2 major versions
not dead
safari >= 15.4
iOS >= 15.4
Chrome >= 120
Firefox >= 121
iOS >= 15.6
Safari >= 15.6
not Explorer <= 11
Samsung >= 23
not kaios <= 2.5

View File

@@ -1,63 +0,0 @@
#!/usr/bin/env node
'use strict'
import { readFileSync, writeFileSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url'
import { sync } from 'glob';
import * as prettier from "prettier";
const __dirname = dirname(fileURLToPath(import.meta.url))
const docs = sync(join(__dirname, '..', 'docs', '**', '*.md'))
async function formatHTML(htmlString) {
try {
const formattedHtml = await prettier.format(htmlString, {
parser: "html",
printWidth: 100,
});
return formattedHtml;
} catch (error) {
console.error("Error formatting HTML:", error);
return htmlString; // Return original in case of an error
}
}
async function replaceAsync(str, regex, asyncFn) {
const matches = [...str.matchAll(regex)];
const replacements = await Promise.all(
matches.map(async (match) => asyncFn(...match))
);
let result = str;
matches.forEach((match, i) => {
result = result.replace(match[0], replacements[i]);
});
return result;
}
for (const file of docs) {
const oldContent = readFileSync(file, 'utf8')
// get codeblocks from markdown
const content = await replaceAsync(oldContent, /(```([a-z0-9]+).*?\n)(.*?)(```)/gs, async (m, m1, m2, m3, m4) => {
if (m2 === 'html') {
m3 = await formatHTML(m3);
// remove empty lines
m3 = m3.replace(/^\s*[\r\n]/gm, '');
return m1 + m3.trim() + "\n" + m4;
}
return m.trim();
})
if (content !== oldContent) {
writeFileSync(file, content, 'utf8')
console.log(`Reformatted ${file}`)
}
}

79
.build/reformat-mdx.ts Normal file
View File

@@ -0,0 +1,79 @@
#!/usr/bin/env node
import { readFileSync, writeFileSync } from 'node:fs'
import { join, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
import { sync } from 'glob'
import * as prettier from 'prettier'
const __dirname = dirname(fileURLToPath(import.meta.url))
const docs: string[] = sync(join(__dirname, '..', 'docs', '**', '*.md'))
async function formatHTML(htmlString: string): Promise<string> {
try {
const formattedHtml = await prettier.format(htmlString, {
parser: 'html',
printWidth: 100,
})
return formattedHtml
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error)
console.error('Error formatting HTML:', errorMessage)
return htmlString // Return original in case of an error
}
}
async function replaceAsync(
str: string,
regex: RegExp,
asyncFn: (...args: string[]) => Promise<string>
): Promise<string> {
const matches = [...str.matchAll(regex)]
const replacements = await Promise.all(
matches.map(async (match: RegExpMatchArray) => asyncFn(...match))
)
let result = str
matches.forEach((match: RegExpMatchArray, i: number) => {
result = result.replace(match[0], replacements[i])
})
return result
}
async function processFiles(): Promise<void> {
for (const file of docs) {
const oldContent = readFileSync(file, 'utf8')
// get codeblocks from markdown
const content = await replaceAsync(
oldContent,
/(```([a-z0-9]+).*?\n)(.*?)(```)/gs,
async (m: string, m1: string, m2: string, m3: string, m4: string) => {
if (m2 === 'html') {
let formattedHtml = await formatHTML(m3)
// remove empty lines
formattedHtml = formattedHtml.replace(/^\s*[\r\n]/gm, '')
return m1 + formattedHtml.trim() + '\n' + m4
}
return m.trim()
}
)
if (content !== oldContent) {
writeFileSync(file, content, 'utf8')
console.log(`Reformatted ${file}`)
}
}
}
processFiles().catch((error) => {
const errorMessage = error instanceof Error ? error.message : String(error)
console.error('Error processing files:', errorMessage)
process.exit(1)
})

View File

@@ -0,0 +1,76 @@
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { defineConfig, type UserConfig } from 'vite'
interface CreateViteConfigOptions {
entry: string
name?: string
fileName: string | ((format: string) => string)
formats: ('es' | 'umd' | 'iife' | 'cjs')[]
outDir: string
banner?: string
minify?: boolean | 'esbuild'
}
/**
* Creates a Vite configuration for building libraries
*/
export function createViteConfig({
entry,
name,
fileName,
formats,
outDir,
banner,
minify = false
}: CreateViteConfigOptions): UserConfig {
const rollupOutput: {
generatedCode: {
constBindings: boolean
}
banner?: string
} = {
generatedCode: {
constBindings: true
}
}
// Add banner if provided
if (banner) {
rollupOutput.banner = banner
}
const config: UserConfig = {
build: {
lib: {
entry: path.resolve(entry),
name: name,
fileName: typeof fileName === 'function' ? fileName : () => fileName,
formats: formats
},
outDir: path.resolve(outDir),
emptyOutDir: false,
sourcemap: true,
rollupOptions: {
output: rollupOutput
},
target: 'es2015',
minify: minify
},
define: {
'process.env.NODE_ENV': '"production"'
},
esbuild: {
target: 'es2015',
tsconfigRaw: {
compilerOptions: {
module: 'ES2020',
target: 'ES2015'
}
}
}
}
return defineConfig(config)
}

View File

@@ -1,30 +0,0 @@
#!/usr/bin/env node
import AdmZip from 'adm-zip';
import path from 'path';
import { fileURLToPath } from 'url';
import { readFileSync } from 'fs';
// Get __dirname in ESM
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const pkg = JSON.parse(
readFileSync(path.join(__dirname, '../core', 'package.json'), 'utf8')
)
// Create zip instance and add folder
const zip = new AdmZip();
zip.addLocalFolder(path.join(__dirname, '../preview/dist'), 'dashboard');
zip.addLocalFile(path.join(__dirname, '../preview/static', 'og.png'), '.', 'preview.png');
zip.addFile("documentation.url", Buffer.from("[InternetShortcut]\nURL = https://tabler.io/docs"));
// Folder to zip and output path
const outputZipPath = path.join(__dirname, '../packages-zip', `tabler-${pkg.version}.zip`);
// Write the zip file
zip.writeZip(outputZipPath);
console.log(`Zipped folder to ${outputZipPath}`);

46
.build/zip-package.ts Normal file
View File

@@ -0,0 +1,46 @@
#!/usr/bin/env node
import AdmZip from 'adm-zip'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { readFileSync } from 'node:fs'
// Get __dirname in ESM
const __dirname = path.dirname(fileURLToPath(import.meta.url))
interface PackageJson {
version: string
[key: string]: unknown
}
const pkg: PackageJson = JSON.parse(
readFileSync(path.join(__dirname, '../core', 'package.json'), 'utf8')
)
// Create zip instance and add folder
const zip = new AdmZip()
zip.addLocalFolder(path.join(__dirname, '../preview/dist'), 'dashboard')
zip.addLocalFile(
path.join(__dirname, '../preview/static', 'og.png'),
'.',
'preview.png'
)
zip.addFile(
'documentation.url',
Buffer.from('[InternetShortcut]\nURL = https://tabler.io/docs')
)
// Folder to zip and output path
const outputZipPath = path.join(
__dirname,
'../packages-zip',
`tabler-${pkg.version}.zip`
)
// Write the zip file
zip.writeZip(outputZipPath)
console.log(`Zipped folder to ${outputZipPath}`)

View File

@@ -0,0 +1,5 @@
---
"@tabler/core": patch
---
Refactored Bootstrap exports to use single source of truth in `bootstrap.js` and removed duplicate exports from `tabler.js` for better maintainability.

View File

@@ -0,0 +1,5 @@
---
"@tabler/core": patch
---
Fixed `.btn-icon` to be square by aligning `min-width` calculation with base `.btn` formula.

View File

@@ -0,0 +1,6 @@
---
"@tabler/core": minor
---
Added `.card-gradient` component with gradient variants, direction modifiers, and animated backgrounds.

View File

@@ -0,0 +1,6 @@
---
"@tabler/preview": minor
---
Added new `card-gradients.html` page showcasing various gradient card styles and components.

View File

@@ -0,0 +1,6 @@
---
"@tabler/preview": minor
---
Added Change Password modal with current password, new password with strength indicator, confirm password validation, and show/hide password toggles.

View File

@@ -0,0 +1,6 @@
---
"@tabler/preview": minor
---
Added Confirm Delete modal with warning icon, confirmation checkbox, and JavaScript validation to enable delete button only when confirmed.

View File

@@ -0,0 +1,6 @@
---
"@tabler/preview": minor
---
Added new Crypto Dashboard page with cryptocurrency portfolio overview, market data, and order history.

View File

@@ -0,0 +1,6 @@
---
"@tabler/preview": patch
---
Added crypto markets and orders data files (`crypto-markets.json`, `crypto-orders.json`) for cryptocurrency dashboard functionality.

View File

@@ -0,0 +1,5 @@
---
"@tabler/docs": patch
---
Updated documentation to explain font sizing and system color CSS variables (`--tblr-primary-rgb`, `--tblr-secondary`, `--tblr-tertiary`, `--tblr-link-color`, `--tblr-gray-*`).

View File

@@ -0,0 +1,6 @@
---
"@tabler/preview": minor
---
Added Edit Profile modal with avatar upload, personal information fields, social links, and date of birth.

View File

@@ -0,0 +1,5 @@
---
"@tabler/preview": patch
---
Update Tabler Icons to v3.35.0

View File

@@ -0,0 +1,5 @@
---
"@tabler/core": patch
---
Updated `$border-color-translucent-dark` from `rgba(72, 110, 149, 0.14)` to `rgba(128, 150, 172, 0.2)` to improve visibility of form checkboxes and other form elements in dark mode.

View File

@@ -0,0 +1,5 @@
---
"@tabler/core": patch
---
Fixed `.input-icon-addon` z-index issue with form validation feedback and added default height.

View File

@@ -0,0 +1,5 @@
---
"@tabler/core": patch
---
Fixed status color classes to use CSS variables instead of hardcoded values and include social colors (bitbucket, facebook, etc.) in status class generation.

View File

@@ -0,0 +1,5 @@
---
"@tabler/core": patch
---
Fixed white space on left side when scrollbar is present by replacing `margin-inline-start: calc(100vw - 100%)` with `scrollbar-gutter: stable` on `html` element, with `overflow-y: scroll` fallback for unsupported browsers.

View File

@@ -0,0 +1,6 @@
---
"@tabler/core": patch
---
Updated flags and avatars styling for better visual consistency.

View File

@@ -0,0 +1,5 @@
---
"@tabler/core": patch
---
Update SCSS to use logical properties

View File

@@ -0,0 +1,7 @@
---
"@tabler/core": minor
"@tabler/docs": patch
---
Added Geist font family integration.

View File

@@ -0,0 +1,6 @@
---
"@tabler/core": minor
"@tabler/preview": minor
---
Added language selector dropdown to navbar with flag indicators for multilingual support.

View File

@@ -0,0 +1,6 @@
---
"@tabler/core": patch
---
Added `border-top-left-radius` and `border-top-right-radius` to first and last child elements in `.card-table` for proper corner rounding.

View File

@@ -0,0 +1,5 @@
---
"@tabler/core": patch
---
Added `media-print` mixin and print styles to hide interactive components during printing.

View File

@@ -0,0 +1,6 @@
---
"@tabler/preview": minor
---
Refactored page-menu structure for dashboards and updated navigation menu organization.

View File

@@ -0,0 +1,6 @@
---
"@tabler/core": patch
---
Migrated `rgba()` functions to modern CSS color functions (`color-mix()` and `color-transparent()`) for better browser support and cleaner code. Replaced `rgba(var(--#{$prefix}*-rgb), ...)` with `color-mix(in srgb, var(--#{$prefix}*) ..., transparent)`, static percentage `color-mix()` with `color-transparent()`, and `rgba($variable, ...)` with `color-transparent($variable, ...)`.

View File

@@ -0,0 +1,8 @@
---
"@tabler/core": minor
"@tabler/preview": minor
"@tabler/docs": minor
---
Migrated build system from Rollup to Vite across all packages. Replaced `rollup.config.mjs` with `vite.config.mjs` and updated build scripts to use `vite build` instead of `rollup`. Build outputs remain identical (UMD and ESM formats) with no breaking changes for end users.

View File

@@ -0,0 +1,7 @@
---
"@tabler/core": minor
"@tabler/preview": minor
---
Refactored navbar-side component by consolidating separate include files (apps, language, notifications, theme, user) into a single `navbar-side.html` file for better maintainability.

View File

@@ -0,0 +1,6 @@
---
"@tabler/preview": minor
---
Added New Task modal with fields for task name, description, assigned user, priority, due date, and category tags.

View File

@@ -0,0 +1,6 @@
---
"@tabler/core": patch
---
Removed redundant nullish coalescing operator from `html` option in popover and tooltip initialization.

View File

@@ -0,0 +1,7 @@
---
"@tabler/core": patch
"@tabler/preview": patch
---
Added Driver.js library integration and Tour demo page for interactive product tours and onboarding guides.

View File

@@ -0,0 +1,6 @@
---
"@tabler/preview": minor
---
Added new Task List page with tables showing tasks organized by status (Upcoming, In Progress, Completed) and modal dialog for adding new tasks.

View File

@@ -0,0 +1,6 @@
---
"@tabler/preview": patch
---
Updated `@tabler/icons` to v3.36.1.

View File

@@ -0,0 +1,7 @@
---
"@tabler/core": minor
"@tabler/preview": minor
---
Upgraded `apexcharts` from `3.54.1` to `5.3.6` and added CSS variables (`--chart-{id}-color-{index}`) for dynamic chart colors to fix compatibility with the new version.

View File

@@ -0,0 +1,5 @@
---
"@tabler/preview": patch
---
Added comprehensive All Elements page with all UI components and Bootstrap elements

View File

@@ -0,0 +1,38 @@
---
description: Git Branch Naming Rules
globs:
alwaysApply: true
---
## Branch naming
- Use lowercase branch names.
- Use a type prefix and a short description in kebab-case.
- Format: `<type>/<short-description>` or `<type>/<issue-id>-<short-description>`
- Use `gh-123` as the issue id format (avoid `#` in branch names).
### Allowed types
- `feat` - new features
- `fix` - bug fixes
- `docs` - documentation changes
- `chore` - maintenance / tooling
- `refactor` - code refactoring (no behavior change)
- `test` - tests only
- `build` - build system changes
- `ci` - CI changes
- `perf` - performance improvements
- `style` - formatting / lint-only changes
- `revert` - reverting prior changes
### Examples
- `feat/gh-123-add-stepper-component`
- `fix/markdown-table-overflow`
- `docs/gh-45-update-contributing`
- `chore/update-pnpm-lock`
### Notes
- Branch off `dev` by default (unless maintainers request otherwise).
- Avoid spaces, uppercase letters, and special characters other than `/` and `-`.

View File

@@ -0,0 +1,39 @@
---
description: Pull Request Title & Description Rules
globs:
alwaysApply: true
---
## Pull request title
- Write PR titles in **English**.
- Start the title with a **capital letter**.
- Use **present tense** and keep it concise (ideally <= 72 chars).
- Avoid a trailing period.
### Examples
- `Improve markdown table overflow handling`
- `Clarify contributing branch naming`
- `Add onboarding stepper page`
## Pull request description
- Write PR descriptions in **English**.
- Focus on **why** the change is needed and what user-visible effect it has.
- Keep it skimmable: bullets, short paragraphs, clear headings.
### Recommended template
```md
## Summary
- <13 bullets describing the change and why>
## Changes
- <key implementation notes, non-obvious decisions>
```
### Notes
- If you changed SCSS or any package behavior, add a **changeset** describing it (one sentence, with backticks for code elements).
- If a PR is WIP, mark it as draft and prefix the title with `WIP:` only while it is not ready for review.

View File

@@ -1,68 +0,0 @@
name: Argos Tests
on:
push:
branches:
- dev
pull_request:
paths:
- 'preview/**/*.js'
- 'preview/**/*.html'
- 'preview/**/*.scss'
- 'core/**/*.js'
- 'core/**/*.scss'
env:
NODE: 20
permissions:
contents: read
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
# if: github.event.pull_request.draft == false
if: false
steps:
- name: Clone repository
uses: actions/checkout@v5
- name: Cache turbo build setup
uses: actions/cache@v4
with:
path: .turbo
key: ${{ runner.os }}-turbo-${{ github.sha }}
restore-keys: |
${{ runner.os }}-turbo-
- name: Install PNPM
uses: pnpm/action-setup@v4
- name: Set up Node.js
uses: actions/setup-node@v5
with:
node-version: "${{ env.NODE }}"
cache: 'pnpm'
- name: Get installed Playwright version
id: playwright-version
run: echo "PLAYWRIGHT_VERSION=$(node -e "console.log(require('./package.json').devDependencies['@playwright/test'])")" >> $GITHUB_ENV
- name: Cache playwright binaries
uses: actions/cache@v4
id: playwright-cache
with:
path: |
~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ env.PLAYWRIGHT_VERSION }}
- name: Install pnpm dependencies
run: pnpm install
- name: Install Playwright Browsers
run: pnpm exec playwright install --with-deps
if: steps.playwright-cache.outputs.cache-hit != 'true'
- name: Run Playwright tests
run: pnpm run playwright

View File

@@ -9,7 +9,7 @@ on:
env:
FORCE_COLOR: 2
NODE: 20
NODE: 22
jobs:
bundlewatch:
@@ -17,10 +17,10 @@ jobs:
steps:
- name: Clone repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Cache turbo build setup
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: .turbo
key: ${{ runner.os }}-turbo-${{ github.sha }}
@@ -31,7 +31,7 @@ jobs:
uses: pnpm/action-setup@v4
- name: Set up Node.js
uses: actions/setup-node@v5
uses: actions/setup-node@v6
with:
node-version: "${{ env.NODE }}"
cache: 'pnpm'

View File

@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false

32
.github/workflows/lint.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
name: Lint
on:
pull_request: null
env:
NODE: 22
permissions:
contents: read
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v6
- name: Install PNPM
uses: pnpm/action-setup@v4
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: "${{ env.NODE }}"
cache: 'pnpm'
- name: Install pnpm dependencies
run: pnpm install
- name: Lint Markdown
run: pnpm run lint

View File

@@ -12,9 +12,9 @@ jobs:
name: Verify lock file integrity
steps:
- name: Clone Tabler
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Prevent lock file change
uses: xalvarez/prevent-file-change-action@v2
uses: xalvarez/prevent-file-change-action@v3
with:
githubToken: ${{ secrets.GITHUB_TOKEN }}
pattern: Gemfile.lock|pnpm-lock.json|pnpm-lock.yaml

View File

@@ -21,13 +21,13 @@ jobs:
pull-requests: write # to create pull request
steps:
- name: Checkout Repo
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Install PNPM
uses: pnpm/action-setup@v4
- name: Setup Node.js 18
uses: actions/setup-node@v5
uses: actions/setup-node@v6
with:
node-version: "${{ env.NODE }}"
cache: 'pnpm'

View File

@@ -4,7 +4,7 @@ on:
pull_request: null
env:
NODE: 20
NODE: 22
permissions:
contents: read
@@ -14,10 +14,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Cache turbo build setup
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: .turbo
key: ${{ runner.os }}-turbo-${{ github.sha }}
@@ -28,7 +28,7 @@ jobs:
uses: pnpm/action-setup@v4
- name: Set up Node.js
uses: actions/setup-node@v5
uses: actions/setup-node@v6
with:
node-version: "${{ env.NODE }}"
cache: 'pnpm'

45
.github/workflows/type-check.yml vendored Normal file
View File

@@ -0,0 +1,45 @@
name: Type Check
on:
pull_request: null
push:
branches:
- main
- dev
env:
NODE: 20
permissions:
contents: read
jobs:
type-check:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v6
- name: Cache turbo build setup
uses: actions/cache@v4
with:
path: .turbo
key: ${{ runner.os }}-turbo-${{ github.sha }}
restore-keys: |
${{ runner.os }}-turbo-
- name: Install PNPM
uses: pnpm/action-setup@v4
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: "${{ env.NODE }}"
cache: 'pnpm'
- name: Install pnpm dependencies
run: pnpm install
- name: Run type-check
run: pnpm run type-check

7
.gitignore vendored
View File

@@ -36,4 +36,9 @@ package-lock.json
demo/
dist/
packages-zip/
.env
.env
sri.json
# TypeScript
*.tsbuildinfo
.tsbuildinfo

61
.markdownlint.json Normal file
View File

@@ -0,0 +1,61 @@
{
"default": true,
"MD001": {
"level": 2
},
"MD003": {
"style": "atx"
},
"MD004": {
"style": "dash"
},
"MD007": {
"indent": 2
},
"MD009": {
"br_spaces": 2
},
"MD010": false,
"MD012": {
"maximum": 2
},
"MD013": {
"line_length": 120,
"code_blocks": false,
"tables": false,
"headings": false,
"headings_line_length": 120
},
"MD022": true,
"MD025": {
"front_matter_title": ""
},
"MD026": {
"punctuation": ".,;:!"
},
"MD030": {
"ul_single": 1,
"ul_multi": 1,
"ol_single": 1,
"ol_multi": 1
},
"MD031": true,
"MD032": true,
"MD033": false,
"MD034": false,
"MD035": {
"style": "---"
},
"MD036": false,
"MD037": true,
"MD038": true,
"MD039": true,
"MD040": true,
"MD041": {
"front_matter_title": ""
},
"MD046": {
"style": "fenced"
},
"MD047": true
}

2
.nvmrc
View File

@@ -1 +1 @@
20
22

View File

@@ -4,7 +4,17 @@
"printWidth": 320,
"proseWrap": "always",
"semi": false,
"singleQuote": false,
"singleQuote": true,
"quoteProps": "consistent",
"tabWidth": 2,
"trailingComma": "all"
"useTabs": false,
"trailingComma": "all",
"overrides": [
{
"files": "*.scss",
"options": {
"parser": "scss"
}
}
]
}

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2018-2025 The Tabler Authors
Copyright (c) 2018-2026 The Tabler Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -28,13 +28,13 @@ A premium and open source dashboard template with a responsive and high-quality
<p align="center">Browser testing via:</p>
<p align="center">
<a href="https://www.lambdatest.com/" target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/user-attachments/assets/14dd2a0a-bafe-436e-a6cb-29636278c781">
<source media="(prefers-color-scheme: light)" srcset="https://github.com/user-attachments/assets/d3dede5a-d702-47c3-bb66-4d887948ed83">
<img src="https://github.com/user-attachments/assets/d3dede5a-d702-47c3-bb66-4d887948ed83" alt="labmdatest" width="296">
</picture>
</a>
<a href="https://www.testmu.ai" target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/user-attachments/assets/f0967860-31ad-4078-850b-40b0abc95582" />
<source media="(prefers-color-scheme: light)" srcset="https://github.com/user-attachments/assets/55ac290a-6729-44aa-bbc3-4c5e909facbf" />
<img src="https://github.com/user-attachments/assets/86bcbe29-eb8d-4273-a381-5ce17d4ca92d" alt="TestMu AI" width="296">
</picture>
</a>
</p>
## 🔎 Preview

View File

@@ -1,18 +1,20 @@
#!/usr/bin/env node
'use strict'
import { readFileSync, writeFileSync } from 'node:fs';
import { join, dirname, basename } from 'node:path';
import { readFileSync, writeFileSync } from 'node:fs'
import { join, dirname, basename } from 'node:path'
import { fileURLToPath } from 'node:url'
import { sync } from 'glob';
import banner from '../../shared/banner/index.mjs';
import { sync } from 'glob'
import banner from '../../shared/banner/index.mjs'
const __dirname = dirname(fileURLToPath(import.meta.url))
const styles = sync(join(__dirname, '..', 'dist', 'css', '*.css'))
const styles: string[] = sync(join(__dirname, '..', 'dist', 'css', '*.css'))
const plugins = {
interface Plugins {
[key: string]: string
}
const plugins: Plugins = {
'tabler-flags': 'Flags',
'tabler-flags.rtl': 'Flags RTL',
'tabler-marketing': 'Marketing',
@@ -25,22 +27,24 @@ const plugins = {
'tabler-vendors.rtl': 'Vendors RTL',
}
styles.forEach((file, i) => {
styles.forEach((file: string) => {
const content = readFileSync(file, 'utf8')
const filename = basename(file)
const pluginKey = Object.keys(plugins).find(plugin => filename.includes(plugin))
const plugin = plugins[pluginKey]
const pluginKey = Object.keys(plugins).find((plugin: string) => filename.includes(plugin))
const plugin = pluginKey ? plugins[pluginKey] : undefined
const regex = /^(@charset ['"][a-zA-Z0-9-]+['"];?)\n?/i
let newContent = ''
const bannerText = banner(plugin)
if (content.match(regex)) {
newContent = content.replace(regex, (m, m1) => {
return `${m1}\n${banner(plugin)}\n`
newContent = content.replace(regex, (m: string, m1: string) => {
return `${m1}\n${bannerText}\n`
})
} else {
newContent = `${banner(plugin)}\n${content}`
newContent = `${bannerText}\n${content}`
}
writeFileSync(file, newContent, 'utf8')
})
})

View File

@@ -0,0 +1,84 @@
import { readFileSync } from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
// Get __dirname in ES modules
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
// File paths (relative to core/.build directory)
const bootstrapPath = path.join(__dirname, '../node_modules/bootstrap/scss/_variables.scss')
const tablerPath = path.join(__dirname, '../scss/_variables.scss')
// Function to extract variable names from SCSS file
function extractVariables(filePath: string): Set<string> {
const content = readFileSync(filePath, 'utf8')
const variables = new Set<string>()
// Regex to find SCSS variables
// Looks for patterns like: $variable-name: value
// Includes variables in maps and lists
const variableRegex = /\$([a-zA-Z0-9_-]+)\s*[:=]/g
let match: RegExpExecArray | null
while ((match = variableRegex.exec(content)) !== null) {
const varName = match[1]
variables.add(varName)
}
return variables
}
// Main function
function compareVariables(): void {
console.log('Analyzing Bootstrap variables...')
const bootstrapVars = extractVariables(bootstrapPath)
console.log(`Found ${bootstrapVars.size} variables in Bootstrap\n`)
console.log('Analyzing Tabler variables...')
const tablerVars = extractVariables(tablerPath)
console.log(`Found ${tablerVars.size} variables in Tabler\n`)
// Find variables that are in Bootstrap but not in Tabler
const missingInTabler: string[] = []
for (const varName of bootstrapVars) {
if (!tablerVars.has(varName)) {
missingInTabler.push(varName)
}
}
// Sort alphabetically
missingInTabler.sort()
console.log('='.repeat(60))
console.log(`Variables in Bootstrap that are missing in Tabler: ${missingInTabler.length}`)
console.log('='.repeat(60))
if (missingInTabler.length === 0) {
console.log('All Bootstrap variables are present in Tabler!')
} else {
console.log('\nList of missing variables:\n')
missingInTabler.forEach((varName: string, index: number) => {
console.log(`${(index + 1).toString().padStart(4)}. $${varName}`)
})
}
// Optionally: show statistics
console.log('\n' + '='.repeat(60))
console.log('Statistics:')
console.log(` Bootstrap: ${bootstrapVars.size} variables`)
console.log(` Tabler: ${tablerVars.size} variables`)
console.log(` Missing: ${missingInTabler.length} variables`)
console.log(` Coverage: ${((1 - missingInTabler.length / bootstrapVars.size) * 100).toFixed(1)}%`)
console.log('='.repeat(60))
}
// Run analysis
try {
compareVariables()
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error)
console.error('Error during analysis:', errorMessage)
process.exit(1)
}

View File

@@ -1,19 +1,30 @@
#!/usr/bin/env node
'use strict'
import { existsSync, mkdirSync, lstatSync } from 'fs'
import { existsSync, mkdirSync } from 'node:fs'
import { emptyDirSync, copySync } from 'fs-extra/esm'
import libs from '../libs.json' with { type: 'json' }
import { fileURLToPath } from 'url'
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url'
import { join, dirname } from 'node:path'
const __dirname = dirname(fileURLToPath(import.meta.url))
interface LibConfig {
npm?: string
js?: string[]
css?: string[]
head?: boolean
}
interface Libs {
[key: string]: LibConfig
}
const libsData = libs as Libs
emptyDirSync(join(__dirname, '..', 'dist/libs'))
for(const name in libs) {
const { npm } = libs[name]
for (const name in libsData) {
const { npm } = libsData[name]
if (npm) {
const from = join(__dirname, '..', `node_modules/${npm}`)
@@ -23,11 +34,12 @@ for(const name in libs) {
if (!existsSync(to)) {
mkdirSync(to, { recursive: true })
}
copySync(from, to, {
dereference: true,
})
console.log(`Successfully copied ${npm}`)
}
}
}

View File

@@ -1,13 +1,18 @@
const crypto = require('node:crypto');
const fs = require('node:fs');
const path = require('node:path');
const sh = require('shelljs');
import * as crypto from 'node:crypto'
import { readFileSync, writeFileSync } from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
sh.config.fatal = true
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const configFile = path.join(__dirname, '../../shared/data/sri.json')
const files = [
interface FileConfig {
file: string
configPropertyName: string
}
const files: FileConfig[] = [
{
file: 'dist/css/tabler.min.css',
configPropertyName: 'css'
@@ -80,28 +85,37 @@ const files = [
file: 'dist/js/tabler-theme.min.js',
configPropertyName: 'js-theme'
},
// {
// file: 'dist/preview/css/demo.min.css',
// configPropertyName: 'demo-css'
// },
// {
// file: 'dist/preview/js/demo.min.js',
// configPropertyName: 'demo-js'
// },
]
for (const { file, configPropertyName } of files) {
fs.readFile(path.join(__dirname, '..', file), 'utf8', (error, data) => {
if (error) {
function generateSRI(): void {
const sriData: Record<string, string> = {}
for (const { file, configPropertyName } of files) {
try {
const filePath = path.join(__dirname, '..', file)
const data = readFileSync(filePath, 'utf8')
const algorithm = 'sha384'
const hash = crypto.createHash(algorithm).update(data, 'utf8').digest('base64')
const integrity = `${algorithm}-${hash}`
console.log(`${configPropertyName}: ${integrity}`)
sriData[configPropertyName] = integrity
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error)
console.error(`Error processing ${file}:`, errorMessage)
throw error
}
}
const algorithm = 'sha384'
const hash = crypto.createHash(algorithm).update(data, 'utf8').digest('base64')
const integrity = `${algorithm}-${hash}`
writeFileSync(configFile, JSON.stringify(sriData, null, 2) + '\n', 'utf8')
}
console.log(`${configPropertyName}: ${integrity}`)
try {
generateSRI()
} catch (error) {
console.error('Failed to generate SRI:', error)
process.exit(1)
}
sh.sed('-i', new RegExp(`^(\\s+"${configPropertyName}":\\s+["'])\\S*(["'])`), `$1${integrity}$2`, configFile)
})
}

View File

@@ -0,0 +1,53 @@
#!/usr/bin/env node
import { existsSync, mkdirSync } from 'node:fs'
import { copySync } from 'fs-extra/esm'
import { fileURLToPath } from 'node:url'
import { join, dirname } from 'node:path'
const __dirname = dirname(fileURLToPath(import.meta.url))
const fromDir = join(__dirname, '..', 'node_modules/geist/dist/fonts')
const toDir = join(__dirname, '..', 'fonts')
// Create fonts directory if it doesn't exist
if (!existsSync(toDir)) {
mkdirSync(toDir, { recursive: true })
}
// Copy geist-mono fonts
const monoFrom = join(fromDir, 'geist-mono')
const monoTo = join(toDir, 'geist-mono')
if (existsSync(monoFrom)) {
if (!existsSync(monoTo)) {
mkdirSync(monoTo, { recursive: true })
}
copySync(monoFrom, monoTo, {
dereference: true,
})
console.log(`Successfully copied geist-mono fonts`)
} else {
console.warn(`Warning: geist-mono fonts not found at ${monoFrom}`)
}
// Copy geist-sans fonts
const sansFrom = join(fromDir, 'geist-sans')
const sansTo = join(toDir, 'geist-sans')
if (existsSync(sansFrom)) {
if (!existsSync(sansTo)) {
mkdirSync(sansTo, { recursive: true })
}
copySync(sansFrom, sansTo, {
dereference: true,
})
console.log(`Successfully copied geist-sans fonts`)
} else {
console.warn(`Warning: geist-sans fonts not found at ${sansFrom}`)
}

View File

@@ -1,47 +0,0 @@
import path from 'node:path'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
import { babel } from '@rollup/plugin-babel'
import { nodeResolve } from '@rollup/plugin-node-resolve'
import replace from '@rollup/plugin-replace'
import banner from '../../shared/banner/index.mjs'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const ESM = process.env.ESM === 'true'
const THEME = process.env.THEME === 'true'
const external = []
const plugins = [
babel({
exclude: 'node_modules/**',
babelHelpers: 'bundled'
})
]
plugins.push(
replace({
'process.env.NODE_ENV': '"production"',
preventAssignment: true
}),
nodeResolve()
)
const destinationFile = `tabler${THEME ? '-theme' : ''}${ESM ? '.esm' : ''}`
const rollupConfig = {
input: path.resolve(__dirname, `../js/tabler${THEME ? '-theme' : ''}.js`),
output: {
banner: banner(),
file: path.resolve(__dirname, `../dist/js/${destinationFile}.js`),
format: ESM ? 'esm' : 'umd',
generatedCode: 'es2015'
},
external,
plugins
}
if (!ESM) {
rollupConfig.output.name = `tabler${THEME ? '-theme' : ''}`
}
export default rollupConfig

View File

@@ -0,0 +1,30 @@
import path from 'node:path'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
import { createViteConfig } from '../../.build/vite.config.helper'
import getBanner from '../../shared/banner/index.mjs'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const baseName = process.env.BASE_NAME || 'tabler'
const entryFile = baseName
const libraryName = baseName
const bannerText = getBanner()
const entryPath = path.resolve(__dirname, `../js/${entryFile}`)
const entry = `${entryPath}.ts`
export default createViteConfig({
entry: entry,
name: libraryName,
fileName: (format) => {
const esmSuffix = format === 'es' ? '.esm' : ''
return `${baseName}${esmSuffix}.js`
},
formats: ['es', 'umd'],
outDir: path.resolve(__dirname, '../dist/js'),
banner: bannerText,
minify: false
})

View File

@@ -106,7 +106,6 @@
### Minor Changes
- a2640e2: Add Playwright configuration and visual regression tests
- d3ae77c: Enable `scrollSpy` in `countup` module
- bd3d959: Refactor SCSS files to replace divide function with calc
- cb278c7: Add Segmented Control component

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More