1
0
mirror of https://github.com/tabler/tabler.git synced 2026-07-15 01:51:43 +04:00

Merge branch 'dev' of https://github.com/tabler/tabler into fix-1927

This commit is contained in:
Bartek
2026-02-26 14:36:17 +01:00
543 changed files with 17492 additions and 8244 deletions
+9 -5
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
-63
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
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)
})
+76
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)
}
-30
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
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}`)
+6
View File
@@ -0,0 +1,6 @@
---
"@tabler/core": minor
"@tabler/preview": minor
---
Added Pay page with dedicated layout, navigation link, and card/PayPal payment form.
@@ -0,0 +1,7 @@
---
"@tabler/core": minor
"@tabler/preview": minor
"@tabler/docs": patch
---
Added background pattern utilities and documentation, including updated preview demos.
+5
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.
+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/core": patch
---
Fixed `.btn-icon` to be square by aligning `min-width` calculation with base `.btn` formula.
+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/core": minor
---
Added `.btn-ghost` button variant with transparent background and hover effects.
+6
View File
@@ -0,0 +1,6 @@
---
"@tabler/core": minor
---
Added `.card-gradient` component with gradient variants, direction modifiers, and animated backgrounds.
+6
View File
@@ -0,0 +1,6 @@
---
"@tabler/preview": minor
---
Added new `card-gradients.html` page showcasing various gradient card styles and components.
+6
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.
+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/preview": minor
---
Added color palette to signing component.
+6
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.
+6
View File
@@ -0,0 +1,6 @@
---
"@tabler/preview": minor
---
Added new Crypto Dashboard page with cryptocurrency portfolio overview, market data, and order history.
+6
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.
+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/core": patch
---
Fixed icon alignment for `.btn-sm` and `.btn-xl` sizes.
@@ -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-*`).
@@ -0,0 +1,5 @@
---
"@tabler/docs": patch
---
Updated UI component documentation to use singular file names and frontmatter fields (`title`, `summary`, `description`), and added redirects from legacy plural routes.
+6
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.
+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/preview": patch
---
Update Tabler Icons to v3.35.0
@@ -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.
+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/core": patch
---
Fixed gray theme custom properties output using SCSS interpolation and updated default `$body-color` to `$gray-500`.
+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/core": patch
---
Fixed `.input-icon-addon` z-index issue with form validation feedback and added default height.
@@ -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.
+5
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.
+6
View File
@@ -0,0 +1,6 @@
---
"@tabler/core": patch
---
Updated flags and avatars styling for better visual consistency.
+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/core": patch
---
Fixed mixed declarations in SCSS.
+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/core": patch
---
Update SCSS to use logical properties
+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/preview": patch
---
Updated preview templates to replace deprecated `font-weight-*` classes with Bootstrap-compatible `fw-*` utility classes.
+7
View File
@@ -0,0 +1,7 @@
---
"@tabler/core": minor
"@tabler/docs": patch
---
Added Geist font family integration.
+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/core": patch
---
Implement `border-radius` mixin across various SCSS files
+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/core": patch
---
Updated `stroke-width` for `.icon-sm` from `1` to `1.5` for better visibility.
+6
View File
@@ -0,0 +1,6 @@
---
"@tabler/core": minor
"@tabler/preview": minor
---
Added language selector dropdown to navbar with flag indicators for multilingual support.
+5
View File
@@ -0,0 +1,5 @@
---
---
Use `.nvmrc` for Node version in CI workflows
+6
View File
@@ -0,0 +1,6 @@
---
"@tabler/core": patch
"@tabler/preview": patch
---
Added `bg-blur` utility and increased `container-tight` width for layout flexibility.
+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/preview": patch
---
Updated icons to v3.34.1 with 75 new icons.
+6
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.
+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/core": patch
---
Added `media-print` mixin and print styles to hide interactive components during printing.
+6
View File
@@ -0,0 +1,6 @@
---
"@tabler/preview": minor
---
Refactored page-menu structure for dashboards and updated navigation menu organization.
+6
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, ...)`.
+8
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.
+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/preview": patch
---
Updated activity messages.
+7
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.
+6
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.
+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/preview": minor
---
Added new onboarding page with progress indicator and navigation layout.
+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/core": patch
---
Fixed double bottom border in tables.
+6
View File
@@ -0,0 +1,6 @@
---
"@tabler/core": minor
"@tabler/preview": minor
---
Added Progress Background component with text labels and value display.
+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/core": minor
---
Added `.progress-lg` and `.progress-xl` size variants for the progress component.
+6
View File
@@ -0,0 +1,6 @@
---
"@tabler/core": minor
"@tabler/preview": minor
---
Added Progress Steps component for step-by-step navigation indicators.
+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/core": patch
---
Added smooth transitions for progress bar `width` and `background-color` changes.
+7
View File
@@ -0,0 +1,7 @@
---
"@tabler/core": patch
"@tabler/preview": patch
"@tabler/docs": patch
---
Added `.prose` alias for markdown content and updated preview/docs references and redirects.
+6
View File
@@ -0,0 +1,6 @@
---
"@tabler/core": patch
---
Removed redundant nullish coalescing operator from `html` option in popover and tooltip initialization.
+7
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.
+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/core": patch
---
Updated skip-link to use `visually-hidden` for improved accessibility.
+6
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.
+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/core": patch
---
Updated deprecated global Sass functions to module equivalents (`map.merge`, `string.slice`, `math.percentage`, etc.).
+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/core": patch
---
Updated Bootstrap to v5.3.8.
+6
View File
@@ -0,0 +1,6 @@
---
"@tabler/core": patch
"@tabler/preview": patch
---
Updated trending component to use `arrow-up`/`arrow-down` instead of `trending-up`/`trending-down`.
+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/docs": patch
---
Fixed Docs search display in dark mode.
+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/preview": patch
---
Fixed responsive layouts on the Form Elements page.
+6
View File
@@ -0,0 +1,6 @@
---
"@tabler/preview": patch
---
Updated `@tabler/icons` to v3.36.1.
+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/core": patch
---
Updated shadow tokens (`--tblr-shadow-*`) to use the new `xs``2xl` and `overlay` values.
+7
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.
+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/preview": patch
---
Added comprehensive All Elements page with all UI components and Bootstrap elements
+38
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 `-`.
+100
View File
@@ -0,0 +1,100 @@
---
description: Cursor Rules for Tabler Changesets
globs:
alwaysApply: true
---
### File Structure
- Each change must be in a separate changeset file in `.changeset/` directory
- Use descriptive kebab-case filenames (e.g., `progress-sizes.md`, `button-ghost.md`)
- Follow the standard changeset format with frontmatter and description
### Change Description Format
- **One sentence per changeset** - keep descriptions concise and focused
- Use **backticks for code elements**: classes (`.btn-ghost`), properties (`stroke-width`), values (`1.5`), icons (`arrow-up`)
- Start with action verbs: "Added", "Updated", "Fixed", "Removed"
- Be specific about what was changed
### Version Bump Guidelines
- **Major**: Breaking changes, complete rewrites
- **Minor**: New features, new components, new pages, significant enhancements
- **Patch**: Bug fixes, small improvements, style updates, accessibility fixes
### Package Selection
- `"@tabler/core"`: Changes to SCSS, core functionality, CSS classes
- `"@tabler/preview"`: New pages, demo updates, preview-specific changes
- `"@tabler/docs"`: Documentation updates
- Use multiple packages when change affects multiple areas
### Examples
#### New Feature (Minor)
```md
---
"@tabler/core": minor
"@tabler/preview": minor
---
Added Progress Steps component for step-by-step navigation indicators.
```
#### Bug Fix (Patch)
```md
---
"@tabler/core": patch
---
Updated `stroke-width` for `.icon-sm` from `1` to `1.5` for better visibility.
```
#### New Page (Minor)
```md
---
"@tabler/preview": minor
---
Added new onboarding page with progress indicator and navigation layout.
```
#### Style Enhancement (Patch)
```md
---
"@tabler/core": patch
---
Added smooth transitions for progress bar width and background color changes.
```
### Code Formatting Rules
- Class names: `.btn-ghost`, `.progress-lg`, `.icon-sm`
- CSS properties: `stroke-width`, `background-color`, `width`
- Values: `1.5`, `transparent`, `100%`
- Icon names: `arrow-up`, `arrow-down`, `trending-up`
- HTML attributes: `aria-label`, `role`, `data-*`
- JavaScript functions: `addEventListener()`, `querySelector()`
### Common Patterns
- **Component additions**: "Added [ComponentName] component for [purpose]"
- **Size variants**: "Added [size] size variant for [component] (`.class-size`)"
- **Style fixes**: "Fixed [issue] in [component/element]"
- **Icon updates**: "Updated [component] to use `new-icon` instead of `old-icon`"
- **Accessibility**: "Improved accessibility by [specific change]"
### Commit Message Format
Use English for commit messages following conventional commit format when possible:
- `feat: add progress steps component`
- `fix: update icon stroke width for better visibility`
- `style: add smooth transitions to progress bars`
+341
View File
@@ -0,0 +1,341 @@
---
description: Markdown documentation writing standards for docs pages
globs: docs/content/**/*.md
alwaysApply: false
---
# Markdown Documentation Rules
## Scope
- Apply these rules when creating or editing Markdown documentation files.
- Write technical documentation in English.
- Use simple English: short sentences, common words, and direct instructions.
## Frontmatter
- Use static YAML frontmatter only (no Liquid in frontmatter).
- Include `title`, `summary`, and `description` for docs pages in `docs/content/**`.
- For component docs, use singular naming in frontmatter and filename:
`title: Card`, `summary` and `description` written with a singular component form, and filename `card.md` (not `cards.md`).
- Add optional frontmatter `related` entries (instead of a `Related` content section).
- In `related`, always use absolute docs paths in `url` (without `./`), for example:
`ui/components/breadcrumb`.
- Use this structure:
```yaml
related:
- name: Pagination
url: ui/components/pagination
- name: Dropdowns
url: ui/components/dropdowns
```
- If documentation describes a component defined in SCSS, add `source-scss` with a relative path
inside `core/scss/` (for example: `source-scss: ui/buttons.scss`).
- If a component extends a Bootstrap component, add `bootstrap-url: components/<name>` in frontmatter
(for example: `bootstrap-url: components/buttons`).
- Add versioning notes for API and class lifecycle changes using fields like
`added-in: 1.4.0` and `deprecated-in: 1.2.0`.
- If documentation introduces a new component, add a changeset with a `minor` version bump.
- Components should include an optional `classnames` section in frontmatter.
- Add only groups that exist for a given component. Do not include empty or irrelevant groups.
- `classnames` entries must match classes that exist in the relevant SCSS source files.
- Do not document classes that are not defined in SCSS, deprecated, or internal-only.
- You can add `status` to class entries: `stable`, `deprecated`, or `experimental`.
- If `status` is not provided, treat the class as `stable` by default.
- Allowed `classnames` groups and meanings:
- `component`: Main component class.
- `modifier`: Modifies behavior of component or part.
- `part`: Part of a component.
- `style`: Visual style of component or part.
- `behavior`: Behavioural style of component or part.
- `color`: Color of component or part.
- `size`: Size of component or part.
- `placement`: Placement of component or part.
- `direction`: Direction of component or part.
- `variable`: CSS variable.
- `variant`: Use like `variant:utility` to apply a utility conditionally.
- Use this structure:
```yaml
classnames:
component:
- class: btn
desc: Main button class
part:
- class: btn-group
desc: Group container for buttons
modifier:
- class: btn-block
desc: Full width button
status: stable
style:
- class: btn-outline
desc: Outline style button
behavior:
- class: btn-disabled
desc: Disabled visual state
status: deprecated
color:
- class: btn-primary
desc: Primary color button
size:
- class: btn-sm
desc: Small size button
placement:
- class: dropdown-menu-end
desc: Align dropdown menu to end
direction:
- class: carousel-vertical
desc: Vertical carousel direction
variable:
- class: --tblr-btn-padding-x
desc: Horizontal button padding variable
variant:
- class: hover:bg-primary
desc: Apply background color on hover
status: experimental
```
## Structure and readability
- Start with a short purpose statement, then explain usage and variants.
- Keep heading hierarchy consistent (`##` then `###`), without skipping levels.
- Keep core documentation sections as `H2` headings in this order:
`Overview`, `Installation` (optional), `Variants` (for components that support variants or `Usage` for components that don't support variants), `Accessibility`,
`Examples` (optional), `SCSS variables` (optional), `Migration notes` (optional).
- In `Overview`, add one visual component preview without code (`hide-code=true`).
- Use descriptive section headings that are stable for linking.
- Vary intro phrasing across pages; avoid repeating the same sentence pattern.
## Markdown style
- Follow `.markdownlint.json` conventions (ATX headings, `-` lists, fenced code blocks).
- Keep line length within 120 characters where practical.
- Keep examples aligned with current API/classes and practical usage.
- Use active voice and direct verbs (`Use`, `Add`, `Set`).
- Keep one main idea per sentence.
- Do not add meta authoring notes in docs text (for example: "this snippet is copy-paste ready").
## Terminology and section naming
- Use consistent terminology across pages; one term per concept.
- Use clear and consistent section names across component docs.
- Prefer shared section labels such as `Overview`, `Accessibility`, `Examples` (optional), and `Migration notes`.
- You can add an `SCSS variables` section for components with documented SCSS tokens/variables.
### Recommended section order
```md
## Overview
## Installation (optional)
## Variants or Usage/API
## Accessibility
## Examples (optional)
## SCSS variables (optional)
## Migration notes (optional)
```
## Project-specific docs patterns
- In docs examples, prefer `{% capture html %}...{% endcapture %}` and, when needed, add `{% capture code %}...{% endcapture %}` for code snippets.
- Use `{% include "docs/example.html" ... %}` to render visual component previews in any relevant section
(`Overview`, `Variants`, `Usage/API`, or `Examples`).
- Before each `{% include "docs/example.html" ... %}`, add 1-2 short sentences that describe
the component state or behavior shown in the preview.
- For examples with explicit source snippets, use `{% include "docs/example.html" html=html code=code %}`.
- For visual-only examples, use `{% include "docs/example.html" html=html hide-code=true %}`.
- If an example shows multiple elements as a list, use `separated` to display items with clean spacing:
`{% include "docs/example.html" html=html separated %}`.
- Add `centered` when the example should be centered (for example, a list of buttons):
`{% include "docs/example.html" html=html separated centered %}`.
- If elements are wide, you can stack them vertically with `vertical` (for example):
`{% include "docs/example.html" html=html centered vertical %}`.
- Use `raw` when you need to render the example without extra preview wrappers
(no padding, no centering), for example full page layout previews:
`{% include "docs/example.html" html=html raw %}`.
- Use `column` to render content in a narrow column (about 25rem), useful for components
commonly presented in columns (for example stat cards):
`{% include "docs/example.html" html=html column %}`.
- For `SCSS variables` sections, use:
`{% scss-docs "alert-variables" "ui/_alerts.scss" %}`.
- This renders the fragment between `// scss-docs-start alert-variables` and `// scss-docs-end`
from the given SCSS file (path relative to `core/scss/`).
- If an SCSS file contains a `scss-docs-start` block, it should be included in documentation
via a matching `{% scss-docs %}` entry.
- Use `hide-code=true` only when source markup is not useful to the reader.
- If an example needs a different background, set it with the include attribute `bg="surface-primary"`.
- Treat `html` as the visual preview layer and `code` as the clean source layer shown to users.
- Keep `code` concise and practical: include only the essential markup needed to explain usage.
- Do not duplicate visual wrapper-only structure in `code` when it exists only to style the preview.
- Do not promote deprecated patterns; when needed, mention the replacement.
- Add accessibility attributes (for example `aria-label`) in interactive examples.
## Publishing checklist
- Verify that examples render correctly.
- Verify that internal and external links work.
- Verify heading hierarchy and section naming consistency.
- Verify code snippets are accurate and minimal for the documented use case.
- Validate documented classes against SCSS files before publishing.
- Ensure every class in `classnames` maps to a real SCSS class or variable.
- Ensure every class listed in `classnames` appears at least once in page content or examples.
- For interactive components, add a short accessibility mini-check (`aria-label`, keyboard focus, semantic markup).
## Documentation structure examples
### Minimal component page structure
```md
---
title: Component name
summary: Short one-sentence summary of what this component does.
description: Practical description of when and why to use this component.
---
## Overview
One short paragraph explaining purpose and primary use case.
Show the component in its default state so users can recognize it immediately.
Keep this first example focused on appearance and context.
{% capture html -%}
<button class="btn btn-primary">Primary button</button>
{% endcapture %}
{% include "docs/example.html" html=html hide-code=true %}
## Variants
### Outline
The outline variant presents a lighter visual emphasis than a filled primary action.
Use it for secondary actions in dense interfaces.
{% capture html -%}
<button class="btn">Secondary</button>
{% endcapture %}
{% include "docs/example.html" html=html %}
## Accessibility
- Use clear labels for interactive controls.
- Add `aria-label` for icon-only buttons.
## Examples (optional, visual usage)
### Default
The default variant represents the primary action style.
Use it as a baseline before introducing additional variants.
{% capture html -%}
<button class="btn btn-primary">Save</button>
{% endcapture %}
{% include "docs/example.html" html=html %}
```
### Visual preview patterns (usable in any section)
```md
## Variants
### Default
The default variant represents the primary action style.
Use it as a baseline before introducing additional variants.
{% capture html -%}
<button class="btn btn-primary">Primary button</button>
{% endcapture %}
{% include "docs/example.html" html=html %}
### With icon
This variant combines a leading icon with a text label.
Use it for actions that benefit from an additional visual cue.
{% capture html -%}
<button class="btn btn-primary">
{% include "ui/icon.html" icon="plus" %}
Add item
</button>
{% endcapture %}
{% include "docs/example.html" html=html %}
### Custom background
This variant presents the component on a dark background surface.
Use it to verify contrast and visual hierarchy.
{% capture html -%}
<button class="btn btn-light">Light button on dark preview</button>
{% endcapture %}
{% include "docs/example.html" html=html bg="dark" %}
```
```md
## Usage
Use this pattern in any section when source markup should be shown together with the visual output.
Use `code` for clean implementation markup without preview-only wrappers.
### Card with footer actions
This example presents a card layout with footer actions in a realistic container.
Show `html` for rendered context and `code` for implementation markup.
{% capture html -%}
<div class="p-4 border rounded bg-light">
<div class="card">
<div class="card-body">Content</div>
<div class="card-footer">
<button class="btn btn-primary">Save</button>
</div>
</div>
</div>
{% endcapture %}
{% capture code -%}
<div class="card">
<div class="card-body">Content</div>
<div class="card-footer">
<button class="btn btn-primary">Save</button>
</div>
</div>
{% endcapture %}
{% include "docs/example.html" html=html code=code %}
```
```md
## Accessibility
Use this pattern in any section only for visual comparison cases where raw markup is not useful,
for example screenshots, behavior notes, or before/after UI states.
This pattern highlights visual state communication without extra source details.
Use it for quick comparisons such as status, before/after states, or behavior notes.
{% capture html -%}
<div class="d-flex align-items-center gap-2">
<span class="badge bg-green"></span>
<span>Active status</span>
</div>
{% endcapture %}
{% include "docs/example.html" html=html hide-code=true %}
### Do
- Explain what changed and why it matters.
- Link to a related practical example if available.
### Don't
- Do not replace all practical examples with image-only examples.
- Do not omit practical markup examples for core use cases.
```
- Do not move core documentation content (for example API details, states, accessibility rules,
migration notes) into `Examples`.
- Keep core sections as normal documentation; `Examples` is an optional showcase section,
while visual preview blocks can be used across multiple sections.
+171
View File
@@ -0,0 +1,171 @@
---
description: Tabler Project HTML Elements Guidelines
globs: ["**/*.html", "**/*.liquid", "**/*.md"]
alwaysApply: true
---
## HTML Elements Guidelines
### 1. Icons
When you need to use an icon, always use the Tabler icon include syntax:
```html
{% include "ui/icon.html" icon="ICON_NAME" %}
```
**Examples:**
- `{% include "ui/icon.html" icon="home" %}`
- `{% include "ui/icon.html" icon="building-community" %}`
- `{% include "ui/icon.html" icon="map-pin" %}`
### 2. Page Links
When linking to other pages, always use the relative page syntax:
```html
href="{{ page | relative }}/url.html"
```
**Examples:**
- `href="{{ page | relative }}/job-post.html"`
- `href="{{ page | relative }}/job-listing.html"`
- `href="{{ page | relative }}/marketing/index.html"`
### 3. Static Generation
All pages are statically generated to HTML using Eleventy (11ty). Keep this in mind when:
- Writing frontmatter (must be static YAML, no Liquid templating)
- Creating dynamic content (use Liquid templating in the body, not frontmatter)
- Linking between pages (use relative paths)
### 4. Additional Guidelines
#### Frontmatter Rules
- Frontmatter must be static YAML
- Cannot use Liquid templating in frontmatter
- Use static values for title, permalink, etc.
#### Liquid Templating
- Use Liquid templating only in the HTML body
- Access data using `{{ variable }}` syntax
- Use `{% for %}` loops for dynamic content
- Use `{% if %}` conditions for conditional rendering
#### File Structure
- Pages go in `preview/pages/`
- Includes go in `shared/includes/`
- Data files go in `shared/data/`
- Documentation goes in `docs/content/`
#### CSS Classes
- Use Bootstrap 5 classes
- Use Tabler's custom CSS classes
- Follow the pattern: `--#{$prefix}component-property`
#### Accessibility
- Include proper ARIA labels
- Use semantic HTML elements
- Ensure proper heading hierarchy
- Add alt text for images
### 5. Component Usage
#### Cards
- Use `card` class for main containers
- Use `card-body` for content areas
- Use `card-header` for card headers
- Use `card-title` for card title
#### Buttons
- Use `btn` class for all buttons
- Use `btn-primary` for primary actions
- Use `btn` for secondary actions, don't use `btn-outline-secondary`
- Use `btn-sm` for smaller buttons
- Use `w-100` for full-width buttons
#### Forms
- Use `form-control` for input fields
- Use `form-label` for labels
- Use `form-check` for checkboxes/radio buttons
- Use `form-select` for dropdowns
#### Layout
- Use Bootstrap grid system (`row`, `col-*`)
- Use `container-xl` for main containers
- Use `page-wrapper` for page structure
- Use `page-body` for main content area
#### Badges
- Use `badge` class for badges
- Don't use `badge-outline` for badges, use `badge` class instead
- Don't use `badge-primary` for badges, use `badge` class instead
- Don't change the text color of badges
#### Markdown
- Use `markdown` class for markdown content
- Apply to containers that render markdown content
- Example: `<div class="markdown">...</div>`
#### Rest of the rules
- Read the rest of the rules in the `docs/content/ui/` folder
### 6. Data Integration
#### Using JSON Data
```liquid
{% for item in items %}
<div>{{ item.name }}</div>
{% endfor %}
```
#### Conditional Rendering
```liquid
{% if condition %}
<div>Content</div>
{% endif %}
```
#### Including Components
```liquid
{% include "ui/button.html" color="primary" text="Click me" %}
```
### 7. Best Practices
#### Performance
- Minimize nested loops
- Use `limit` filters when iterating large datasets
- Optimize images for web use
#### Code Organization
- Keep components modular and reusable
- Use consistent naming conventions
- Comment complex logic
- Group related functionality together
#### Error Handling
- Always check if data exists before using it
- Provide fallback content for missing data
- Use `{% if %}` guards for optional content
+39
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.
-67
View File
@@ -1,67 +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@v4
- name: Cache turbo build setup
uses: actions/cache@v4
with:
path: .turbo
key: ${{ runner.os }}-turbo-${{ github.sha }}
restore-keys: |
${{ runner.os }}-turbo-
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "${{ env.NODE }}"
- name: Install PNPM
uses: pnpm/action-setup@v4
- 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
+8 -8
View File
@@ -9,7 +9,6 @@ on:
env:
FORCE_COLOR: 2
NODE: 20
jobs:
bundlewatch:
@@ -17,24 +16,25 @@ jobs:
steps:
- name: Clone repository
uses: actions/checkout@v4
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 }}
restore-keys: |
${{ runner.os }}-turbo-
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "${{ env.NODE }}"
- name: Install PNPM
uses: pnpm/action-setup@v4
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version-file: '.nvmrc'
cache: 'pnpm'
- name: Set up Bundler
uses: ruby/setup-ruby@v1
with:
@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
persist-credentials: false
+1 -1
View File
@@ -11,7 +11,7 @@ jobs:
pull-requests: write
issues: write
steps:
- uses: actions/stale@v9
- uses: actions/stale@v10
with:
days-before-issue-stale: 360
days-before-issue-close: 14
+29
View File
@@ -0,0 +1,29 @@
name: Lint
on:
pull_request: null
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-file: '.nvmrc'
cache: 'pnpm'
- name: Install pnpm dependencies
run: pnpm install
- name: Lint Markdown
run: pnpm run lint
+2 -2
View File
@@ -12,9 +12,9 @@ jobs:
name: Verify lock file integrity
steps:
- name: Clone Tabler
uses: actions/checkout@v4
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
+7 -6
View File
@@ -21,16 +21,17 @@ jobs:
pull-requests: write # to create pull request
steps:
- name: Checkout Repo
uses: actions/checkout@v4
- name: Setup Node.js 18
uses: actions/setup-node@v4
with:
node-version: 20
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-file: '.nvmrc'
cache: 'pnpm'
- name: Install Dependencies
run: pnpm install
+8 -10
View File
@@ -3,9 +3,6 @@ name: Test build
on:
pull_request: null
env:
NODE: 20
permissions:
contents: read
@@ -14,24 +11,25 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v4
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 }}
restore-keys: |
${{ runner.os }}-turbo-
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "${{ env.NODE }}"
- name: Install PNPM
uses: pnpm/action-setup@v4
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version-file: '.nvmrc'
cache: 'pnpm'
- run: node --version
- name: Install pnpm dependencies
+42
View File
@@ -0,0 +1,42 @@
name: Type Check
on:
pull_request: null
push:
branches:
- main
- dev
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@v5
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-file: '.nvmrc'
cache: 'pnpm'
- name: Install pnpm dependencies
run: pnpm install
- name: Run type-check
run: pnpm run type-check
+6 -1
View File
@@ -36,4 +36,9 @@ package-lock.json
demo/
dist/
packages-zip/
.env
.env
sri.json
# TypeScript
*.tsbuildinfo
.tsbuildinfo
+61
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
}
+1 -1
View File
@@ -1 +1 @@
20
22
+12 -2
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"
}
}
]
}
+1 -1
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
+7 -7
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.testmuai.com/?utm_medium=sponsor&utm_source=tabler" 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
@@ -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')
})
})
+84
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)
}
@@ -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}`)
}
}
}
@@ -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)
})
}
+53
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}`)
}
-47
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
+30
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
})
-1
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.

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